【Flutter 实战】文件系统目录
老孟导读:Flutter 中获取文件路径,我们都知道使用 path_provider,但对其目录对含义不是很清楚,此文介绍 Android、iOS 系统的文件目录,不同场景下建议使用的目录。
不同的平台对应的文件系统是不同的,比如文件路径,因此 Flutter 中获取文件路径需要原生支持,原生端通过 MethodChannel 传递文件路径到 Flutter,如果没有特殊的需求,推荐大家使用 Google 官方维护的插件 path_provider。
pub 地址:https://pub.flutter-io.cn/packages/path_provider
Github 地址:https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider
添加依赖
在项目的 pubspec.yaml
文件中添加依赖:
dependencies:
path_provider: ^1.6.14
执行命令:
flutter pub get
文件路径
path_provider(版本:1.6.14)提供了8个方法获取不同的文件路径,目前 Flutter(Flutter 1.20.1 • channel stable )只发布了正式版本的 Android 和 iOS,因此下面仅介绍 Android 和 iOS 平台的文件路径。
getTemporaryDirectory
临时目录,适用于下载的缓存文件,此目录随时可以清除,此目录为应用程序私有目录,其他应用程序无法访问此目录。
Android 上对应
getCacheDir
。iOS上对应
NSCachesDirectory
。getApplicationSupportDirectory
应用程序可以在其中放置应用程序支持文件的目录的路径。
将此文件用于您不想向用户公开的文件。 您的应用不应将此目录用于存放用户数据文件。
在iOS上,对应
NSApplicationSupportDirectory
,如果此目录不存在,则会自动创建。
在Android上,对应getFilesDir
。getLibraryDirectory
应用程序可以在其中存储持久性文件,备份文件以及对用户不可见的文件的目录路径,例如storage.sqlite.db。
在Android上,此函数抛出[UnsupportedError]异常,没有等效项路径存在。
getApplicationDocumentsDirectory
应用程序可能在其中放置用户生成的数据或应用程序无法重新创建的数据的目录路径。
在iOS上,对应
NSDocumentDirectory
API。 如果数据不是用户生成的,考虑使用[getApplicationSupportDirectory]。在Android上,对应
getDataDirectory
API。 如果要让用户看到数据,请考虑改用[getExternalStorageDirectory]。getExternalStorageDirectory
应用程序可以访问顶级存储的目录的路径。由于此功能仅在Android上可用,因此应在发出此函数调用之前确定当前操作系统。
在iOS上,此功能会引发[UnsupportedError]异常,因为无法在应用程序的沙箱外部访问。
在Android上,对应
getExternalFilesDir(null)
。getExternalCacheDirectories
存储特定于应用程序的外部缓存数据的目录的路径。 这些路径通常位于外部存储(如单独的分区或SD卡)上。 电话可能具有多个可用的存储目录。
由于此功能仅在Android上可用,因此应在发出此函数调用之前确定当前操作系统。
在iOS上,此功能会抛出UnsupportedError,因为这是不可能的在应用程序的沙箱外部访问。在Android上,对应
Context.getExternalCacheDirs()
或API Level 低于19的Context.getExternalCacheDir()
。getExternalStorageDirectories
可以存储应用程序特定数据的目录的路径。 这些路径通常位于外部存储(如单独的分区或SD卡)上。
由于此功能仅在Android上可用,因此应在发出此函数调用之前确定当前操作系统。
在iOS上,此功能会抛出UnsupportedError,因为这是不可能的在应用程序的沙箱外部访问。
在Android上,对应Context.getExternalFilesDirs(String type)
或API Level 低于19的Context.getExternalFilesDir(String type)
。getDownloadsDirectory
存储下载文件的目录的路径,这通常仅与台式机操作系统有关。
在Android和iOS上,此函数将引发[UnsupportedError]异常。
如果没有 Android 或者 iOS开发经验,看完上面的说明应该是一脸懵逼的,这么多路径到底用哪个?有什么区别?下面从 Android 和 iOS 平台的角度介绍其文件路径,最后给出路径使用的建议以及使用过程中需要注意的事项。
Android 文件存储
Android 文件存储分为内部存储和外部存储。
内部存储
用于保存应用的私有文件,其他应用无法访问这些数据,创建的文件在此应用的包名目录下,没有 root 权限 的手机无法在手机的 文件管理 应用中看到此目录,不过可以通过 Android Studio 工具查看,路径为:data/data/包名:
看下包名下具体的目录结构:
- cache 目录:对应 getTemporaryDirectory 方法,用于缓存文件,此目录随时可能被系统清除。
- files 目录:对应 getApplicationSupportDirectory 方法。
- code_cache:此目录存储 Flutter 相关代码和资源。
- flutter_engine/skia:Flutter 渲染引擎。
- flutter_guidePVWGWK/flutter_guide/build/flutter_assets:Flutter 资源文件。
- shared_prefs: SharePreferences 的默认路径。
- app_flutter:对应 getApplicationDocumentsDirectory方法。
- app_flutter/dbName:使用 sqlite 的默认路径,sqlite 也可以指定位置。
SharePreferences 和 sqlite 是两种保存数据的第三方插件。
内部存储的特点:
- 安全性,其他应用无法访问这些数据。
- 当应用卸载的时候,这些数据也会被删除,避免垃圾文件。
- 不需要申请额外权限。
- 存储的空间有限,此目录数据随时可能被系统清除,也可以通过 设置 中的 清除数据 可以清除此目录数据。
- 国内特色,不同手机厂商对此目录做了不同的限制,比如总体大小限制、单个应用程序所占空间大小限制、清除数据策略不同等。
外部存储
外部存储可以通过手机的 文件管理 应用查看,
这里面有一个特殊的目录:Android/data/包名:
看到这个目录是不是觉得和内部存储目录非常相似,一个包名代表一个应用程序:
- cache:缓存目录,对应 getExternalCacheDirectories 方法。
- files:对应 getExternalStorageDirectories 方法。
此目录的特点:
- 当应用卸载的时候,这些数据也会被删除,避免垃圾文件。
- 不需要申请额外权限。
- 空间大且不会被系统清除,通过 设置 中的 清除数据 可以清除此目录数据。
- 用户可以直接对文件进行删除、导入操作。
外部存储除了 Android/data/ 目录,还有和此目录同级的目录,特点:
- 所有应用程序均可访问。
- 用户可以直接对文件进行删除、导入操作。
- 需要申请读写权限。
Android 官方对此目录的管理越来越严格, Android 11 系统已经开始强制执行分区存储,详情见:https://developer.android.com/preview/privacy/storage?hl=zh-cn
上面说了这么多,总结如下:
- SharePreferences 和 sqlite 数据建议存放在内部存储,插件已经帮我们完成了,无需手动处理。
- 严格保密的数据,比如用户数据,建议存放在内部存储,对应 getApplicationSupportDirectory 方法。
- 其余所有的数据建议存放 Android/data/包名/ ,对应 getExternalCacheDirectories 和 getExternalStorageDirectories 方法。
iOS 文件存储
iOS 文件存储相比 Android 要简单的多,因为 iOS 对用户隐私保护非常严格,每个 iOS 应用程序都有一个单独的文件系统,而且只能在对应的文件系统中进行操作,此区域被称为沙盒。
每个应用沙盒含有3个文件夹:Documents, Library 和 tmp:
- Documents:应用程序数据文件写入到这个目录下。这个目录用于存储用户数据。保存应用程序的重要数据文件和用户数据文件等。iTunes 同步时会备份该目录,对应 getApplicationDocumentsDirectory 方法。
- Library:对应 getLibraryDirectory 方法。
- Caches:保存应用程序使用时产生的支持文件、缓存文件、日志文件等,比如下载的音乐,视频,SDWebImage缓存等。对应 getTemporaryDirectory 方法。
- Preferences:包含应用程序的偏好设置文件,iCloud会备份设置信息。
- Application Support:对应 getApplicationSupportDirectory 方法。
- tmp:存放临时文件,不会被备份,而且这个文件下的数据有可能随时被清除的可能,按照官方说法每三天清理一次缓存数据。
path_provider 使用
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
///
/// desc:
///
class PathProviderDemo extends StatefulWidget {
@override
_PathProviderDemoState createState() => _PathProviderDemoState();
}
class _PathProviderDemoState extends State<PathProviderDemo> {
Future<Directory> _tempDirectory;
Future<Directory> _appSupportDirectory;
Future<Directory> _appLibraryDirectory;
Future<Directory> _appDocumentsDirectory;
Future<Directory> _externalStorageDirectory;
Future<List<Directory>> _externalStorageDirectories;
Future<List<Directory>> _externalCacheDirectories;
Future<Directory> _downloadDirectory;
@override
void initState() {
super.initState();
setState(() {
_tempDirectory = getTemporaryDirectory();
_appSupportDirectory = getApplicationSupportDirectory();
_appLibraryDirectory = getLibraryDirectory();
_appDocumentsDirectory = getApplicationDocumentsDirectory();
_externalStorageDirectory = getExternalStorageDirectory();
_externalCacheDirectories = getExternalCacheDirectories();
_externalStorageDirectories = getExternalStorageDirectories();
_downloadDirectory = getDownloadsDirectory();
});
}
Widget _buildDirectory(
BuildContext context, AsyncSnapshot<Directory> snapshot) {
Text text = const Text('');
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
text = Text('Error: ${snapshot.error}');
} else if (snapshot.hasData) {
text = Text('path: ${snapshot.data.path}');
} else {
text = const Text('path unavailable');
}
}
return Padding(padding: EdgeInsets.symmetric(horizontal: 16), child: text);
}
Widget _buildDirectories(
BuildContext context, AsyncSnapshot<List<Directory>> snapshot) {
Text text = const Text('');
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
text = Text('Error: ${snapshot.error}');
} else if (snapshot.hasData) {
final String combined =
snapshot.data.map((Directory d) => d.path).join(', ');
text = Text('paths: $combined');
} else {
text = const Text('path unavailable');
}
}
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16), child: text);
}
Widget _buildItem(String title, Future<Directory> future) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(title),
),
FutureBuilder<Directory>(future: future, builder: _buildDirectory),
],
);
}
Widget _buildItem1(String title, Future<List<Directory>> future) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(title),
),
FutureBuilder<List<Directory>>(
future: future,
builder: _buildDirectories),
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: ListView(
itemExtent: 120,
children: <Widget>[
_buildItem('getTemporaryDirectory', _tempDirectory),
_buildItem('getApplicationSupportDirectory', _appSupportDirectory),
_buildItem('getLibraryDirectory', _appLibraryDirectory),
_buildItem(
'getApplicationDocumentsDirectory', _appDocumentsDirectory),
_buildItem(
'getExternalStorageDirectory', _externalStorageDirectory),
_buildItem('getDownloadsDirectory', _downloadDirectory),
_buildItem1('getExternalStorageDirectories',_externalStorageDirectories),
_buildItem1('getExternalCacheDirectories',_externalCacheDirectories),
],
),
),
);
}
}
Android 系统各个路径:
iOS 系统各个路径:
交流
交流
老孟Flutter博客(330个控件用法+实战入门系列文章):http://laomengit.com
欢迎加入Flutter交流群(微信:laomengit)、关注公众号【老孟Flutter】:
【Flutter 实战】文件系统目录的更多相关文章
- Flutter实战视频-移动电商-02.Flutter实战建立项目和编写入口文件
02.Flutter实战建立项目和编写入口文件 创建项目: flutter create flutter_shop 创建完成之后呢,它会提示我们, 进入flutter_shop的目录,然后执行flut ...
- 《Flutter实战》开源电子书
<Flutter实战>开源电子书 <Flutter实战> 开源了,本书为 Flutter中文网开源电子书项目,本书系统介绍了Flutter技术的各个方面,本书属于原创书籍(并非 ...
- Flutter实战】文本组件及五大案例
老孟导读:大家好,这是[Flutter实战]系列文章的第二篇,这一篇讲解文本组件,文本组件包括文本展示组件(Text和RichText)和文本输入组件(TextField),基础用法和五个案例助你快速 ...
- 【Flutter实战】图片组件及四大案例
老孟导读:大家好,这是[Flutter实战]系列文章的第三篇,这一篇讲解图片组件,Image有很多高级用法,希望对您有所帮助. 图片组件是Flutter基础组件之一,和文本组件一样必不可少.图片组件包 ...
- Linux文件系统目录
Linux操作系统目录呈树形结构,文件系统只有一个根目录,其余文件都是从根目录下延伸出来的 上图是一个Linux文件系统目录的展现,现在我们来看一下文件系统目录下相关目录及其功能 根目录( / ) L ...
- 《Flutter 实战》开源电子书
<Flutter 实战>开源电子书 转 https://blog.csdn.net/OQjya206rsQ71/article/details/86619630 关于 Flutter ...
- 什么是FHS,Linux的文件系统目录标准是怎样的
Filesystem Hierarchy Standard(文件系统目录标准)的缩写,多数Linux版本采用这种文件组织形式,类似于Windows操作系统中c盘的文件目录,FHS采用树形结构组织文件. ...
- 【Flutter实战】移动技术发展史
老孟导读:大家好,这是[Flutter实战]系列文章的第一篇,这并不是一篇Flutter技术文章,而是介绍智能手机操作系统.跨平台技术的演进以及我对各种跨平台技术看法的文章. 智能手机操作系统 塞班( ...
- 【Flutter实战】定位装饰权重组件及柱状图案例
老孟导读:Flutter中有这么一类组件,用于定位.装饰.控制子组件,比如 Container (定位.装饰).Expanded (扩展).SizedBox (固定尺寸).AspectRatio (宽 ...
随机推荐
- sizeof的用法 2007-12-19 11:06
sizeof的作用是什么?sizeof是C/C++中的一个操作符(operator),简单的说其作用就是返回一个对象或者类型所占的内存字节数.其返回值类型为size_t,在头文件stddef.h中定义 ...
- 隐藏jqgrid滚动条
.ui-jqgrid .ui-jqgrid-bdiv{ overflow-x: hidden; } /* 隐藏jqgrid滚动条 */
- java如何将char类型的数字转换成int型的数字,而不是Ascii
如何把 char ‘3’ 转为 int 3, 大家应该知道,不能直接转化,那样得到是‘3’的Ascii. 如下面: public class Leet { public static void mai ...
- ASP.Net中的async+await异步编程
在.NET Framework4.5框架.C#5.0语法中,通过async和await两个关键字,引入了一种新的基于任务的异步编程模型(TAP).在这种方式下,可以通过类似同步方式编写异步代码,极大简 ...
- composer安装包的时候触发PHP fatal error,提示允许的内存耗光
在composer require之前加COMPOSER_MEMORY_LIMIT=-1
- selenium定位方法(二)
selenium定位方法(二) 1.xpath定位:xpath是在XML中查找节点所在的路径的表达式 1)绝对路径的Xpath表达式 例:/html/body/div/div[1]/ul//li[3 ...
- 操作系统-I/O(2)设备的分配
作业执行前对设备提出申请时,指定某台具体的物理设备会让设备分配变得简单,但如果所指定设备出现故障,即便计算机系统中有同类设备也不能运行 设备独立性:用户通常不指定物理设备,而是指定逻辑设备,使得用户作 ...
- SPSSAU新功能上线:高级公式、综合得分一键计算!
一直关注我们的朋友们一定会发现,近期SPSSAU增添了很多新功能. 我们精挑细选出6个最常使用的功能,介绍给大家,看看这些新功能你有没有解锁成功呢? 01 一键删除无效样本 “无效样本”功能中,添加了 ...
- WPF Devexpress ChartControl CrosshairLabel显示内容居右
源码可加Q群:580749909. 一.解决的问题 ChartControl中希望CrosshairLabel的内容据右 or 自定义 二.实现. 多个显示实例(实例:条形,线形,点等等)下的内容设置 ...
- vue cli3如何引入全局less变量
最近在项目中需要写一个全局的style.less,然后在各组件中可以直接调用: 1.在assets下创建一个less文件: 2.安装style-resources-loader (npm i styl ...