日益努力,而后风生水起|

Flutter桌面端开发——选择读取本地文件

注意:查看本文章前请先查看更新日志,以至于该文章适合插件的最新版本

更新日志
详情日期
更新了插件的版本和file_selector的部分用法2023-08-13
更新了插件的版本,用法不变2022-12-10
更新了file_selector在0.8.4+3版本存储文件的用法2022-06-13
修正了file_picker存储文件的用法2022-05-24

上一节讲了怎么拖动文件到应用中,从应用中点击按钮弹出选择框上传文件,也是必不可少的一个功能。

file_selector

安装🛠

点击file_selector获取最新版本。以下是在编写本文章时的最新版本:

file_selector: ^1.0.0

👻注意:在开发 macOS 端的程序时,还需要额外的操作,具体可以查看这里

了解🧩

在 file_selector 插件中,我们需要了解对象 XTypeGroup 和方法 openFiles。

XTypeGroup的作用是使用给定的标签和文件扩展名创建一个新组,没有提供任何类型选项的组表示允许任何类型。

XTypeGroup 可以传入5个参数:

  • String? label:用来自己作区分
  • List<String>? extensions:过滤非描述的后缀文件,默认加载全部类型的文件
  • List<String>? mimeTypes:主要针对 Linux 系统下的文件类型
  • List<String>? macUTIs:主要针对 mac 系统下的文件类型
  • List<String>? webWildCards:主要针对网页开发时的类型

openFile 方法返回一个 XFile 对象,可以传入3个参数:

  • List acceptedTypeGroups = const []:接受的类型组,传入一个XTypeGroup列表
  • String? initialDirectory:初始化文件夹。打开文件对话框以加载文件并返回文件路径
  • String? confirmButtonText:弹窗“打开”按钮的显示文字

openFiles 方法返回 XFile 对象列表,传入的参数和 openFile 一样。

使用🥩

选择的文件我们最后需要读取出来,读取需要接受一个 String 类型的路径:

String path = '';

这里先以选择图片为例,我们需要先定义一个 XTypeGroup 对象:

final xType = XTypeGroup(label: '图片', extensions: ['jpg', 'png']);

选择单张图片

打开弹窗选取单个文件,使用 openFile 方法:

final XFile? file = await openFile(acceptedTypeGroups: [xType]);

将获取到的 XFile 对象的路径值传给 path。当然,并不是每次打开弹窗都会选择图片,所以需要判断一下:

if (file != null) {
  path = file.path;
  setState((){});
} else {
  BotToast.showText(text: '你不选择图片打开干啥😤');
}

image

openFile 方法中还有两个属性,我们修改试一下:

final XFile? file = await openFile(
  acceptedTypeGroups: [xType],
  initialDirectory: r'C:\Users\ilgnefz\Pictures',
  confirmButtonText: '嘿嘿嘿',
);

image

initialDirectory 属性貌似没用😕去看了官方的例子,也没用到过这个参数,以后就忽略它吧。

选择多张图片

选取多张图片,我们就需要定义一个路径的数组了:

final List<String> paths = [];

XTypeGroup 对象和刚才的一样就行,重要的是使用 openFiles 方法:

final List<XFile> files = await openFiles(acceptedTypeGroups: [xType]);

将获取到的文件路径列表赋值给 paths:

if (file != null) {
  paths.addAll(files.map((e) => e.path).toList());
  setState((){});
} else {
  BotToast.showText(text: '你不选择图片打开干啥😤');
}

好了,来看看效果如何

image

读取文本文件

读取文本文件,我们需要获取文件的名称和内容:

final String title = '';
final String content = '';

再更改一下 XTypeGroup 对象就行:

final XTypeGroup xType = XTypeGroup(label: '文本', extensions: ['txt']);
final XFile? file = await openFile(acceptedTypeGroups: [xType]);

将获取到的 XFile 对象的属性赋值给我们定义的对象:

if (file != null) {
  title = file.name;
  content = await file.readAsString();
  setState((){});
} else {
  BotToast.showText(text: '打开了个寂寞🙄');
}

image

存储文本文件

存储文本需要用到 XFile 对象中的 fromData 方法。让我们来看看这个方法中需要传入什么参数:

  • Uint8List bytes:存储的主要内容
  • String? mimeType:文件的 mine 类型
  • String? name:文件名?测试了毫无用处😑
  • int? length:不知道是什么的长度,反正无法截取内容😑
  • DateTime? lastModified:最后修改文件的时间
  • String? path:文件保存的路径?测试了毫无效果😑
  • CrossFileTestOverrides? overrides:覆盖CrossFile的某些方法用来测试

(以上几个参数要是有朋友测试出来了,可以告知一下😁)

在存储文本文件前,我们需要先知道应该存储在哪个文件夹:

final FileSaveLocation? path = await getSaveLocation();

