当前位置:   article > 正文

Flutter笔记: 在Flutter中的操作文件

Flutter笔记: 在Flutter中的操作文件

一、介绍

文件是存储在磁盘上的数据的集合,可以包含任何类型的数据,如文本、图片、音频等。文件是计算机中用于数据存储的基本单位,它可以包含各种类型的数据,如程序代码、文本文档、图片、音频和视频等。
在 Dart 中,我们可以使用 dart:io 库中的 File 类来操作文件

二、文件(File)

以下是一个简单的示例,演示如何使用 File 类来创建一个新文件,并向其中写入一些文本

void demo1() async {
    var file = File('test.txt');
    try {
      await file.writeAsString('Hello, Dart!');
    } catch (e) {
      print('Error: $e');
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.1 创建文件

使用 create 方法可以创建一个新的文件。如果文件已经存在,create 方法不会有任何效果。如果你希望覆盖已经存在的文件,可以给 create 方法传入一个名为 recursive 的参数,将其设为 true。

void demo2() async {
    var file = File('test.txt');
    try {
      await file.create(recursive: true);
    } catch (e) {
      print('Error: $e');
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.2 读取文件

使用 readAsString 方法可以读取文件的内容,返回一个字符串。如果文件不存在,readAsString 方法会抛出一个异常

void demo3() async {
    var file = File('test.txt');
    try{
      String contents = await file.readAsString();
      print(contents);
    }catch(e){
      print('文件不存在: $e');
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2.3 写入文件

使用 writeAsString 方法可以向文件中写入字符串。如果文件不存在,writeAsString 方法会创建一个新的文件。如果文件已经存在,writeAsString 方法会覆盖文件的内容。

void demo4() async {
    var file = File('test.txt');
    File? fileCached;
    try {
      fileCached = await file.writeAsString('Hello, Dart!');
    } catch (e) {
      print(e);
    }
    print('fileCached: $fileCached');
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

2.4 删除文件

使用 delete 方法可以删除一个文件。如果文件不存在,delete 方法不会有任何效果。

void demo5() async {
    var file = File('test.txt');
    try {
      await file.delete();
    } catch (e) {
      print(e);
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.5 权限

如果我们没有权限写入 ‘/root/test.txt’ 文件,writeAsString 方法会抛出一个 FileSystemException 异常,然后我们在 catch 语句中捕获这个异常,并打印一个错误消息

void demo6() async {
    var file = File('/root/test.txt');
    try {
      await file.writeAsString('Hello, Dart!');
    } catch (e) {
      print('Error: $e');
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

三、目录 (Directory)

3.1 创建目录

使用 create 方法可以创建一个新的目录。如果目录已经存在,create 方法不会有任何效果。如果你希望创建的目录的父目录不存在,可以给 create 方法传入一个名为 recursive 的参数,将其设为 true

void dir1() async {
    var directory = Directory('test');
    await directory.create();
  }
  • 1
  • 2
  • 3
  • 4

3.2 目录列表

使用list方法可以列出目录的内容,包括文件和子目录。list 方法返回一个 Stream 对象,我们可以使用 await for 语句来遍历这个 Stream

void dir2() async {
     var directory = Directory('test');
     await for (var entity in directory.list()) {
       print(entity.path);
     }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

3.3 删除目录

使用 delete 方法可以删除一个目录。如果目录不存在,delete 方法不会有任何效果。如果你希望删除的目录包含文件或子目录,可以给 delete 方法传入一个名为 recursive 的参数,将其设为true

void dir3() async {
     var directory = Directory('test');
     await directory.delete();
  }
  • 1
  • 2
  • 3
  • 4

四、链接 (Link)

链接是指向另一个文件或目录的引用。在文件系统中,链接可以使我们在不复制文件内容的情况下,从不同的位置访问同一个文件。这对于节省存储空间和提高数据管理的效率非常有用。链接可以分为两种类型:硬链接 和 软链接(也被称为 符号链接)

4.1 硬链接 和 软链接

硬链接和软链接是两种不同类型的链接,它们的主要区别在于指向文件的方式不同。

  1. 硬链接
    硬链接 是指向文件的物理位置的引用。换句话说,硬链接和它所指向的文件在文件系统中是等价的,它们共享同一块存储空间。这意味着,如果你删除了硬链接,它所指向的文件仍然存在;如果你修改了硬链接的内容,它所指向的文件的内容也会被修改。

  2. 软链接
    软链接(也称为 符号链接 或 symlink)是指向另一个链接或文件的路径的引用。软链接和它所指向的文件在文件系统中是独立的,它们不共享存储空间。这意味着,如果你删除了软链接,它所指向的文件不会受到影响;如果你修改了软链接的内容,它所指向的文件的内容不会被修改。

在大多数情况下,我们都会使用软链接,因为它更加灵活和方便。然而,在某些情况下,硬链接可能会更有用。例如,如果你希望在不增加存储空间的情况下,从不同的位置访问同一个文件,你可以使用硬链接。

4.2 创建链接

使用 create 方法可以创建一个新的链接。如果链接已经存在,create方法会覆盖已经存在的链接。在这个示例中,我们创建了一个新的链接,该链接指向名为 ‘test.txt’ 的文件

void link1() async {
    var link = Link('link');
    await link.create('test.txt');
  }
  • 1
  • 2
  • 3
  • 4

4.3 更新链接

使用update方法可以更新链接的目标。

void link2() async {
    var link = Link('link');
    await link.update('test2.txt');
  }
  • 1
  • 2
  • 3
  • 4

4.4 删除链接

使用 delete 方法可以删除一个链接。如果链接不存在,delete 方法不会有任何效果。

void link3() async {
    var link = Link('link');
    await link.delete();
  }
  • 1
  • 2
  • 3
  • 4

五、路径 (path)

5.1 创建路径

Dart 中,我们可以使用字符串来表示路径。例如,我们可以创建一个表示文件路径的字符串,然后使用这个字符串来创建一个 File 对象

void path1() async {
     var path = '/home/user/documents/report.txt';
     var file = File(path);
     print(file);
  }
  • 1
  • 2
  • 3
  • 4
  • 5

5.2 连接路径(join)

join 函数可以连接两个或多个路径。它会自动处理路径中的分隔符,使得你的代码可以在不同的操作系统上正确运行

void path2() async {
    var p = path.join('/home/user', 'documents', 'report.txt');
    print(p);  // Outputs: /home/user/documents/report.txt
  }
  • 1
  • 2
  • 3
  • 4

5.3 获取路径的基名(basename)

basename 函数可以获取路径的基名,即路径的最后一部分。

void path3() async {
    var name = path.basename('/home/user/documents/report.txt');
    print(name);  // Outputs: report.txt
  }
  • 1
  • 2
  • 3
  • 4

5.4 分割路径(split)

split函数可以分割路径,将路径分解为它的各个部分。

void path4() async {
    var parts = path.split('/home/user/documents/report.txt');
    print(parts);  // Outputs: ['/', 'home', 'user', 'documents', 'report.txt']
  }
  • 1
  • 2
  • 3
  • 4

5.5 获取路径的目录名(dirname)

dirname 函数可以获取路径的目录名,即除去最后一部分的路径。

void path5() async {
    var dir = path.dirname('/home/user/documents/report.txt');
    print(dir);  // Outputs: /home/user/documents
  }
  • 1
  • 2
  • 3
  • 4

5.6 获取路径的扩展名(extension)

extension函数可以获取路径的扩展名,即最后一部分中的点(.)之后的部分。

void path6() async {
    var ext = path.extension('/home/user/documents/report.txt');
    print(ext);  // Outputs: .txt
  }
  • 1
  • 2
  • 3
  • 4

5.7 判断路径是否为绝对路径(isAbsolute)

isAbsolute 函数可以判断路径是否为绝对路径。绝对路径是从文件系统的根目录开始的路径,而相对路径是从当前目录开始的路径。

 void path7() async {
    var absolute = path.isAbsolute('/home/user/documents/report.txt');
    var relative = path.isAbsolute('report.txt');
    print(absolute);  // Outputs: true
    print(relative);  // Outputs: false
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

六、字节流

字节流是一种低级的输入输出流,它以字节为单位进行读写操作。字节流通常用于处理二进制数据,如图片、音频和视频等。

在Dart中,我们可以使用File类的openRead和openWrite方法来创建字节流。

以下是一些使用字节流进行文件读写的示例:

为此文件的内容创建一个新的独立 [Stream]

如果 [start] 存在,将从字节偏移量 [start] 读取文件。否则从开始(索引0)。

如果 [end] 存在,只读取到字节索引[end] 的字节。否则,直到文件结束。

为确保系统资源被释放,必须读取流到完成,或者取消对流的订阅。

如果 [File] 是一个 命名管道,那么返回的 [Stream] 将等待管道的写入端关闭后才会发出 “done” 信号。如果在打开时管道没有连接的写入器,那么[Stream.listen] 将等待写入器打开管道。

Stream<List<int>> openRead([int? start, int? end])

6.1 读取字节流

使用 openRead 方法可以创建一个字节流,用于读取文件的内容。openRead 方法返回一个 Stream<List> 对象,我们可以使用 Stream 的各种方法来处理字节流

在这个示例中,我们首先创建了一个字节流,然后使用await for循环来处理字节流中的数据。

static void file1() async{
    var file = File('test.txt');
    Stream<List<int>> inputStream = file.openRead();
    await for (var data in inputStream) {
      // 处理数据...
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

6.2 写入字节流

使用 openWrite 方法可以创建一个字节流,用于向文件中写入数据。openWrite 方法返回一个 IOSink 对象,我们可以使用IOSink 的 add 或 write 方法来写入数据

在这个示例中,我们首先创建了一个字节流,然后使用 write 方法向字节流中写入数据。最后,我们使用 close 方法关闭字节流

void file2() async{
    var file = File('test.txt');
    var outputStream = file.openWrite();
    outputStream.write('Hello, Dart!');
    await outputStream.close();
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

七、 字符流

以字节流读取文件

字符流是一种高级的输入输出流,它以字符为单位进行读写操作。字符流通常用于处理文本数据。

在 Dart 中,我们可以使用 File 类的 readAsString 和 writeAsString 方法来进行字符流的读写操作。

7.1 读取字符流

在这个示例中,我们使用 readAsString 方法读取了文件的内容,并将其打印出来

void file3() async{
    var file = File('test.txt');
    String contents = await file.readAsString();
    print(contents);
  }
  • 1
  • 2
  • 3
  • 4
  • 5

7.2 写入字符流

使用 writeAsString 方法可以向文件中写入字符串。如果文件不存在,writeAsString 方法会创建一个新的文件。如果文件已经存在,writeAsString 方法会覆盖文件的内容

将字节列表写入文件。

打开文件,将字节列表写入文件,然后关闭文件。返回一个 Future<File>,当整个操作完成后,该Future将完成并返回此 [File] 对象。

认情况下,[writeAsBytes] 会创建文件并进行写入,如果文件已存在,将会截断文件。如果要将字节追加到现有文件,可以将 [FileMode.append] 作为可选的mode参数传入。

如果参数 [flush] 设置为 true,在返回的Future完成之前,写入的数据将被刷新到文件系统。

在这个示例中,我们使用 writeAsString 方法向文件中写入了一些文本

void file4() async{
    var file = File('test.txt');
    await file.writeAsString('Hello, Dart!');
  }
  • 1
  • 2
  • 3
  • 4

7.3 使用流优化文件读写

在读取或写入大文件时,我们通常使用流来处理数据。流可以让我们以块的形式处理数据,而不是一次性加载整个文件到内存中。这样可以显著减少内存使用,并提高程序的响应性

在这个示例中,我们首先打开了一个文件的读取流,然后使用 transform 方法将字节流转换为字符串流,再将字符串流转换为行流。最后,我们使用 listen 方法处理每一行数据。

void file5() async{
    var file = File('large_file.txt');
    var inputStream = file.openRead();

    inputStream
      .transform(utf8.decoder)  // 将字节解码为UTF-8
      .transform(const LineSplitter())  // 将流转换为单独的行
      .listen((String line) {  // 处理结果
        print('从流中获得 ${line.length} 个字符');
      }, onDone: () {
        print('文件现已关闭');
      }, onError: (e) {
        print(e.toString());
    });
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

八、文件权限

在进行文件操作时,我们需要考虑到平台权限问题。不同的操作系统和设备可能有不同的权限要求。以下是一些可能需要处理的权限问题

使用 permission_handler 请求权限
为了方便的获取相应的用户授权,在进行文件操作前,我们需要检查并请求必要的运行时权限,这可以使用 permission_handler 库来完成。我们可以使用来处理运行时权限。
请求存储权限

8.1 请求权限

以下是一个请求权限的示例

import 'package:permission_handler/permission_handler.dart';

      void main() async {
        var status = await Permission.storage.status;
        if (!status.isGranted) {
          status = await Permission.storage.request();
        }

        if (status.isGranted) {
          // 你可以开始文件操作
        } else {
          // 不能启动文件操作
        }
      }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

8.2 处理权限拒绝

如果用户拒绝了我们的权限请求,我们需要处理这种情况。我们可以显示一个解释为什么我们需要这个权限的对话框,或者引导用户到系统设置中开启权限

import 'package:permission_handler/permission_handler.dart';

    void handler_storage_permission() async {
      var status = await Permission.storage.status;
      if (!status.isGranted) {
        status = await Permission.storage.request();
      }

      if (status.isGranted) {
        // 你可以开始文件操作
        // ...
      } else if (status.isPermanentlyDenied) {
        // 用户选择永久拒绝权限
        // 你可以在这里打开应用设置
        // ...
        openAppSettings();
      } else {
        // 用户选择拒绝权限
        // 你可以在这里显示一个对话框
        // ...
      }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

九、 文件存储

path_provider 库及其用法
api地址: https://pub-web.flutter-io.cn/documentation/path_provider/latest/path_provider/path_provider-library.html
path_provider 是一个Flutter插件,用于查找iOS和Android上的常用位置的路径。这个库可以帮助我们找到存储应用数据的正确位置。

9.1 获取临时目录

我们可以使用getTemporaryDirectory方法来获取临时目录的路径。临时目录是一个可以用来存储临时数据的目录。系统可能会随时清理这个目录,因此不应将重要数据保存在这里。

void path1() async {
    Directory tempDir = await getTemporaryDirectory();
    String tempPath = tempDir.path;
    print(tempPath);
  }
  • 1
  • 2
  • 3
  • 4
  • 5

9.2 取应用程序目录

我们可以使用getApplicationDocumentsDirectory方法来获取应用程序目录的路径。应用程序目录是一个可以用来存储应用需要持久化的数据的目录。系统不会清理这个目录,因此可以安全地将重要数据保存在这里。

void path2() async {
    Directory appDocDir = await getApplicationDocumentsDirectory();
    String appDocPath = appDocDir.path;
    print(appDocPath);
  }
  • 1
  • 2
  • 3
  • 4
  • 5

9.3 获取外部存储中为应用程序创建的目录

我们可以使用 getExternalStorageDirectory 方法来获取外部存储中为应用程序创建的目录的路径。
这个目录通常用来存储可以由用户在其他应用中访问的文件,如图片、音乐等

void path3() async {
    Directory? externalStorageDir = await getExternalStorageDirectory();
    String externalStoragePath = externalStorageDir!.path;
    print(externalStoragePath);
  }
  • 1
  • 2
  • 3
  • 4
  • 5

9.4 获取应用程序可以放置应用库文件的目录

我们可以使用getApplicationSupportDirectory方法来获取应用程序可以放置应用库文件的目录的路径。这个目录用来存储应用的支持文件,这些文件只有应用本身可以访问。

void path4() async {
    Directory supportDir = await getApplicationSupportDirectory();
    String supportPath = supportDir.path;
    print(supportPath);
  }
  • 1
  • 2
  • 3
  • 4
  • 5

十、读写特殊类型的文件示例

在Flutter中,我们可以使用File类来读写各种类型的文件,包括图片、音频等特殊类型的文件。
然而,由于这些特殊类型的文件通常是二进制格式的,因此我们需要使用readAsBytes和writeAsBytes方法来读写这些文件。

10.1 图片文件

10.1.1 读取图片文件

我们可以使用 readAsBytes 方法来读取图片文件,然后使用 Image.memory 构造函数来创建一个图片控件。

Future<Widget> file6() async {
     File file = File('image.png');
     Uint8List bytes = await file.readAsBytes();
     Image image = Image.memory(bytes);
     print(image);
    return image;
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

10.1.2 写入图片文件

我们可以使用writeAsBytes方法来写入图片文件。首先,我们需要获取图片的字节数据,然后将这些字节数据写入文件。

void file7() async {
     var bytes = await rootBundle.load('assets/image.png');
     var file = File('path_to_your_image_file');
     await file.writeAsBytes(bytes.buffer.asUint8List());
  }
  • 1
  • 2
  • 3
  • 4
  • 5

10.2 音频文件

10.2.1 读取音频文件

读取音频文件的方法与读取图片文件类似,我们可以使用 readAsBytes 方法来读取音频文件。
然而,由于Flutter的核心库并不支持音频播放,因此我们需要使用一个第三方库,如audioplayers,来播放音频。

void videoRead() async {
    var file = File('path_to_your_audio_file');
    var bytes = await file.readAsBytes();
    var player = AudioPlayer();
    await player.playBytes(bytes);
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
10.2.2 写入音频文件

写入音频文件的方法与写入图片文件类似,我们可以使用writeAsBytes方法来写入音频文件。

void videoWrite() async {
    var bytes = await rootBundle.load('assets/audio.mp3');
    var file = File('path_to_your_audio_file');
    await file.writeAsBytes(bytes.buffer.asUint8List());
  }
  • 1
  • 2
  • 3
  • 4
  • 5
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/267585?site
推荐阅读
相关标签
  

闽ICP备14008679号