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 上也是如此。

文件保存位置

有两个重要的方法用于导出/保存文件:exportToStoragesaveToPath

  • 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 for 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

更多信息

设置 go-flutter设置 FDE

移动平台

无需设置?。

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

注意: 与以前的版本不同,您不再需要修改任何文件。

GitHub

查看 Github