cordova-plugin-file

Android Testsuite Chrome Testsuite iOS Testsuite Lint Test

此插件实现了一个文件 API,允许对设备上的文件进行读/写访问。

此插件基于多个规范,包括:HTML5 文件 API http://www.w3.org/TR/FileAPI/

目录和系统扩展 最新:http://www.w3.org/TR/2012/WD-file-system-api-20120417/ 虽然大多数插件代码是在较早的规范当前时编写的:http://www.w3.org/TR/2011/WD-file-system-api-20110419/

它还实现了 FileWriter 规范:http://dev.w3.org/2009/dap/file-system/file-writer.html

注意 虽然 W3C FileSystem 规范对于 Web 浏览器来说已被弃用,但对于 受支持平台 列表中列出的平台,此插件在 Cordova 应用程序中支持 FileSystem API,但浏览器平台除外。

要了解如何使用该插件的一些想法,请查看本页面底部的示例。有关其他示例(以浏览器为中心),请参阅 HTML5 Rocks 的 FileSystem 文章。

有关其他存储选项的概述,请参阅 Cordova 的存储指南

此插件定义了一个全局 cordova.file 对象。

尽管该对象位于全局作用域中,但在 deviceready 事件触发之前,应用程序无法使用该对象。

document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
    console.log(cordova.file);
}

安装

cordova plugin add cordova-plugin-file

支持的平台

  • Android
  • iOS
  • OS X
  • Windows*
  • 浏览器

* 这些平台不支持 FileReader.readAsArrayBufferFileWriter.write(blob)

存储文件的位置

