window.showDirectoryPicker()
方法。从 JavaScript 使用它来允许用户选择一个目录,然后列出它的内容,如下所示:
const dirHandle = await window.showDirectoryPicker();
for await (const entry of dirHandle.values()) {
console.log(entry.name);
}
FileSystemDirectoryHandle.values()
的文档说它返回“一个新的数组迭代器,其中包含FileSystemDirectoryHandle
对象中每个索引的值”。它没有具体说明,尽管它似乎是一个 async 迭代器,因为枚举它需要 await for
。
我正在尝试创建绑定以从 Dart 调用它。我希望我的绑定应该返回
Stream<FileSystemHandle>
,尽管我找不到任何有关执行此操作的示例或文档。我尝试了很多组合,但这是我现在所拥有的:
@JS()
library js_lib;
import 'dart:html';
import 'package:js/js.dart';
import 'package:js/js_util.dart';
Future<FileSystemDirectoryHandle> showDirectoryPicker() =>
promiseToFuture(callMethod(window, 'showDirectoryPicker', []));
@JS()
abstract class FileSystemDirectoryHandle extends FileSystemHandle {
external Stream<FileSystemHandle> values();
}
@JS()
abstract class FileSystemHandle {
String get name;
}
@JS()
abstract class FileSystemFileHandle extends FileSystemHandle {}
我想这样称呼它:
final dirHandle = await showDirectoryPicker();
await for (final item in dirHandle.values()) {
print(item.name);
}
但是这会失败,如错误所示:
错误:需要“Stream”类型的值,但得到“NativeJavaScriptObject”类型之一
我怀疑我可能需要一些东西来将 JS 迭代器转换为 Dart 流(类似于我的
promiseToFuture
调用),尽管我找不到方法来做到这一点(我也无法向 添加方法) FileSystemDirectoryHandle
不是 external
。
编辑:这似乎适用于
build_runner serve
,但不适用于 build_runner serve --release
。我已提交问题以确定这是否是一个错误或者这是错误的方法。
意识到 JS 中的异步迭代器只是
next()
返回承诺的迭代器之后,我能够编写一个小函数,只需手动调用该方法并在 async*
函数中产生:
extension FileSystemDirectoryHandleExtensions on FileSystemDirectoryHandle {
Stream<FileSystemHandle> values() =>
_asyncIterator<FileSystemHandle>(callMethod(this, 'values', []));
}
Stream<T> _asyncIterator<T>(jsIterator) async* {
while (true) {
final next = await promiseToFuture(callMethod(jsIterator, 'next', []));
if (getProperty(next, 'done')) {
break;
}
yield getProperty(next, 'value');
}
}
这是使用上面的原始 Dart 代码调用的,并根据需要打印文件名。
你可以这样做:
import 'dart:js_interop';
import 'package:web/web.dart';
extension type FileSystemDirectoryHandleExt._(FileSystemDirectoryHandle _)
implements FileSystemHandle, JSObject {
external JsAsyncIterator<JSAny> keys();
}
extension type JsAsyncIterator<T extends JSAny>._(JSObject _)
implements JSObject {
external JSPromise<JsAsyncIteratorState<T>> next();
Stream<T> asStream() async* {
while (true) {
final result = await next().toDart;
if (result.done) break;
yield result.value;
}
}
}
extension type JsAsyncIteratorState<T extends JSAny>._(JSObject _)
implements JSObject {
external bool get done;
external T get value;
}
然后像这样使用它:
final storageRoot = await window.navigator.storage.getDirectory().toDart;
final storageRootExt = storageRoot as FileSystemDirectoryHandleExt;
await for (final JSString key in storageRootExt.keys().asStream()) {
// ...
}
values()
和 entries()
可以按照与上面 keys()
类似的方式添加。请参阅MDN。
我不确定为什么这还没有在 package:web 中。难道是我看的不够仔细?