cordova-plugin-file
此插件实现了一个文件 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 文件系统规范已弃用用于 Web 浏览器,但文件系统 API 在 Cordova 应用程序中使用此插件得到支持,支持的平台列在支持的平台列表中,浏览器平台除外。
要了解如何使用此插件的一些想法,请查看此页面底部的示例。有关其他示例(以浏览器为中心),请参阅 HTML5 Rocks 的文件系统文章。
有关其他存储选项的概述,请参阅 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.readAsArrayBuffer
或 FileWriter.write(blob)
。
文件存储位置
从 v1.2.0 开始,提供了指向重要文件系统目录的 URL。每个 URL 的格式为file:///path/to/spot/,可以使用 window.resolveLocalFileSystemURL()
转换为 DirectoryEntry
。
-
cordova.file.applicationDirectory
- 应用程序安装的只读目录。(iOS、Android、BlackBerry 10、OSX、windows) -
cordova.file.applicationStorageDirectory
- 应用程序沙箱的根目录;在 iOS 和 windows 上,此位置为只读(但特定子目录 [例如 iOS 上的/Documents
或 windows 上的/localState
] 为读写)。其中包含的所有数据对应用程序私有。(iOS、Android、BlackBerry 10、OSX) -
cordova.file.dataDirectory
- 使用内部存储器在应用程序沙箱内进行持久且私有的数据存储(在 Android 上,如果您需要使用外部存储器,请使用.externalDataDirectory
)。在 iOS 上,此目录不会与 iCloud 同步(使用.syncedDataDirectory
)。(iOS、Android、BlackBerry 10、windows) -
cordova.file.cacheDirectory
- 用于缓存数据文件或应用程序可以轻松重新创建的任何文件的目录。当设备存储空间不足时,操作系统可能会删除这些文件,但应用程序不应依赖操作系统来删除此处的文件。(iOS、Android、BlackBerry 10、OSX、windows) -
cordova.file.externalApplicationStorageDirectory
- 外部存储器上的应用程序空间。(Android)。请参阅怪癖。 -
cordova.file.externalDataDirectory
- 在外部存储器上放置特定于应用程序的数据文件的位置。(Android)。请参阅怪癖。 -
cordova.file.externalCacheDirectory
- 外部存储器上的应用程序缓存。(Android)。请参阅怪癖。 -
cordova.file.externalRootDirectory
- 外部存储器(SD 卡)根目录。(Android、BlackBerry 10)。请参阅怪癖。 -
cordova.file.tempDirectory
- 操作系统可以随时清除的临时目录。不要依赖操作系统来清除此目录;您的应用程序应始终根据需要删除文件。(iOS、OSX、windows) -
cordova.file.syncedDataDirectory
- 保存应同步(例如,与 iCloud 同步)的特定于应用程序的文件。(iOS、windows) -
cordova.file.documentsDirectory
- 对应用程序私有的文件,但对其他应用程序有意义(例如,Office 文件)。请注意,对于OSX,这是用户的~/Documents
目录。(iOS、OSX) -
cordova.file.sharedDirectory
- 对所有应用程序全局可用的文件(BlackBerry 10)
文件系统布局
虽然从技术上讲是实现细节,但了解 cordova.file.*
属性如何映射到真实设备上的物理路径非常有用。
iOS 文件系统布局
设备路径 | cordova.file.* |
iosExtraFileSystems |
r/w? | 持久性? | 操作系统清除 | 同步 | 私有 |
---|---|---|---|---|---|---|---|
/var/mobile/Applications/<UUID>/ |
applicationStorageDirectory | - | r | N/A | N/A | N/A | 是 |
appname.app/ |
applicationDirectory | bundle | r | N/A | N/A | N/A | 是 |
www/ |
- | - | r | N/A | N/A | N/A | 是 |
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? | 持久性? | 操作系统清除 | 私有 |
---|---|---|---|---|---|---|
file:///android_asset/ |
applicationDirectory | assets | r | N/A | N/A | 是 |
/data/data/<app-id>/ |
applicationStorageDirectory | - | r/w | N/A | N/A | 是 |
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 的外部存储器怪癖
随着作用域存储的引入,通过文件 API 访问外部存储器不可靠或有限制。作用域存储是在 API 29 中引入的。虽然现有应用程序可能可以选择退出,但此选项不适用于新应用程序。在 Android API 30 及更高版本上,作用域存储完全强制执行。
此外,API 29 上不支持直接文件访问。这意味着此插件无法在 API 29 设备上访问外部存储介质。
API 30 引入了FUSE,它允许使用文件 API 对外部存储器进行有限访问,使此插件能够再次部分工作。
有限访问包括但不限于
- 具有适当
READ_EXTERNAL
或 READ_MEDIA_* 权限的只读访问。 - 只读访问仅限于媒体文件,不包括文档。
- 写入仅限于您的应用程序拥有的文件。通过文件 API 修改第三方应用程序拥有的文件(例如,通过相机插件创建的图像文件)是不可能的。
- 并非外部存储器中的所有路径都可写。
这些限制仅适用于外部文件系统(例如 cordova.file.external*
路径)。内部文件系统(例如 cordova.file.dataDirectory
路径)不受这些限制的影响。
如果您的应用程序需要与外部文件系统交互,请考虑使用MediaStore 插件。
OS X 文件系统布局
设备路径 | cordova.file.* |
iosExtraFileSystems |
r/w? | 操作系统清除 | 私有 |
---|---|---|---|---|---|
/Applications/<appname>.app/ |
- | bundle | r | N/A | 是 |
Content/Resources/ |
applicationDirectory | - | r | N/A | 是 |
~/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/
下。
* 文件在应用程序重启和升级后仍然存在,但此目录可以在操作系统需要时随时清除。您的应用程序应能够重新创建可能被删除的任何内容。您应根据应用程序的需要清除此目录。
** 允许访问整个文件系统。这仅适用于非沙箱应用程序。
Windows 文件系统布局
设备路径 | cordova.file.* |
r/w? | 持久性? | 操作系统清除 | 私有 |
---|---|---|---|---|---|
ms-appdata:/// |
applicationDirectory | r | N/A | N/A | 是 |
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 上列出资产目录非常慢。但是,您可以通过将src/android/build-extras.gradle
添加到 Android 项目的根目录来加快速度(还需要 [email protected] 或更高版本)。
在 Marshmallow 上未挂载外部存储时写入外部存储的权限
Marshmallow 要求应用程序在读写外部位置时请求权限。默认情况下,您的应用程序有权写入cordova.file.applicationStorageDirectory
和cordova.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.*
属性之一(只有applicationDirectory
和applicationStorageDirectory
是只读的)。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 作为基础。所有浏览器在路径中都使用正斜杠作为目录分隔符。
- 必须依次创建目录条目。例如,调用
fs.root.getDirectory('dir1/dir2', {create:true}, successCallback, errorCallback)
如果 dir1 不存在,将失败。 - 插件在应用程序首次启动时请求用户使用持久性存储的权限。
- 插件仅支持
cdvfile://localhost
(本地资源)。即,不支持通过cdvfile
访问外部资源。 - 插件不遵循"文件系统 API 8.3 命名限制"。
- Blob 和 File 的
close
函数不受支持。 FileSaver
和BlobBuilder
不受此插件支持,并且没有存根。- 插件不支持
requestAllFileSystems
。此函数在规范中也缺失。 - 如果您对现有目录使用
create: true
标志,则目录中的条目不会被删除。 - 不支持通过构造函数创建的文件。您应该使用 entry.file 方法。
- 每个浏览器都使用自己的形式来引用 blob URL。
- 支持
readAsDataURL
函数,但 Chrome 中的媒体类型取决于条目名称扩展名,IE 中的媒体类型始终为空(与规范中的text-plain
相同),Firefox 中的媒体类型始终为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://localhost/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.txt
、filesystem:http://localhost:8080/persistent/somefile.txt
。 toURL
函数结果在目录条目情况下不包含尾部斜杠。但是,Chrome 会正确解析带有斜杠结尾的 URL 的目录。resolveLocalFileSystemURL
方法要求传入的url
具有filesystem
前缀。例如,resolveLocalFileSystemURL
的url
参数应为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)。
- 尝试移动到不存在的目录时,会抛出 INVALID_MODIFICATION_ERR(代码:9),而不是 NOT_FOUND_ERR(代码:1)。
基于 IndexedDB 的实现特性(Firefox 和 IE)
- 不支持
.
和..
。 - IE 不支持
file:///
模式;仅支持托管模式(http://localhost:xxxx)。 - Firefox 文件系统大小不受限制,但每个 50 MB 的扩展程序都会请求用户权限。IE10 允许在实现文件系统时,AppCache 和 IndexedDB 的组合使用量最多为 10 MB,而不会提示,一旦达到该级别,系统会询问您是否允许将其增加到每个站点最多 250 MB。因此,
requestFileSystem
函数的size
参数不会影响 Firefox 和 IE 中的文件系统。 - 规范中未说明
readAsBinaryString
函数,IE 中也不支持,并且没有存根。 file.type
始终为 null。- 您不应该使用已删除的 DirectoryEntry 实例回调结果来创建条目。否则,您将获得一个“挂起条目”。
- 在读取刚刚写入的文件之前,您需要获取该文件的新的实例。
- 规范中未说明的
setMetadata
函数仅支持更改modificationTime
字段。 copyTo
和moveTo
函数不支持目录。- 不支持目录元数据。
- Entry.remove 和 directoryEntry.removeRecursively 在删除非空目录时不会失败 - 删除的目录会与其内容一起被清理。
- 不支持
abort
和truncate
函数。 - 不会触发进度事件。例如,此处理程序将不会执行
writer.onprogress = function() { /*commands*/ };
升级说明
在此插件的 v1.0.0 版本中,FileEntry
和DirectoryEntry
结构已更改,以更符合已发布的规范。
插件的早期版本(早于 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 文件系统根目录的路径。因此,上面的路径现在都将由具有fullPath
为的FileEntry
对象表示
/path/to/file
如果您的应用程序使用设备绝对路径,并且您以前通过Entry
对象的fullPath
属性检索了这些路径,那么您应该更新代码以使用entry.toURL()
。
为了向后兼容,resolveLocalFileSystemURL()
方法将接受设备绝对路径,并将返回与之对应的Entry
对象,只要该文件存在于TEMPORARY
或PERSISTENT
文件系统中。
这在 File-Transfer 插件中尤其是一个问题,该插件以前使用设备绝对路径(并且仍然可以接受它们)。它已更新为与文件系统 URL 正确配合使用,因此将entry.fullPath
替换为entry.toURL()
应该可以解决使该插件与设备上的文件配合使用时遇到的任何问题。
在 v1.1.0 中,toURL()
的返回值已更改(参见CB-6394),以尽可能返回绝对的“file://”URL。要确保“cdvfile:”URL,您现在可以使用toInternalURL()
。此方法现在将返回以下形式的文件系统 URL
cdvfile://localhost/persistent/path/to/file
这可用于唯一标识文件。
在 v7.0.0 中,Android 上 toURL()
的返回值已更新为在应用内容从 file://
方案提供时返回绝对 file://
URL。
如果应用内容从 http(s)://
方案提供,则将返回 cdvfile
格式的 URL。cdvfile
格式的 URL 是从内部方法 toInternalURL()
创建的。
一个示例 toInternalURL()
返回的文件系统 URL
https://localhost/persistent/path/to/file
建议始终使用 toURL()
以确保返回正确的 URL。
cdvfile 协议
- Android 上不支持
用途
cdvfile://localhost/persistent|temporary|another-fs-root*/path/to/file
可用于平台无关的文件路径。cdvfile 路径受核心插件支持 - 例如,您可以通过 cordova-plugin-file-transfer
将 mp3 文件下载到 cdvfile 路径,并通过 cordova-plugin-media
播放它。
*注意:有关可用 fs 根目录的更多详细信息,请参阅 文件存储位置、文件系统布局 和 配置插件。
要将 cdvfile
用作标签的 src
,您可以通过解析后的 fileEntry 的 toURL()
方法将其转换为本机路径,您可以通过 resolveLocalFileSystemURL
获取 - 请参阅下面的示例。
您也可以在 DOM 中直接使用 cdvfile://
路径,例如
<img src="cdvfile://localhost/persistent/img/logo.png" />
注意:此方法需要遵循以下内容安全规则更新
- 将
cdvfile:
方案添加到索引页面的Content-Security-Policy
元标签中,例如<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://localhost/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://localhost/temporary/path/to/file.mp3', function (entry) { ...
var my_media = new Media('cdvfile://localhost/temporary/path/to/file.mp3', ...);
my_media.play();
cdvfile 特性
- 在 DOM 中使用
cdvfile://
路径在 Windows 平台上不受支持(可以将路径转换为本机路径)。
错误代码和含义列表
抛出错误时,将使用以下代码之一。
代码 | 常量 |
---|---|
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 都识别一个
<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-nosync
和 documents-nosync
,它们表示 /Library
或 /Documents
文件系统中的特殊非同步目录。
示例:创建文件和目录、写入、读取和追加文件
文件插件允许您执行诸如将文件存储在应用程序的临时或持久存储位置(沙盒存储)以及将文件存储在其他平台相关位置等操作。本节中的代码片段演示了不同的任务,包括
创建持久文件
在使用文件插件 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);
为了完整起见,这里有一个 xhr 请求来获取 Blob 图像。此代码中没有 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 添加到 Content-Security-Policyindex.html 中的元素。
获取文件后,将内容复制到新文件。当前 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,并且想要显示图像,则可能需要在 Content-Security-Policy 中包含 URI 的主要部分在 index.html 中的元素。例如,在 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);
}
创建子文件夹时,您需要像前面的代码所示那样分别创建每个文件夹。