getSavePath有以下几个可选参数:

  • List acceptedTypeGroups:可以在对话框中选择的文件类型组的列表。其显示方式取决于 pltaform,例如:
    • 在 Windows 和 Linux 上,每个组将是过滤器选项列表中的一个条目
    • 在 macOS 上,将允许所有组允许的所有类型的并集
  • String? initialDirectory:打开对话框时将显示的目录的完整路径。如果未提供,平台将选择一个初始位置
  • String? suggestedName:文件名的初始值
  • String? confirmButtonText:对话框的确认按钮中的文本。如果未提供,则使用默认操作系统标签(例如,“保存”)

以下是没传任何参数的效果:

image

以下是传了参数的效果:

final FileSaveLocation? path = await getSaveLocation(
  acceptedTypeGroups: [
    XTypeGroup(label: '图片', extensions: ['jpg', 'png']),
    XTypeGroup(label: '视频', extensions: ['mp4', 'avi'])
  ],
  initialDirectory: r'C:\Users\ilgnefz\Pictures',
  suggestedName: '新建文件',
  confirmButtonText: '点我吧!',
);

image

getSaveLocation方法返回的FileSaveLocation对象有一个重要的属性:path,记录了文件的保存路径。

XFile.fromData 虽然有很多参数可以使用,但是大部分没有啥效果,通过上面的示例,我们可以直接使用getSaveLocation方法来是指必要的内容:

final String title = widget.provider.title;
final String content = widget.provider.txtContent;
final Uint8List fileData = const Utf8Encoder().convert(content);
// final Uint8List fileData = Uint8List.fromList(content.codeUnits);
final FileSaveLocation? saveLocation = await getSaveLocation(
  acceptedTypeGroups: [
    const XTypeGroup(label: '文本', extensions: ['txt']),
  ],
  initialDirectory: r'C:\Users\ilgnefz\Pictures',
  suggestedName: title,
);
debugPrint('存储路径:${saveLocation?.path}');
if (saveLocation != null) {
  const String fileMimeType = 'text/plain';
  final XFile xFile = XFile.fromData(
    fileData,
    mimeType: fileMimeType,
  );
  await xFile.saveTo(saveLocation.path);
} else {
  BotToast.showText(text: '给你个眼神自己体会😑');
}

image

现在去选择的文件夹就能找到我们存储的文件了。initialDirectory是路径的初始值,我打开文本改变了路径,所以就没生效。

选择文件夹

选择文件夹使用 getDirectoryPath 方法:

final String? path = await getDirectoryPath();
if (path != null) {
  title = '目录';
  content = path;
  setState((){});
}

6

选择多文件夹

选择多文件夹需要使用 getDirectoryPaths 方法:

final List<String?> paths = await getDirectoryPaths();
if (paths.isNotEmpty) {
  widget.provider.setTitle('多目录');
  widget.provider.setTextContent(paths.join('\n'));
  widget.provider.setFileType('text');
}

image

file_picker

安装🛠

点击file_picker获取最新版本。以下是在编写本文章时的最新版本:

file_picker: ^5.3.3

使用🥩

先定义一个默认的路径:

String path = '';

选择单个文件

选择单个文件需要用到 pickFiles 方法,该方法可以传入10个参数:

  • String? dialogTitle:弹窗的标题
  • String? initialDirectory:初始化的文件夹
  • FileType type = FileType.any:文件的类型
  • List<String>? allowedExtensions:允许的文件后缀名称,需配合FileType.custom使用
  • dynamic Function(FilePickerStatus)? onFileLoading:监听文件选择的状态
  • bool allowCompression = true:是否允许压缩
  • bool allowMultiple = false:是否允许选择多个文件
  • bool withData = false:如果为true,选取的文件将在内存中立即以“Uint8List”的形式提供其字节数据,如果您选择它进行服务器上传或类似操作,这将很有用。但是,请记住,如果您允许多个选择或选择大文件,则在 IO(iOS 和 Android)上启用此功能可能会导致内存不足问题。请改用 [withReadStream]。在 web 上默认为 true,其他为 false
  • bool withReadStream = false:拾取的文件将以 [Stream<List>] 的形式提供其字节数据,这对于上传和处理大文件很有用
  • bool lockParentWindow = false:是否将子窗口(文件选择器窗口)一直停留在 Flutter 窗口的前面,直到它关闭(如模态窗口)。此参数仅适用于 Windows
FilePickerResult? result = await FilePicker.platform.pickFiles();
if (result != null) {
  File file = File(result.files.single.path!);
  path = file.path;
  setState((){});
}

image

我们试着添加一些参数:

FilePickerResult? result = await FilePicker.platform.pickFiles(
  dialogTitle: '我的地盘我做主',
  initialDirectory: r'C:\Users\ilgnefz\Pictures\Saved Pictures',
  type: FileType.image,
);

8

initialDirectory 又没起作用😑

选择多个文件

定义一个接受所有路径的数组:

final List<String> paths = [];
FilePickerResult? result = await FilePicker.platform.pickFiles(
  allowMultiple: true,
);
if (result != null) {
  paths = result.files.map((e) => e.path!).toList();
 	setState((){});
}

