乐趣区

关于flutter: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 也能够指定地位。

SharePreferencessqlite 是两种保留数据的第三方插件。

外部存储的特点:

  • 安全性,其余利用无法访问这些数据。
  • 当利用卸载的时候,这些数据也会被删除,防止垃圾文件。
  • 不须要申请额定权限。
  • 存储的空间无限,此目录数据随时可能被零碎革除,也能够通过 设置 中的 革除数据 能够革除此目录数据。
  • 国内特色,不同手机厂商对此目录做了不同的限度,比方总体大小限度、单个应用程序所占空间大小限度、革除数据策略不同等。

内部存储

内部存储能够通过手机的 文件治理 利用查看,

这外面有一个非凡的目录:Android/data/ 包名:

看到这个目录是不是感觉和外部存储目录十分类似,一个包名代表一个应用程序:

  • cache:缓存目录,对应 getExternalCacheDirectories 办法。
  • files:对应 getExternalStorageDirectories 办法。

此目录的特点:

  • 当利用卸载的时候,这些数据也会被删除,防止垃圾文件。
  • 不须要申请额定权限。
  • 空间大且不会被零碎革除,通过 设置 中的 革除数据 能够革除此目录数据。
  • 用户能够间接对文件进行删除、导入操作。

内部存储除了 Android/data/ 目录,还有和此目录同级的目录,特点:

  • 所有应用程序均可拜访。
  • 用户能够间接对文件进行删除、导入操作。
  • 须要申请 读写权限

Android 官网对此目录的治理越来越严格,Android 11 零碎曾经开始强制执行分区存储,详情见:https://developer.android.com/preview/privacy/storage?hl=zh-cn

下面说了这么多,总结如下:

  • SharePreferencessqlite 数据倡议寄存在外部存储,插件曾经帮咱们实现了,无需手动解决。
  • 严格窃密的数据,比方用户数据,倡议寄存在外部存储,对应 getApplicationSupportDirectory 办法。
  • 其余所有的数据倡议寄存 Android/data/ 包名 /,对应 getExternalCacheDirectoriesgetExternalStorageDirectories 办法。

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】:

退出移动版