file_picker_cross
唯一一个 Flutter 插件,用于在 Android、iOS、桌面和 Web 上选择、打开、挑选、拾取和创建文档、图片、视频或其他文件,用于读取、写入、用作字符串、字节列表或 HTTP 上传。
入门
file_picker_cross 允许您从设备中选择、编辑和保存文件,并兼容 Android、iOS、桌面(使用 go-flutter 或 FDE)和 Web。
注意:我们最近有 API 更改。请相应地更新您的代码。
// show a dialog to open a file
FilePickerCross myFile = await FilePickerCross.importFromStorage(
type: FileTypeCross.any, // Available: `any`, `audio`, `image`, `video`, `custom`. Note: not available using FDE
fileExtension: 'txt, md' // Only if FileTypeCross.custom . May be any file extension like `dot`, `ppt,pptx,odp`
);
// save our file to the fictional directory. It is not necessary that it already exists.
myFile.saveToPath('/my/awesome/folder/' + myFile.fileName);
// save our file to the internal storage or share to other apps
myFile.exportToStorage();
// for sharing to other apps you can also specify optional `text` and `subject`
myFile.exportToStorage(
subject: "Awesome file",
text: "Here is the file you've been waiting for",
);
// on iPad you may also need to specify the `sharePositionOrigin` for native share UI
GlobalKey widgetKey = GlobalKey();
...
RenderBox renderBox = widgetKey.currentContext.findRenderObject();
Offset position = renderBox.localToGlobal(Offset.zero);
filePickerCross.exportToStorage(
position.dx, position.dy, renderBox.size.width, renderBox.size.height);
List<FilePickerCross> myMultipleFiles = await FilePickerCross.importMultipleFromStorage();
print(myMultipleFiles);
// list all previously opened files
List<String> paths = await FilePickerCross.listInternalFiles();
print(paths);
// open an existing file
FilePickerCross anotherFile = FilePickerCross.fromInternalPath(paths[0]);
// delete our perfect file
FilePickerCross.delete(paths[0]);
// get the file storage size
print(await FilePickerCross.quota());
// you can access the following properties on a FilePickerCross instance:
myFile.toString();
myFile.toUint8List();
myFile.toBase64();
myFile.toMultipartFile(filename: 'myFile.txt');
myFile.length;
myFile.path;
myFile.fileName;
myFile.fileExtension;
myFile.directory;
有关特定属性和方法的详细信息,请参阅 API 文档。
异常处理
不同平台由于用户操作或平台限制会抛出不同的异常。例如,您可能想知道用户是否拒绝了存储访问权限并据此采取行动。有一个方法可以帮助解决这个问题。
await FilePickerCross.importFromStorage().then(() {
// ...
}).onError((error, _) {
String _exceptionData = error.reason();
print('----------------------');
print('REASON: ${_exceptionData}');
if (_exceptionData == 'read_external_storage_denied') {
print('Permission was denied');
} else if (_exceptionData == 'selection_canceled') {
print('User canceled operation');
}
print('----------------------');
});
当抛出 FileSelectionCanceledError 异常时,您可以调用 reason() 方法来收集底层异常信息。它的返回类型是 String。
行为
- 用户取消时,将返回
selection_canceled - 发生
PlatformException异常时,将提取原始错误值并返回(即read_external_storage_denied) - 作为回退,未被“处理”的异常将被完整返回
此包的范围
简而言之:我们提供了一个并行的、平台无关的假文件系统实现,您可以在其中为您的应用程序创建、打开和保存文件——即使在 Web 上也是如此。此外,我们还提供 API 与真实文件系统进行交互,以便从设备导入和导出文件。
在跨平台应用中处理文件非常困难。虽然桌面平台有一个供所有应用程序使用的文件系统,但移动平台为每个应用程序提供了隔离的文件系统。Web 并没有在所有浏览器上都可用的实际文件系统。因此,在所有平台上实现存储和访问文件很困难——而且您不必这样做,因为我们已经为您完成了。
使用 file_picker_cross,我们为您的应用程序提供了一个假文件系统。与其他软件包不同,我们不仅提供用于读取或保存文件的对话框,还提供了一个完整的、位于您应用程序存储内部的文件系统,您可以在其中执行任何操作,如搜索文件、打开和保存它们。当然,我们也有用于从共享存储(设备存储、主文件夹等)导入文件或导出到这些位置的 API——即使在 Web 上也是如此。
文件保存位置
有两个重要的方法用于导出/保存文件:exportToStorage 和 saveToPath。
exportToStorage显示一个对话框,允许用户选择保存文件的位置。saveToPath用于自动保存,以防应用程序自动创建文件仅供应用程序内部使用。对于 Web,这意味着文件存储在 localStorage 中;在 Windows 上,路径是%LOCALAPPDATA%\your_app_name\;在所有其他平台上,文件存储在${getApplicationDocumentsDirectory()}/your_app_name/中。
关于目录说明
为什么无法选择目录? 这是我们经常被问到的问题。原因很简单:移动和 Web 设备的安全机制。
不过,根据您的计划,有两种变通方法可用。
如果您有很多文件需要存储在某个地方,您可以使用我们提供的 saveToPath('/my/path') API。这允许您将任何文件保存到应用程序内部的路径。有关更多详细信息,请参阅 API 文档。
另一种用例是将文件保存到用户定义的目录。对于单个文件,您可以使用 exportToStorage() API(文档)。但是,如果您想一次选择一个目录并在此目录中连续保存和读取文件,这在大多数设备上通常是不可能的,桌面设备除外。所有其他设备类型都因其安全机制而阻止此操作。在桌面设备上(而且不幸的是,*仅限*桌面设备),有一个变通方法可用。
// for the first file, you show an export as dialog
FilePickerCross myFile = ...
String pathForExports = await myFile.exportToStorage(); // <- will return the file's path on desktops
// you parse the file's directory and use it for later automated exports.
pathForExports = pathForExports.substring(0,pathForExports.lastIndexOf(r'/'));
print(pathForExports);
// now save the path for later use using shared preferences etc.
...
// next time, check whether you are overwriting an existing file or simply write the file
print(await File(pathForExports+'/myNextFile.csv').exists());
File myCsvFile = await File(pathForExports+'/myNextFile.csv').writeAsString('comma,separated,values'); // <- This only works on desktops. All other devices prevent this.
(来源:Issue #11)
此外,我们计划支持对设备上的某些共享存储位置(如 Documents、Downloads 等)的直接写入访问。我们在这方面的计划和 API 尚未准备好,但您可以预期会支持此功能。
go-flutter 和 FDE
Flutter 最初只支持 Android 和 iOS。为了增加对桌面平台的支持,一些人启动了 go-flutter 项目,使用 Go 语言在 Windows、Linux 和 macOS 上提供 Flutter 应用程序。
之后,Flutter 本身也宣布了桌面支持 (FDE),但它仍然不稳定。
我们努力尽可能多地支持两者。
Web
当然,这需要 Flutter 已设置为 Web 开发。
所有桌面平台
当然,这需要 Flutter 已设置为您的平台。
请注意,Windows 未经 Google 官方支持。Linux 和 macOS 支持处于 Alpha 状态。可能会遇到问题,有时还需要手动处理不兼容的版本。
flutter channel dev # or master
flutter upgrade
flutter config --enable-linux-desktop
flutter config --enable-macos-desktop
flutter config --enable-windows-desktop
更多信息
移动平台
无需设置?。
macOS (使用 FDE)
您需要 添加一个
权限
用于只读访问
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
或读/写访问
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
取决于您的用例。
Linux (使用 FDE)
此插件需要以下库
- GTK 3
- pkg-config
- xdg-user-dirs
Debian 系系统的安装示例
sudo apt-get install libgtk-3-dev pkg-config xdg-user-dirs
注意: 与以前的版本不同,您不再需要修改任何文件。