Flutter笔记:目录与文件存储以及在Flutter中的使用(下)
作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263
邮箱 :291148484@163.com
本文地址:https://blog.csdn.net/qq_28550263/article/details/134499297
【简介】本文探讨了Dart和Flutter中的文件系统操作和文件存储。内容覆盖了Dart的文件系统基础,文件路径处理,文件读写操作及其优化(上),以及在Flutter中的文件读写相关注意的事项(下)。
1. Dart文件读写
本节将介绍 字节流 和 字符流 的基本概念、操作方法以及性能优化。
1.1 字节流(Byte Stream)
字节流是一种低级的输入输出流,它以字节为单位进行读写操作。字节流通常用于处理二进制数据,如图片、音频和视频等。
在Dart中,我们可以使用File类的openRead和openWrite方法来创建字节流。
以下是一些使用字节流进行文件读写的示例:
1.1.1 以字节流读取文件
使用 openRead 方法可以创建一个字节流,用于读取文件的内容。openRead 方法返回一个 Stream<List<int>> 对象,我们可以使用 Stream 的各种方法来处理字节流。
/// 为此文件的内容创建一个新的独立 [Stream]。////// 如果 [start] 存在,将从字节偏移量 [start] 读取文件。否则从开始(索引0)。////// 如果 [end] 存在,只读取到字节索引 [end] 的字节。否则,直到文件结束。////// 为确保系统资源被释放,必须读取流到完成,或者取消对流的订阅。////// 如果 [File] 是一个 [命名管道](https://en.wikipedia.org/wiki/Named_pipe)/// 那么返回的 [Stream] 将等待管道的写入端关闭后才会发出 "done" 信号。如果在打开时管道没有连接的写入器,/// 那么 [Stream.listen] 将等待写入器打开管道。Stream<List<int>> openRead([int? start, int? end]);
例如:
import 'dart:io';void main() async {var file = File('test.txt');Stream<List<int>> inputStream = file.openRead();await for (var data in inputStream) {// 处理数据...}
}
在这个示例中,我们首先创建了一个字节流,然后使用await for循环来处理字节流中的数据。
1.1.2 以字节流写入文件
使用 openWrite 方法可以创建一个字节流,用于向文件中写入数据。openWrite 方法返回一个 IOSink 对象,我们可以使用IOSink 的 add 或 write 方法来写入数据。
// abstract interface class IOSink/// 将字节 [data] 添加到目标消费者,忽略 [encoding]。////// [encoding] 不适用于此方法,[data] 列表直接作为流事件传递给目标消费者。////// 当使用 [addStream] 添加流时,不应调用此方法。////// 此操作是非阻塞的。查看 [flush] 或 [done] 以获取此调用生成的任何错误。////// 传递给 `add` 后,不应修改数据列表,因为目标消费者接收列表的原始状态还是修改后的状态未定义。////// [data] 中的单个值如果不在 0 .. 255 的范围内,将被截断到其低8位,就像通过 [int.toUnsigned] 那样,然后再使用。void add(List<int> data);/// 通过调用 [Object.toString] 将 [object] 转换为字符串,并将结果的编码 [add] 到目标消费者。////// 此操作是非阻塞的。查看 [flush] 或 [done] 以获取此调用生成的任何错误。void write(Object? object);
例如:
import 'dart:io';void main() async {var file = File('test.txt');var outputStream = file.openWrite();outputStream.write('Hello, Dart!');await outputStream.close();
}
在这个示例中,我们首先创建了一个字节流,然后使用 write 方法向字节流中写入数据。最后,我们使用 close 方法关闭字节流。
1.2 字符流(Character Stream)
1.2.1 以字节流读取文件
字符流是一种高级的输入输出流,它以字符为单位进行读写操作。字符流通常用于处理文本数据。
在 Dart 中,我们可以使用 File 类的 readAsString 和 writeAsString 方法来进行字符流的读写操作。
/// 使用给定的 [Encoding] 读取整个文件内容作为字符串。////// 返回一个 `Future<String>`,当文件内容被读取后,该Future将完成并返回字符串。Future<String> readAsString({Encoding encoding = utf8});
例如:
import 'dart:io';void main() async {var file = File('test.txt');String contents = await file.readAsString();print(contents);
}
在这个示例中,我们使用 readAsString 方法读取了文件的内容,并将其打印出来。
1.2.2 以字节流写入文件
使用 writeAsString 方法可以向文件中写入字符串。如果文件不存在,writeAsString 方法会创建一个新的文件。如果文件已经存在,writeAsString 方法会覆盖文件的内容。
/// 将字节列表写入文件。////// 打开文件,将字节列表写入文件,然后关闭文件。返回一个 `Future<File>`,当整个操作完成后,该Future将完成并返回此 [File] 对象。////// 默认情况下,[writeAsBytes] 会创建文件并进行写入,如果文件已存在,将会截断文件。如果要将字节追加到现有文件,可以将 [FileMode.append] 作为可选的mode参数传入。////// 如果参数 [flush] 设置为 `true`,在返回的Future完成之前,写入的数据将被刷新到文件系统。Future<File> writeAsBytes(List<int> bytes,{FileMode mode = FileMode.write, bool flush = false});
例如:
import 'dart:io';void main() async {var file = File('test.txt');await file.writeAsString('Hello, Dart!');
}
在这个示例中,我们使用 writeAsString 方法向文件中写入了一些文本。
1.3 使用流优化文件读写
在读取或写入大文件时,我们通常使用流来处理数据。流可以让我们以块的形式处理数据,而不是一次性加载整个文件到内存中。这样可以显著减少内存使用,并提高程序的响应性。
以下是一个使用流读取大文件的示例:
import 'dart:io';
import 'dart:convert';void main() {var file = File('large_file.txt');var inputStream = file.openRead();inputStream.transform(utf8.decoder) // 将字节解码为UTF-8.transform(LineSplitter()) // 将流转换为单独的行.listen((String line) { // 处理结果print('从流中获得 ${line.length} 个字符');}, onDone: () {print('文件现已关闭');}, onError: (e) {print(e.toString());});
}
在这个示例中,我们首先打开了一个文件的读取流,然后使用 transform 方法将字节流转换为字符串流,再将字符串流转换为行流。最后,我们使用 listen 方法处理每一行数据。
2. 文件权限
在进行文件操作时,我们需要考虑到平台权限问题。不同的操作系统和设备可能有不同的权限要求。以下是一些可能需要处理的权限问题:
2.1 不同移动平台上的权限
以往,在 Android 平台上,我们需要在 AndroidManifest.xml 文件中声明我们需要的权限。
- READ_EXTERNAL_STORAGE:这个权限允许应用读取设备上的外部存储空间。
- WRITE_EXTERNAL_STORAGE:这个权限允许应用写入设备上的外部存储空间。
但是从Android 6.0(API级别23)开始,用户可以在运行时授予或撤销某些权限。
即使你在 AndroidManifest.xml 文件中声明了这些权限,你仍然需要在运行时检查这些权限是否已经被授予,并在需要时请求用户授予这些权限。
而在iOS平台上,我们需要在Info.plist文件中声明我们需要的权限。以下是一些可能需要的权限:
- NSFileProtectionCompleteUntilFirstUserAuthentication:这个权限允许应用在设备解锁后访问文件。
2.2 使用 permission_handler 请求权限
为了方便的获取相应的用户授权,在进行文件操作前,我们需要检查并请求必要的运行时权限,这可以使用 permission_handler 库来完成。我们可以使用来处理运行时权限。
2.2.1 安装和配置 permission_handler
首先,我们需要在项目中安装 permission_handler 库。在 pubspec.yaml 文件中添加以下依赖:
dependencies:flutter:sdk: flutterpermission_handler: ^11.0.1
然后,运行 flutter pub get 命令来获取库。
2.2.2 请求存储权限
以下是一个请求权限的示例:
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 {// 不能启动文件操作}
}
在这个示例中,我们首先检查存储权限的状态。如果权限没有被授予,我们请求用户授予权限。如果权限被授予,我们可以开始文件操作。否则,我们不能开始文件操作。
2.3 处理权限拒绝
如果用户拒绝了我们的权限请求,我们需要处理这种情况。我们可以显示一个解释为什么我们需要这个权限的对话框,或者引导用户到系统设置中开启权限。例如:
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 {// 用户选择拒绝权限// 你可以在这里显示一个对话框// ...}
}
在这个示例中,如果用户永久地拒绝了权限,我们可以打开应用设置。如果用户只是拒绝了权限,我们可以显示一个对话框。
3. Flutter 文件存储
本节讲解如何在Flutter中进行文件存储,包括如何使用path_provider库来获取应用的文件存储路径,如何使用File类的方法来进行文件的读写操作,以及如何读写特殊类型的文件。
3.1 path_provider 库及其用法
path_provider 是一个Flutter插件,用于查找iOS和Android上的常用位置的路径。这个库可以帮助我们找到存储应用数据的正确位置。
3.1.1 安装 path_provider
首先,我们需要在项目中安装path_provider库。在pubspec.yaml文件中添加以下依赖:
dependencies:flutter:sdk: flutterpath_provider: ^2.1.1
3.1.2 获取临时目录
我们可以使用getTemporaryDirectory方法来获取临时目录的路径。临时目录是一个可以用来存储临时数据的目录。系统可能会随时清理这个目录,因此不应将重要数据保存在这里。
import 'package:path_provider/path_provider.dart';Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;print(tempPath);
3.1.3 取应用程序目录
我们可以使用getApplicationDocumentsDirectory方法来获取应用程序目录的路径。应用程序目录是一个可以用来存储应用需要持久化的数据的目录。系统不会清理这个目录,因此可以安全地将重要数据保存在这里。
import 'package:path_provider/path_provider.dart';Directory appDocDir = await getApplicationDocumentsDirectory();
String appDocPath = appDocDir.path;print(appDocPath);
3.1.4 获取外部存储中为应用程序创建的目录
我们可以使用 getExternalStorageDirectory 方法来获取外部存储中为应用程序创建的目录的路径。这个目录通常用来存储可以由用户在其他应用中访问的文件,如图片、音乐等。
import 'package:path_provider/path_provider.dart';Directory externalStorageDir = await getExternalStorageDirectory();
String externalStoragePath = externalStorageDir.path;print(externalStoragePath);
注意,由于Android 10的隐私更改,此方法不再可用。作为替代,你可以查看MediaStore。
3.1.5 获取应用程序缓存目录
我们可以使用getCacheDirectory方法来获取应用程序缓存目录的路径。这个目录用来存储临时缓存数据。
import 'package:path_provider/path_provider.dart';Directory cacheDir = await getCacheDirectory();
String cachePath = cacheDir.path;print(cachePath);
3.1.6 获取应用程序可以放置应用库文件的目录
我们可以使用getApplicationSupportDirectory方法来获取应用程序可以放置应用库文件的目录的路径。这个目录用来存储应用的支持文件,这些文件只有应用本身可以访问。
import 'package:path_provider/path_provider.dart';Directory supportDir = await getApplicationSupportDirectory();
String supportPath = supportDir.path;print(supportPath);
3.2 文件的读写操作
在Flutter中,我们可以使用File类的方法来进行文件的读写操作,与在Dart中的操作方式相同。因此你需要 导入 ‘dart:io’:
import 'dart:io';
具体操作这里不再赘述。
3.3 读写特殊类型的文件示例
在Flutter中,我们可以使用File类来读写各种类型的文件,包括图片、音频等特殊类型的文件。然而,由于这些特殊类型的文件通常是二进制格式的,因此我们需要使用readAsBytes和writeAsBytes方法来读写这些文件。
3.3.1 图片文件
- 读取图片文件
我们可以使用 readAsBytes 方法来读取图片文件,然后使用 Image.memory 构造函数来创建一个图片控件。
import 'dart:io';
import 'package:flutter/material.dart';void main() async {var file = File('path_to_your_image_file');var bytes = await file.readAsBytes();var image = Image.memory(bytes);runApp(MaterialApp(home: Scaffold(body: Center(child: image),),));
}
- 写入图片文件
我们可以使用writeAsBytes方法来写入图片文件。首先,我们需要获取图片的字节数据,然后将这些字节数据写入文件。
import 'dart:io';
import 'package:flutter/services.dart';void main() async {var bytes = await rootBundle.load('assets/image.png');var file = File('path_to_your_image_file');await file.writeAsBytes(bytes.buffer.asUint8List());
}
3.3.2 音频文件
- 读取音频文件
读取音频文件的方法与读取图片文件类似,我们可以使用 readAsBytes 方法来读取音频文件。然而,由于Flutter的核心库并不支持音频播放,因此我们需要使用一个第三方库,如audioplayers,来播放音频。
import 'dart:io';
import 'package:audioplayers/audioplayers.dart';void main() async {var file = File('path_to_your_audio_file');var bytes = await file.readAsBytes();var player = AudioPlayer();await player.playBytes(bytes);
}
- 写入音频文件
写入音频文件的方法与写入图片文件类似,我们可以使用writeAsBytes方法来写入音频文件。
import 'dart:io';
import 'package:flutter/services.dart';void main() async {var bytes = await rootBundle.load('assets/audio.mp3');var file = File('path_to_your_audio_file');await file.writeAsBytes(bytes.buffer.asUint8List());
}
相关文章:
Flutter笔记:目录与文件存储以及在Flutter中的使用(下)
Flutter笔记 目录与文件存储以及在Flutter中的使用(下) 文件读写与Flutter中文件管理 作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263 邮箱 :291148484163.com 本文地址:…...
机器学习笔记 - Ocr识别中的CTC算法原理概述
一、文字识别 在文本检测步骤中,分割出了文本区域。现在需要识别这些片段中存在哪些文本。 机器学习笔记 - Ocr识别中的文本检测EAST网络概述-CSDN博客文章浏览阅读300次。在 EAST 网络的这个分支中,它合并了 VGG16 网络不同层的特征输出。现在,该层之后的特征大小将等于 p…...
系列二、Lock接口
一、多线程编程模板 线程 操作 资源类 高内聚 低耦合 二、实现步骤 1、创建资源类 2、资源类里创建同步方法、同步代码块 三、12306卖票程序 3.1、synchronized实现 3.1.1、Ticket /*** Author : 一叶浮萍归大海* Date: 2023/11/20 8:54* …...
JVM虚拟机:通过日志学习PS+PO垃圾回收器
我们刚才设置参数的时候看到了-XXPrintGCDetails表示输出详细的GC处理日志,那么我们如何理解这个日志呢?日志是有规则的,我们需要按照这个规则来理解日志中的内容,它有两个格式,一个格式是GC的格式(新生代&…...
从0开始学习JavaScript--JavaScript使用Promise
JavaScript中的异步编程一直是开发中的重要话题。传统的回调函数带来了回调地狱和代码可读性的问题。为了解决这些问题,ES6引入了Promise,一种更现代、更灵活的异步编程解决方案。本文将深入探讨JavaScript中如何使用Promise,通过丰富的示例代…...
使用契约的链上限价订单
我们开发了链上限价订单。 它基于一种称为契约的智能合约,只有在花费输出的交易满足特定条件时才可以花费输出。 为了演示其工作原理,我们实施了以比特币支付的 Ordinals 代币买卖限价订单,无需托管人。 它可以运行在任何比特币协议链上&…...
Iceberg学习笔记(1)—— 基础知识
Iceberg是一个面向海量数据分析场景的开放表格式(Table Format),其设计的目的是解决数据存储和计算引擎之间的适配的问题 表格式(Table Format)可以理解为元数据以及数据文件的一种组织方式,处于计算框架&…...
springboot中动态api如何设置
1.不需要编写controller 等mvc层,通过接口动态生成api。 这个问题,其实很好解决,以前编写接口,是要写controller,需要有 RestController RequestMapping("/test1") public class xxxController{ ApiOperat…...
Java —— 抽象类和接口
目录 1. 抽象类 1.1 抽象类概念 1.2 抽象类语法与特性 1.3 抽象类的作用 2. 接口 2.1 接口的概念 2.2 接口的语法规则与特性 2.3 实现多个接口(解决多继承的问题) 2.4 接口间的继承 2.5 抽象类和接口的区别 2.6 接口的使用实例 2.7 Clonable 接口和深拷贝 2.7.1 Cloneable接口 …...
数字IC前端学习笔记:异步复位,同步释放
相关阅读 数字IC前端https://blog.csdn.net/weixin_45791458/category_12173698.html?spm1001.2014.3001.5482 异步复位 异步复位是一种常见的复位方式,可以使电路进入一个可知的状态。但是不正确地使用异步复位会导致出现意想不到的错误,复位释放便是…...
Linux内核移植之网络驱动更改说明一
一. 简介 本文学习 NXP官方Linux内核移植网络驱动的更改。 为了方便后面 Linux驱动的开发调试,所以,必须要把网络驱动调试好。 如果在做 Linux驱动开发时,写了一个 app或驱动,就需要将系统全部文件(即 uboot&#…...
邮件|gitpushgithub报错|Lombok注解
基于 Spring Boot 搭建一个定时发送邮件的项目可以按照以下步骤进行: 创建一个新的 Spring Boot 项目,并添加所需的依赖。在 pom.xml 文件中添加以下依赖项(根据你的需要进行调整): xml org.springframework.boot sp…...
【前端知识】Node——events模块的相关方法
一、events模块的常用方法 // 事件总线 const EventsEmitter require(events);const emitter new EventsEmitter();function HLog(msg){console.log(msg); }// 监听 emitter.on(hlog, HLog);setTimeout(() > {// 触发,打印emitter.emit(hlog, hello emitter!)…...
广州华锐互动VRAR | VR课件内容编辑器解决院校实践教学难题
VR课件内容编辑器由VR制作公司广州华锐互动开发,是一款专为虚拟现实教育领域设计的应用,它能够将传统的教学内容转化为沉浸式的三维体验。通过这款软件,教师可以轻松创建和编辑各种虚拟场景、模型和动画,以更生动、直观的方式展示…...
Wireshark抓包:理解TCP三次握手和四次挥手过程
TCP是一种面向连接、端到端可靠的协议,它被设计用于在互联网上传输数据和确保成功传递数据和消息。本节来介绍一下TCP中的三次握手和四次挥手。 文章目录 1 TCP头部格式2 wireshark抓包分析2.1 SEQ和ACK2.2 三次握手2.3 四次挥手 3 程序 1 TCP头部格式 TCP头部占据…...
网络工程师-HCIA网课视频学习
这里是速成的,只积累下,自己未曾学习到的东西。通过书本补充知识点。 视频:hcia17-链路聚合_哔哩哔哩_bilibili hcia16-路由高级特性: hcia17-链路聚合: 由于如果根据视频来学习的话,感觉视频的总结并不…...
【每日刷题——语音信号篇】
思考与练习 练习2.1 语音信号在产生的过程中,以及被感知的过程中,分别要经过人体的哪些器官? 1.产生过程: 肺部空气 → \rightarrow →冲击声带 → \rightarrow →通过声道(可以调节) → \rightarrow →…...
Linux进程通信——IPC、管道、FIFO的引入
进程间的通信——IPC 进程间通信 (IPC,InterProcess Communication) 是指在不同进程之间传播或交换信息。 IPC的方式通常有管道 (包括无名管道和命名管道) 、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。 …...
数理统计的基本概念(一)
文章目录 总体、样本与统计量总体及其分布样本及其分布统计量统计量概念样本矩顺序统计量及其分布样本中位数与样本极差经验分布函数 参考文献 总体、样本与统计量 总体及其分布 在数理统计中,称所研究的对象的全体为总体,总体中的元素称为个体。若总体…...
clickhouse分布式之弹性扩缩容的故事
现状 社区不支持喔,以后也不会有了。曾经尝试过,难道是是太难了,无法实现吗?因为他们企业版支持了,可能是利益相关吧,谁知道呢,毕竟开源也要赚钱,谁乐意一直付出没有回报呢。 社区…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...
渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
vue3 daterange正则踩坑
<el-form-item label"空置时间" prop"vacantTime"> <el-date-picker v-model"form.vacantTime" type"daterange" start-placeholder"开始日期" end-placeholder"结束日期" clearable :editable"fal…...
Python 高级应用10:在python 大型项目中 FastAPI 和 Django 的相互配合
无论是python,或者java 的大型项目中,都会涉及到 自身平台微服务之间的相互调用,以及和第三发平台的 接口对接,那在python 中是怎么实现的呢? 在 Python Web 开发中,FastAPI 和 Django 是两个重要但定位不…...
