【Flutter 實戰】文件系統目錄

老孟導讀:Flutter 中獲取文件路徑,咱們都知道使用 path_provider,但對其目錄對含義不是很清楚,此文介紹 Android、iOS 系統的文件目錄,不一樣場景下建議使用的目錄。android

不一樣的平臺對應的文件系統是不一樣的,好比文件路徑,所以 Flutter 中獲取文件路徑須要原生支持,原生端經過 MethodChannel 傳遞文件路徑到 Flutter,若是沒有特殊的需求,推薦你們使用 Google 官方維護的插件 path_providerios

pub 地址:https://pub.flutter-io.cn/packages/path_providergit

Github 地址:https://github.com/flutter/plugins/tree/master/packages/path_provider/path_providergithub

添加依賴

在項目的 pubspec.yaml 文件中添加依賴:sql

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微信

    臨時目錄,適用於下載的緩存文件,此目錄隨時能夠清除,此目錄爲應用程序私有目錄,其餘應用程序沒法訪問此目錄。app

    Android 上對應getCacheDiride

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

相關文章
相關標籤/搜索