从 v1.2.0 开始,提供了重要文件系统目录的 URL。每个 URL 的格式为 file:///path/to/spot/,并且可以使用 window.resolveLocalFileSystemURL() 转换为 DirectoryEntry

  • cordova.file.applicationDirectory - 安装应用程序的只读目录。(iOSAndroidBlackBerry 10OSXwindows

  • cordova.file.applicationStorageDirectory - 应用程序沙箱的根目录;在 iOS 和 windows 上,此位置是只读的(但特定的子目录[如 iOS 上的 /Documents 或 windows 上的 /localState]是可读写的)。其中包含的所有数据都是该应用程序的私有数据。(iOSAndroidBlackBerry 10OSX

  • cordova.file.dataDirectory - 使用内部存储器(在 Android 上,如果需要使用外部存储器,请使用 .externalDataDirectory)的应用程序沙箱内持久且私有的数据存储。在 iOS 上,此目录不会与 iCloud 同步(使用 .syncedDataDirectory)。(iOSAndroidBlackBerry 10windows

  • cordova.file.cacheDirectory - 用于缓存数据文件或应用程序可以轻松重新创建的任何文件的目录。当设备的存储空间不足时,操作系统可能会删除这些文件,但应用程序不应依赖操作系统来删除此处的这些文件。(iOSAndroidBlackBerry 10OSXwindows

  • cordova.file.externalApplicationStorageDirectory - 外部存储上的应用程序空间。(Android)。请参阅 怪癖

  • cordova.file.externalDataDirectory - 在外部存储上放置特定于应用程序的数据文件的位置。(Android)。请参阅 怪癖

  • cordova.file.externalCacheDirectory - 外部存储上的应用程序缓存。(Android)。请参阅 怪癖

  • cordova.file.externalRootDirectory - 外部存储(SD 卡)根目录。(AndroidBlackBerry 10)。请参阅 怪癖

  • cordova.file.tempDirectory - 操作系统可以随意清除的临时目录。不要依赖操作系统来清除此目录;应用程序应始终在适用时删除文件。(iOSOSXwindows

  • cordova.file.syncedDataDirectory - 保存应同步(例如,同步到 iCloud)的特定于应用程序的文件。(iOSwindows

  • cordova.file.documentsDirectory - 应用程序私有的文件,但对其他应用程序有意义(例如,Office 文件)。请注意,对于 OSX,这是用户的 ~/Documents 目录。(iOSOSX

  • cordova.file.sharedDirectory - 全局可供所有应用程序使用的文件 (BlackBerry 10)

文件系统布局

虽然技术上是实现细节,但了解 cordova.file.* 属性如何映射到实际设备上的物理路径非常有用。

iOS 文件系统布局

设备路径 cordova.file.* iosExtraFileSystems r/w? 持久? OS 清除 同步 私有
/var/mobile/Applications/<UUID>/ applicationStorageDirectory - r 不适用 不适用 不适用
   appname.app/ applicationDirectory bundle r 不适用 不适用 不适用
      www/ - - r 不适用 不适用 不适用
   Documents/ documentsDirectory documents r/w
      NoCloud/ - documents-nosync r/w
   Library - library r/w 是?
      NoCloud/ dataDirectory library-nosync r/w
      Cloud/ syncedDataDirectory - r/w
      Caches/ cacheDirectory cache r/w 是* 是***
   tmp/ tempDirectory - r/w 否** 是***

* 文件在应用程序重启和升级后仍然存在,但操作系统可以随时清除此目录。您的应用程序应该能够重新创建任何可能被删除的内容。

** 文件可能会在应用程序重启后仍然存在,但不要依赖这种行为。不能保证文件在更新后仍然存在。当适用时,您的应用程序应从此目录中删除文件,因为操作系统不保证何时(或是否)删除这些文件。

*** 操作系统可能会在它认为必要时清除此目录的内容,但不要依赖此行为。您应该根据您的应用程序清除此目录。

Android 文件系统布局

设备路径 cordova.file.* AndroidExtraFileSystems r/w? 持久? OS 清除 私有
file:///android_asset/ applicationDirectory assets r 不适用 不适用
/data/data/<app-id>/ applicationStorageDirectory - r/w 不适用 不适用
   cache cacheDirectory cache r/w 是*
   files dataDirectory files r/w
      Documents   documents r/w
<sdcard>/ externalRootDirectory sdcard r/w***
   Android/data/<app-id>/ externalApplicationStorageDirectory - r/w
      cache externalCacheDirectory cache-external r/w 否**
      files externalDataDirectory files-external r/w

* 操作系统可能会定期清除此目录,但不要依赖这种行为。根据您的应用程序清除此目录的内容。如果用户手动清除缓存,则会删除此目录的内容。

** 操作系统不会自动清除此目录;您有责任自己管理内容。如果用户手动清除缓存,则会删除该目录的内容。

*** 从 API 30 开始,这些目录不再可写。

注意:如果无法挂载外部存储,则 cordova.file.external* 属性为 null

Android 的外部存储怪癖

随着 Scoped Storage 的引入,通过文件 API 访问外部存储变得不可靠或受到限制。Scoped Storage 是在 API 29 中引入的。虽然现有应用程序可能可以选择退出,但此选项不适用于新应用程序。在 Android API 30 及更高版本上,完全强制执行 Scoped Storage。

此外,API 29 不支持直接文件访问。这意味着此插件无法访问 API 29 设备上的外部存储介质。

API 30 引入了 FUSE,它允许使用文件 API 有限地访问外部存储,从而使此插件可以再次部分工作。

有限访问包括但不限于

  • 使用适当的 READ_EXTERNALREAD_MEDIA_* 权限的只读访问。
  • 只读访问仅限于媒体文件,而不包括文档。
  • 写入仅限于您的应用程序拥有的文件。通过文件 API 无法修改第三方应用程序拥有的文件(包括例如通过相机插件创建的图像文件)。
  • 并非外部存储中的所有路径都是可写的。

这些限制仅适用于外部文件系统(例如,cordova.file.external* 路径)。内部文件系统(如 cordova.file.dataDirectory 路径)不受这些限制的限制。

如果与外部文件系统交互是您的应用程序的要求,请考虑使用 MediaStore 插件。

OS X 文件系统布局

设备路径 cordova.file.* iosExtraFileSystems r/w? OS 清除 私有
/Applications/<appname>.app/ - bundle r 不适用
    Content/Resources/ applicationDirectory - r 不适用
~/Library/Application Support/<bundle-id>/ applicationStorageDirectory - r/w
    files/ dataDirectory - r/w
~/Documents/ documentsDirectory documents r/w
~/Library/Caches/<bundle-id>/ cacheDirectory cache r/w
/tmp/ tempDirectory - r/w 是*
/ rootDirectory root r/w 否**

注意:这是非沙盒应用程序的布局。如果您启用沙盒,则 applicationStorageDirectory 将位于 `~/Library/Containers/` 下方。/Data/Library/Application Support`。

* 文件在应用程序重启和升级后仍然存在,但操作系统可以随时清除此目录。您的应用程序应该能够重新创建任何可能被删除的内容。您应该根据您的应用程序清除此目录。

** 允许访问整个文件系统。这仅适用于非沙盒应用程序。

Windows 文件系统布局

设备路径 cordova.file.* r/w? 持久? OS 清除 私有
ms-appdata:/// applicationDirectory r 不适用 不适用
   local/ dataDirectory r/w
   temp/ cacheDirectory r/w 是*
   temp/ tempDirectory r/w 是*
   roaming/ syncedDataDirectory r/w

* 操作系统可能会定期清除此目录

Android 怪癖

Android 持久存储位置

在 Android 设备上存储持久文件有多个有效位置。有关各种可能性的广泛讨论,请参阅此页面

该插件的早期版本会在启动时根据设备是否声明已挂载 SD 卡(或等效存储分区)来选择临时文件和持久文件的位置。如果 SD 卡已挂载,或者有较大的内部存储分区可用(例如在 Nexus 设备上),则持久文件将存储在该空间的根目录中。这意味着所有 Cordova 应用都可以看到该卡上的所有文件。

如果 SD 卡不可用,则早期版本会将数据存储在 /data/data/<packageId> 下,这会将应用彼此隔离,但仍可能导致数据在用户之间共享。

现在可以选择将文件存储在内部文件存储位置,或者使用之前的逻辑,并在应用程序的 config.xml 文件中设置首选项。为此,请将以下两行之一添加到 config.xml

<preference name="AndroidPersistentFileLocation" value="Internal" />

<preference name="AndroidPersistentFileLocation" value="Compatibility" />

如果没有此行,File 插件将使用 Internal 作为默认值。如果存在首选项标记,但不是这些值之一,则应用程序将不会启动。

如果你的应用程序之前已发布给用户,使用的是此插件的旧版本(3.0.0 之前),并且已将文件存储在持久文件系统中,则如果你的 config.xml 未指定持久文件系统的位置,则应将首选项设置为 Compatibility。将位置切换为“Internal”意味着升级其应用程序的现有用户可能无法访问其先前存储的文件,具体取决于他们的设备。

如果你的应用程序是新的,或者以前从未将文件存储在持久文件系统中,则通常建议使用 Internal 设置。

/android_asset 的慢速递归操作

在 Android 上,列出 asset 目录非常慢。但是,你可以通过将 src/android/build-extras.gradle 添加到你的 Android 项目的根目录来加速它(也需要 [email protected] 或更高版本)。

当外部存储未在 Marshmallow 上挂载时,允许写入外部存储

Marshmallow 要求应用在读取/写入外部位置时请求权限。默认情况下,你的应用有权写入 cordova.file.applicationStorageDirectorycordova.file.externalApplicationStorageDirectory,并且除非外部存储未挂载,否则该插件不会请求这两个目录的权限。但是,由于一个限制,当外部存储未挂载时,它会请求写入 cordova.file.externalApplicationStorageDirectory 的权限。

SDK 目标低于 29

根据 Android 11 中的存储更新 官方文档,WRITE_EXTERNAL_STORAGE 权限不再有效,并且不提供访问权限。

如果未对以 Build.VERSION_CODES.Q (SDK 29) 之前的 API 级别为目标的应用允许此权限,则无法向该应用授予此权限。

如果你需要添加此权限,请将以下内容添加到你的 config.xml 中。

<config-file target="AndroidManifest.xml" parent="/*" xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
</config-file>

iOS 怪癖

  • cordova.file.applicationStorageDirectory 是只读的;尝试在根目录中存储文件将失败。请使用为 iOS 定义的其他 cordova.file.* 属性之一(只有 applicationDirectoryapplicationStorageDirectory 是只读的)。
  • FileReader.readAsText(blob, encoding)
    • 不支持 encoding 参数,并且始终使用 UTF-8 编码。

iOS 持久存储位置

在 iOS 设备上有两个有效的位置来存储持久文件:Documents 目录和 Library 目录。该插件的早期版本始终只将持久文件存储在 Documents 目录中。这产生了一个副作用,即所有应用程序的文件都可以在 iTunes 中看到,这通常不是预期的,特别是对于处理大量小文件的应用程序,而不是生成用于导出的完整文档,这才是该目录的预期用途。

现在可以选择将文件存储在 Documents 目录或 Library 目录中,并在你的应用程序的 config.xml 文件中设置首选项。为此,请将以下两行之一添加到 config.xml

<preference name="iosPersistentFileLocation" value="Library" />

<preference name="iosPersistentFileLocation" value="Compatibility" />

如果没有此行,File 插件将使用 Compatibility 作为默认值。如果存在首选项标记,但不是这些值之一,则应用程序将不会启动。

如果你的应用程序之前已发布给用户,使用的是此插件的旧版本(1.0 之前),并且已将文件存储在持久文件系统中,则应将首选项设置为 Compatibility。将位置切换为 Library 将意味着升级其应用程序的现有用户将无法访问其先前存储的文件。

如果你的应用程序是新的,或者以前从未将文件存储在持久文件系统中,则通常建议使用 Library 设置。

浏览器怪癖

常见怪癖和说明

  • 每个浏览器都使用其自己的沙盒文件系统。IE 和 Firefox 使用 IndexedDB 作为基础。所有浏览器都在路径中使用正斜杠作为目录分隔符。
  • 必须依次创建目录条目。例如,如果 dir1 不存在,则调用 fs.root.getDirectory('dir1/dir2', {create:true}, successCallback, errorCallback) 将失败。
  • 该插件会在应用程序首次启动时请求用户允许使用持久存储。
  • 该插件仅支持 cdvfile://127.0.0.1(本地资源)。即,不支持通过 cdvfile 访问外部资源。
  • 该插件不遵循 “文件系统 API 8.3 命名限制”
  • Blob 和 File 的 close 函数不受支持。
  • 此插件不支持 FileSaverBlobBuilder,并且没有存根。
  • 该插件不支持 requestAllFileSystems。规范中也缺少此函数。
  • 如果对现有目录使用 create: true 标志,则目录中的条目将不会被删除。
  • 不支持通过构造函数创建的文件。应该改用 entry.file 方法。
  • 每个浏览器都使用其自己的 blob URL 引用格式。
  • 支持 readAsDataURL 函数,但是 Chrome 中的 mediatype 取决于条目名称扩展名,IE 中的 mediatype 始终为空(根据规范,这与 text-plain 相同),Firefox 中的 mediatype 始终为 application/octet-stream。例如,如果内容是 abcdefg,则 Firefox 返回 data:application/octet-stream;base64,YWJjZGVmZw==,IE 返回 data:;base64,YWJjZGVmZw==,Chrome 返回 data:<mediatype depending on extension of entry name>;base64,YWJjZGVmZw==
  • toInternalURL 返回 file:///persistent/path/to/entry 形式的路径(Firefox、IE)。Chrome 返回 cdvfile://127.0.0.1/persistent/file 形式的路径。

Chrome 怪癖

  • Chrome 文件系统在设备就绪事件之后不会立即准备就绪。作为一种解决方法,你可以订阅 filePluginIsReady 事件。例如
    window.addEventListener('filePluginIsReady', function(){ console.log('File plugin is ready');}, false);
    

    你可以使用 window.isFilePluginReadyRaised 函数来检查事件是否已触发。

  • 在 Chrome 中,window.requestFileSystem 的 TEMPORARY 和 PERSISTENT 文件系统配额不受限制。
  • 要在 Chrome 中增加持久存储,你需要调用 window.initPersistentFileSystem 方法。默认情况下,持久存储配额为 5 MB。
  • Chrome 需要 --allow-file-access-from-files 运行参数才能支持通过 file:/// 协议访问 API。
  • 如果你在获取现有 Entry 时使用标志 {create:true},则 File 对象将不会更改。
  • 在 Chrome 中,事件的 cancelable 属性设置为 true。这与 规范 相反。
  • Chrome 中的 toURL 函数根据应用程序主机返回以 filesystem: 为前缀的路径。例如,filesystem:file:///persistent/somefile.txtfilesystem:https://127.0.0.1:8080/persistent/somefile.txt
  • 如果为目录条目,则 toURL 函数结果不包含尾部斜杠。但是,Chrome 会正确解析带有斜杠尾部的目录 URL。
  • resolveLocalFileSystemURL 方法要求传入的 url 具有 filesystem 前缀。例如,resolveLocalFileSystemURLurl 参数应采用 filesystem:file:///persistent/somefile.txt 的形式,而不是 Android 中的 file:///persistent/somefile.txt 形式。
  • 不推荐使用的 toNativeURL 函数不受支持,并且没有存根。
  • 规范中未声明 setMetadata 函数,并且不受支持。
  • 在请求不存在的文件系统时,会抛出 INVALID_MODIFICATION_ERR(代码:9)而不是 SYNTAX_ERR(代码:8)。
  • 在尝试独占创建已存在的文件或目录时,会抛出 INVALID_MODIFICATION_ERR(代码:9)而不是 PATH_EXISTS_ERR(代码:12)。
  • 在尝试对根文件系统调用 removeRecursively 时,会抛出 INVALID_MODIFICATION_ERR(代码:9)而不是 NO_MODIFICATION_ALLOWED_ERR(代码:6)。
  • 在尝试 moveTo 不存在的目录时,会抛出 INVALID_MODIFICATION_ERR(代码:9)而不是 NOT_FOUND_ERR(代码:1)。

基于 IndexedDB 的实现怪癖(Firefox 和 IE)

  • 不支持 ...
  • IE 不支持 file:/// 模式;仅支持托管模式 (https://127.0.0.1:xxxx)。
  • Firefox 文件系统大小不受限制,但每次扩展 50MB 都会请求用户允许。IE10 允许在不提示的情况下使用最多 10MB 的组合 AppCache 和 IndexedDB 用于文件系统的实现,一旦达到该级别,系统会询问你是否要允许将其增加到每个站点最多 250MB。因此,requestFileSystem 函数的 size 参数不会影响 Firefox 和 IE 中的文件系统。
  • 规范中未声明 readAsBinaryString 函数,并且 IE 中不支持,也没有存根。
  • file.type 始终为 null。
  • 你不应使用已删除的 DirectoryEntry 实例回调结果创建条目。否则,你会得到一个“挂起的条目”。
  • 在你可以读取刚刚写入的文件之前,你需要获取此文件的新实例。
  • 规范中未声明的 setMetadata 函数仅支持 modificationTime 字段更改。
  • copyTomoveTo 函数不支持目录。
  • 不支持目录元数据。
  • 当删除非空目录时,Entry.remove 和 directoryEntry.removeRecursively 都不会失败 - 将清除要删除的目录及其内容。
  • 不支持 aborttruncate 函数。
  • 不会触发进度事件。例如,此处理程序将不会执行
    writer.onprogress = function() { /*commands*/ };
    

升级说明

在此插件的 v1.0.0 版本中,FileEntryDirectoryEntry 结构已更改,以更符合已发布的规范。

该插件的先前版本(1.0.0 之前)将设备绝对文件位置存储在 Entry 对象的 fullPath 属性中。这些路径通常看起来像

/var/mobile/Applications/<application UUID>/Documents/path/to/file  (iOS)
/storage/emulated/0/path/to/file                                    (Android)

这些路径也由 Entry 对象的 toURL() 方法返回。

在 v1.0.0 版本中,fullPath 属性是文件的路径,相对于 HTML 文件系统的根目录。因此,上述路径现在都将由一个 FileEntry 对象表示,其 fullPath

/path/to/file

如果您的应用程序使用设备绝对路径,并且您之前通过 Entry 对象的 fullPath 属性检索这些路径,则应更新您的代码以使用 entry.toURL() 代替。

为了向后兼容,resolveLocalFileSystemURL() 方法将接受设备绝对路径,并且只要该文件存在于 TEMPORARYPERSISTENT 文件系统中,就会返回一个与之对应的 Entry 对象。

这在使用 File-Transfer 插件时尤其是一个问题,该插件之前使用设备绝对路径(并且仍然可以接受它们)。它已更新为可以正确使用文件系统 URL,因此将 entry.fullPath 替换为 entry.toURL() 应该可以解决该插件在设备上使用文件时遇到的任何问题。

在 v1.1.0 中,toURL() 的返回值已更改(请参阅 CB-6394),尽可能返回一个绝对的“file://”URL。为了确保获得 'cdvfile:'-URL,您现在可以使用 toInternalURL()。此方法现在将返回以下形式的文件系统 URL:

cdvfile://127.0.0.1/persistent/path/to/file

它可用于唯一标识文件。

在 v7.0.0 中,当应用程序内容从 file:// 方案提供时,Android 的 toURL() 返回值已更新为返回绝对的 file:// URL。

如果应用程序内容从 http(s):// 方案提供,则将返回 cdvfile 格式的 URL。cdvfile 格式的 URL 是从内部方法 toInternalURL() 创建的。

一个 toInternalURL() 返回的文件系统 URL 示例

https://127.0.0.1/persistent/path/to/file

toURL flow

建议始终使用 toURL() 以确保返回正确的 URL。

cdvfile 协议

  • Android 上不支持

用途

cdvfile://127.0.0.1/persistent|temporary|another-fs-root*/path/to/file 可用于平台无关的文件路径。核心插件支持 cdvfile 路径 - 例如,您可以通过 cordova-plugin-file-transfer 将 mp3 文件下载到 cdvfile 路径,并通过 cordova-plugin-media 播放它。

*注意: 有关可用文件系统根目录的更多详细信息,请参阅存储文件的位置文件系统布局配置插件

要将 cdvfile 用作标签的 src,您可以通过已解析的 fileEntry 的 toURL() 方法将其转换为本机路径,您可以通过 resolveLocalFileSystemURL 获取 fileEntry - 请参阅下面的示例。

您还可以在 DOM 中直接使用 cdvfile:// 路径,例如

<img src="cdvfile://127.0.0.1/persistent/img/logo.png" />

注意: 此方法需要更新以下内容安全规则

  • cdvfile: 方案添加到索引页面的 Content-Security-Policy meta 标签中,例如
    • <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: cdvfile: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
  • <access origin="cdvfile://*" /> 添加到 config.xml

将 cdvfile:// 转换为本机路径

resolveLocalFileSystemURL('cdvfile://127.0.0.1/temporary/path/to/file.mp4', function(entry) {
    var nativePath = entry.toURL();
    console.log('Native URI: ' + nativePath);
    document.getElementById('video').src = nativePath;

将本机路径转换为 cdvfile://

resolveLocalFileSystemURL(nativePath, function(entry) {
    console.log('cdvfile URI: ' + entry.toInternalURL());

在核心插件中使用 cdvfile

fileTransfer.download(uri, 'cdvfile://127.0.0.1/temporary/path/to/file.mp3', function (entry) { ...
var my_media = new Media('cdvfile://127.0.0.1/temporary/path/to/file.mp3', ...);
my_media.play();

cdvfile 的怪癖

  • 在 Windows 平台上不支持在 DOM 中使用 cdvfile:// 路径(可以将其转换为本机路径)。

错误代码和含义列表

发生错误时,将使用以下代码之一。

代码 常量
1 NOT_FOUND_ERR
2 SECURITY_ERR
3 ABORT_ERR
4 NOT_READABLE_ERR
5 ENCODING_ERR
6 NO_MODIFICATION_ALLOWED_ERR
7 INVALID_STATE_ERR
8 SYNTAX_ERR
9 INVALID_MODIFICATION_ERR
10 QUOTA_EXCEEDED_ERR
11 TYPE_MISMATCH_ERR
12 PATH_EXISTS_ERR

配置插件(可选)

可以按平台配置可用文件系统的集合。iOS 和 Android 都识别 config.xml 中的标签,该标签指定要安装的文件系统。默认情况下,启用所有文件系统根目录。

<preference name="iosExtraFilesystems" value="library,library-nosync,documents,documents-nosync,cache,bundle,root" />
<preference name="AndroidExtraFilesystems" value="files,files-external,documents,sdcard,cache,cache-external,assets,root" />

Android

  • files: 应用程序的内部文件存储目录
  • files-external: 应用程序的外部文件存储目录
  • sdcard: 全局外部文件存储目录(如果安装了 SD 卡,则这是 SD 卡的根目录)。您必须拥有 android.permission.WRITE_EXTERNAL_STORAGE 权限才能使用此目录。
  • cache: 应用程序的内部缓存目录
  • cache-external: 应用程序的外部缓存目录
  • assets: 应用程序的捆绑包(只读)
  • root: 整个设备文件系统
  • applicationDirectory: 只读且访问受限。可以在此目录中复制文件,但直接读取会导致“找不到文件”。Android 还支持一个名为“documents”的特殊文件系统,它表示“files”文件系统中的“/Documents/”子目录。

iOS

  • library: 应用程序的库目录
  • documents: 应用程序的文档目录
  • cache: 应用程序的缓存目录
  • bundle: 应用程序的捆绑包;应用程序本身在磁盘上的位置(只读)
  • root: 整个设备文件系统

默认情况下,库和文档目录可以同步到 iCloud。您还可以请求另外两个文件系统,library-nosyncdocuments-nosync,它们表示 /Library/Documents 文件系统中的特殊非同步目录。

示例:创建文件和目录、写入、读取和附加文件

File 插件允许您执行以下操作:将文件存储在应用程序的临时或持久存储位置(沙盒存储)中,以及将文件存储在其他平台相关的位置中。本节中的代码片段演示了不同的任务,包括

创建一个持久文件

在使用 File 插件 API 之前,您可以使用 requestFileSystem 访问文件系统。执行此操作时,您可以请求持久或临时存储。除非用户授予权限,否则不会删除持久存储。

当您使用 requestFileSystem 获取文件系统访问权限时,仅授予沙盒文件系统的访问权限(沙盒限制对应用程序本身的访问权限),而不是对设备上任何文件系统位置的常规访问权限。(要访问沙盒存储之外的文件系统位置,请使用其他方法,例如 window.resolveLocalFileSystemURL,它支持特定于平台的位置。有关此示例,请参阅附加文件。)

这是对持久存储的请求。

注意 当面向 WebView 客户端(而不是浏览器)或原生应用程序 (Windows) 时,您无需在使用持久存储之前使用 requestQuota

window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fs) {

    console.log('file system open: ' + fs.name);
    fs.root.getFile("newPersistentFile.txt", { create: true, exclusive: false }, function (fileEntry) {

        console.log("fileEntry is file?" + fileEntry.isFile.toString());
        // fileEntry.name == 'someFile.txt'
        // fileEntry.fullPath == '/someFile.txt'
        writeFile(fileEntry, null);

    }, onErrorCreateFile);

}, onErrorLoadFs);

成功回调接收 FileSystem 对象 (fs)。使用 fs.root 返回 DirectoryEntry 对象,您可以使用它来创建或获取文件(通过调用 getFile)。在此示例中,fs.root 是一个 DirectoryEntry 对象,表示沙盒文件系统中的持久存储。

getFile 的成功回调接收 FileEntry 对象。您可以使用它来执行文件写入和文件读取操作。

创建一个临时文件

这是一个请求临时存储的示例。如果设备的内存不足,操作系统可能会删除临时存储。

window.requestFileSystem(window.TEMPORARY, 5 * 1024 * 1024, function (fs) {

    console.log('file system open: ' + fs.name);
    createFile(fs.root, "newTempFile.txt", false);

}, onErrorLoadFs);

当您使用临时存储时,可以通过调用 getFile 来创建或获取文件。与持久存储示例一样,这将为您提供一个 FileEntry 对象,您可以将其用于读取或写入操作。

function createFile(dirEntry, fileName, isAppend) {
    // Creates a new file or returns the file if it already exists.
    dirEntry.getFile(fileName, {create: true, exclusive: false}, function(fileEntry) {

        writeFile(fileEntry, null, isAppend);

    }, onErrorCreateFile);

}

写入文件

获得 FileEntry 对象后,可以通过调用 createWriter 将文件写入该文件,这会在成功回调中返回一个 FileWriter 对象。调用 FileWriter 的 write 方法以写入文件。

function writeFile(fileEntry, dataObj) {
    // Create a FileWriter object for our FileEntry (log.txt).
    fileEntry.createWriter(function (fileWriter) {

        fileWriter.onwriteend = function() {
            console.log("Successful file write...");
            readFile(fileEntry);
        };

        fileWriter.onerror = function (e) {
            console.log("Failed file write: " + e.toString());
        };

        // If data object is not passed in,
        // create a new Blob instead.
        if (!dataObj) {
            dataObj = new Blob(['some file data'], { type: 'text/plain' });
        }

        fileWriter.write(dataObj);
    });
}

读取文件

您还需要一个 FileEntry 对象来读取现有文件。使用 FileEntry 的 file 属性来获取文件引用,然后创建一个新的 FileReader 对象。您可以使用诸如 readAsText 之类的方法来启动读取操作。读取操作完成后,this.result 将存储读取操作的结果。

function readFile(fileEntry) {

    fileEntry.file(function (file) {
        var reader = new FileReader();

        reader.onloadend = function() {
            console.log("Successful file read: " + this.result);
            displayFileData(fileEntry.fullPath + ": " + this.result);
        };

        reader.readAsText(file);

    }, onErrorReadFile);
}

使用其他方法附加文件

当然,您通常希望附加现有文件而不是创建新文件。这是一个示例。此示例显示了您可以使用 window.resolveLocalFileSystemURL 访问文件系统的另一种方式。在此示例中,将跨平台的 Cordova 文件 URL (cordova.file.dataDirectory) 传递给该函数。成功回调接收一个 DirectoryEntry 对象,您可以使用它来执行诸如创建文件之类的操作。

window.resolveLocalFileSystemURL(cordova.file.dataDirectory, function (dirEntry) {
    console.log('file system open: ' + dirEntry.name);
    var isAppend = true;
    createFile(dirEntry, "fileToAppend.txt", isAppend);
}, onErrorLoadFs);

除了这种用法之外,您还可以使用 resolveLocalFileSystemURL 来访问不属于沙盒存储系统的某些文件系统位置。有关更多信息,请参阅存储文件的位置;其中许多存储位置特定于平台。您还可以使用 cdvfile 协议将跨平台文件系统位置传递给 resolveLocalFileSystemURL

对于附加操作,在前面代码中调用的 createFile 函数中没有任何新内容(有关实际代码,请参阅前面的示例)。createFile 调用 writeFile。在 writeFile 中,您检查是否请求了附加操作。

获得 FileWriter 对象后,调用 seek 方法,并传入您要写入的位置的索引值。在此示例中,您还要测试该文件是否存在。在调用 seek 后,调用 FileWriter 的 write 方法。

function writeFile(fileEntry, dataObj, isAppend) {
    // Create a FileWriter object for our FileEntry (log.txt).
    fileEntry.createWriter(function (fileWriter) {

        fileWriter.onwriteend = function() {
            console.log("Successful file read...");
            readFile(fileEntry);
        };

        fileWriter.onerror = function (e) {
            console.log("Failed file read: " + e.toString());
        };

        // If we are appending data to file, go to the end of the file.
        if (isAppend) {
            try {
                fileWriter.seek(fileWriter.length);
            }
            catch (e) {
                console.log("file doesn't exist!");
            }
        }
        fileWriter.write(dataObj);
    });
}

存储现有二进制文件

我们已经展示了如何写入您刚刚在沙盒文件系统中创建的文件。如果您需要访问现有文件并将其转换为可以存储在设备上的内容,该怎么办?在此示例中,您使用 xhr 请求获取文件,然后将其保存到沙盒文件系统中的缓存中。

在获取文件之前,请使用 requestFileSystem 获取 FileSystem 引用。通过在方法调用中传递 window.TEMPORARY(与之前相同),返回的 FileSystem 对象 (fs) 表示沙盒文件系统中的缓存。使用 fs.root 获取您需要的 DirectoryEntry 对象。

window.requestFileSystem(window.TEMPORARY, 5 * 1024 * 1024, function (fs) {

    console.log('file system open: ' + fs.name);
    getSampleFile(fs.root);

}, onErrorLoadFs);

为了完整起见,这里是获取 Blob 图像的 xhr 请求。此代码中没有任何特定于 Cordova 的内容,只是您将已获取的 DirectoryEntry 引用作为参数转发给 saveFile 函数。您稍后将在读取文件后保存 Blob 图像并显示它(以验证操作)。

function getSampleFile(dirEntry) {

    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://cordova.net.cn/static/img/cordova_bot.png', true);
    xhr.responseType = 'blob';

    xhr.onload = function() {
        if (this.status == 200) {

            var blob = new Blob([this.response], { type: 'image/png' });
            saveFile(dirEntry, blob, "downloadedImage.png");
        }
    };
    xhr.send();
}

注意 对于 Cordova 5 安全性,前面的代码要求您将域名 https://cordova.net.cn 添加到 index.html 中的 Content-Security-Policy元素中。

获取文件后,将内容复制到新文件中。当前 DirectoryEntry 对象已经与应用程序缓存关联。

function saveFile(dirEntry, fileData, fileName) {

    dirEntry.getFile(fileName, { create: true, exclusive: false }, function (fileEntry) {

        writeFile(fileEntry, fileData);

    }, onErrorCreateFile);
}

在 writeFile 中,您将 Blob 对象作为 dataObj 传递,并将其保存在新文件中。

function writeFile(fileEntry, dataObj, isAppend) {

    // Create a FileWriter object for our FileEntry (log.txt).
    fileEntry.createWriter(function (fileWriter) {

        fileWriter.onwriteend = function() {
            console.log("Successful file write...");
            if (dataObj.type == "image/png") {
                readBinaryFile(fileEntry);
            }
            else {
                readFile(fileEntry);
            }
        };

        fileWriter.onerror = function(e) {
            console.log("Failed file write: " + e.toString());
        };

        fileWriter.write(dataObj);
    });
}

写入文件后,读取并显示它。您已将图像保存为二进制数据,因此可以使用 FileReader.readAsArrayBuffer 读取它。

function readBinaryFile(fileEntry) {

    fileEntry.file(function (file) {
        var reader = new FileReader();

        reader.onloadend = function() {

            console.log("Successful file write: " + this.result);
            displayFileData(fileEntry.fullPath + ": " + this.result);

            var blob = new Blob([new Uint8Array(this.result)], { type: "image/png" });
            displayImage(blob);
        };

        reader.readAsArrayBuffer(file);

    }, onErrorReadFile);
}

读取数据后,可以使用如下代码显示图像。使用 window.URL.createObjectURL 获取 Blob 图像的 DOM 字符串。

function displayImage(blob) {

    // Displays image if result is a valid DOM string for an image.
    var elem = document.getElementById('imageFile');
    // Note: Use window.URL.revokeObjectURL when finished with image.
    elem.src = window.URL.createObjectURL(blob);
}

显示图像文件

要使用 FileEntry 显示图像,您可以调用 toURL 方法。

function displayImageByFileURL(fileEntry) {
    var elem = document.getElementById('imageFile');
    elem.src = fileEntry.toURL();
}

如果您使用的是一些平台特定的 URI 而不是 FileEntry,并且您想显示图像,则可能需要在 index.html 的 Content-Security-Policy元素中包含 URI 的主要部分。例如,在 Windows 10 上,您可以在您的元素中包含 ms-appdata:。这是一个示例。

<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: ms-appdata: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">

创建目录

在此处的代码中,您将在应用程序存储位置的根目录中创建目录。您可以将此代码用于任何可写入的存储位置(即任何 DirectoryEntry)。在这里,您通过将 fs.root 传递给此函数来写入应用程序缓存(假设您使用 window.TEMPORARY 来获取 FileSystem 对象)。

此代码在应用程序缓存中创建 /NewDirInRoot/images 文件夹。对于平台特定的值,请查看文件系统布局

function createDirectory(rootDirEntry) {
    rootDirEntry.getDirectory('NewDirInRoot', { create: true }, function (dirEntry) {
        dirEntry.getDirectory('images', { create: true }, function (subDirEntry) {

            createFile(subDirEntry, "fileInNewSubDir.txt");

        }, onErrorGetDir);
    }, onErrorGetDir);
}

创建子文件夹时,您需要像前面的代码中所示的那样单独创建每个文件夹。