读取文件信息

通过以上的方法,我们会得到一个 PlatformFile 对象:

FilePickerResult? result = await FilePicker.platform.pickFiles();
PlatformFile file = result.files.single;

该对象有以下几个属性:

  • name:文件名称
  • size:文件大小,以字节为单位
  • bytes:此文件的字节数据。如果您想操作其数据或轻松上传到其他地方,则特别有用。 在常见问题解答中查看此处 一个关于如何使用它在网络上上传的示例。
  • extension:文件后缀
  • path:文件路径
  • identifier:原始文件的平台标识符,是指 Android 上的 Uri 和 iOS 上的 NSURL。其他为null
  • readStream:将文件内容转换成流读取

1

存储文件

存储文件需要使用 saveFile 方法,该方法有可以传入6个参数:

  • String? dialogTitle:同 pickFiles 方法
  • String? fileName:存储文件的名字
  • String? initialDirectory:同 pickFiles 方法
  • FileType type = FileType.any:同 pickFiles 方法
  • List ? allowedExtensions:同 pickFiles 方法
  • bool lockParentWindow = false:同 pickFiles 方法
String? outputFile = await FilePicker.platform.saveFile(fileName: 'hello.txt');

image

这个时候我们去选择的文件夹查看,并不能发现我们存储的文件。这是因为这个方法并不能直接存储文件,我们把outputFile打印一下会发现以下结果:

outputFile: C:\Users\ilgnefz\Pictures\hello.txt

诶😀虽然它不能帮我们直接存储一个文件,但是会返回一个文件的路径。我们可以使用File对象来将需要的内容存储下来。

if (outputFile != null) {
  File file = File(outputFile);
  await file.writeAsString('Hello World');
  await file.create();
  BotToast.showText(text: '文件存储成功');
}

image

现在我们打开文件存储时选择的文件夹查看

image

获取文件夹路径

获取文件夹需要使用 getDirectoryPath 方法,可以传入3个参数:

  • String? dialogTitle:同 pickFiles 方法
  • bool lockParentWindow = false:同 pickFiles 方法
  • String? initialDirectory:同 pickFiles 方法
final String title = '';
final String content = '';
String? dir = await FilePicker.platform.getDirectoryPath();
if (path != null) {
  title = '目录';
  content = path;
  setState((){});
}

9

🛫OK,以上就是这篇文章的全部内容,仅针对插件的当前版本,并不能保证适用于以后插件用法的更新迭代。

最后,感谢 flutter 团队和 miguelpruivo 对以上插件的开发和维护😁。本应用代码已上传至 githubgitee,有需要的可以下载下来查看学习。

本文作者:菠萝橙子丶

本文链接:https://www.cnblogs.com/ilgnefz/p/16010886.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   菠萝橙子丶  阅读(5195)  评论(9编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 Shining For One Thing 赵贝尔
Shining For One Thing - 赵贝尔
00:00 / 00:00
An audio error has occurred.

Shining For One Thing (《一闪一闪亮星星》影视剧歌曲) - 赵贝尔

词:萨吉

曲:金大洲

编曲:金大洲D-Jin

制作人:金大洲D-Jin

吉他/Bass:D-Jin

合声编写/合声:赵贝尔

人声录音/编辑:张德龙@D-Jin Music Studio

混音/母带处理:George Dum

宣传推广:杨慧颖/杨佩

封面设计:HOO

OP/音乐制作出品:D-Jin Music(北京翊辰文化传媒有限公司)

(未经著作权人许可,不得翻唱、翻录或使用)

夏夜的花火

夏夜的花火

因为你在身边而深刻

因为你在身边而深刻

幸运的是我

在宇宙之间听见承诺

在宇宙之间听见承诺

嗨 是我

这一次别再错过

这一次别再错过

喜欢你该由我主动了

喜欢你该由我主动了

星星那么多

星星那么多

有数不尽的浪漫闪烁

注定这一颗

会让你刻在手臂左侧

属于我

星形心率的贴合

幸有你总在守护我

幸有你总在守护我

I fall in love

I fall in love

I see your love

遇见你才发现我在

等你到来

等你到来

Fallen star

The wonder of you

我会永远在你天空

我会永远在你天空

为你闪烁 my love

为你闪烁 my love

Shining for one thing

Shining for one thing

Shining for one thing

Shining for one thing

It's you

It's you

星星那么多

星星那么多

有数不尽的浪漫闪烁

注定这一颗

会让你刻在手臂左侧

属于我

星形心率的贴合

幸有你总在守护我

幸有你总在守护我

I fall in love

I fall in love

I see your love

遇见你才发现我在

等你到来

等你到来

Fallen star

The wonder of you

我会永远在你天空

我会永远在你天空

为你闪烁 my love

为你闪烁 my love

Shining for one thing

Shining for one thing

Shining for one thing

Shining for one thing

It's you

It's you