本文讲解cordova-plugin-file插件以及如何在Wex5中使用该插件。

目录:

1.插件概述
2.插件使用

1.插件概述


文件管理插件,提供了读写设备文件的API。

2.插件使用

<1>配置插件(可选)


文件系统设置可以配置每个平台,在xml文件中,Android和iOS的平台都识别文件系统安装的标识。默认情况下,可以访问所有的文件根目录,如果我们禁止访问某一目录,可以在下面的配置代码中删除该目录的标识(比如“cache”)。

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,root" ;

<2>文件存储位置

  • cordova.file.applicationDirectory:只读的应用程序的安装目录。 (iOS,Android,OSX, windows)
  • cordova.file.applicationStorageDirectory:应用程序的根目录的沙盒;在iOS和windows这个位置是只读的(但具体的子目录(iOS上/Documents或windows上/localState)读写)。这其中包含的所有文件都是应用私有的。 (iOS,Android,OSX)
  • cordova.file.dataDirectory:使用内存存储在应用程序的沙箱中,私有的、持久的(在Android上,如果你需要使用外部存储器,用.externalDataDirectory)。在iOS上,该目录是不与iCloud同步的。 (iOS,Android,windows)
  • cordova.file.cacheDirectory:存储缓存数据文件或者其他的可以轻松重建的文件的目录,操作系统会在运行比较慢时删除这些文件,然而应用程序不应该依赖于系统才能删除这些文件。 (iOS,Android, OSX, windows)
  • cordova.file.externalApplicationStorageDirectory:应用程序外部存储空间。 (Android)
  • cordova.file.externalDataDirectory:存储应用特定数据的外部存储空间。 (Android)
  • cordova.file.externalCacheDirectory:存储缓存数据的外部存储空间
  • cordova.file.externalRootDirectory:外部存储(SD卡)根目录 (Android)
  • cordova.file.tempDirectory:临时文件存储目录,系统可以删除此目录,应用程序可以在适当条件下删除此目录,并不依赖于文件系统。 (iOS, OSX, windows)
  • cordova.file.syncedDataDirectory:需要同步(比如iCloud)的应用特定数据 (iOS, windows)
  • cordova.file.documentsDirectory:应用的私有文件,但是对其他应用也有用(比如,办公文件)注意:对于OSX来说,指的是用户的~/Documents目录。 (iOS,OSX)
  • cordova.file.applicationDirectory:只读应用程序的安装目录
  • cordova.file.applicationDirectory:只读应用程序的安装目录

<3>系统文件列表

① iOS

Device Path cordova.file.* iosExtraFileSystems r/w? persistent? OS clears sync private
/var/mobile/Applications/<UUID>/ applicationStorageDirectory r N/A N/A N/A Yes
appname.app/ applicationDirectory bundle r N/A N/A N/A Yes
www/ r N/A N/A N/A Yes
Documents/ documentsDirectory documents r/w Yes No Yes Yes
NoCloud/ documents-nosync r/w Yes No No Yes
Library library r/w Yes No Yes? Yes
NoCloud/ dataDirectory library-nosync r/w Yes No No Yes
Cloud/ syncedDataDirectory r/w Yes No Yes Yes
Caches/ cacheDirectory cache r/w Yes* Yes*** No Yes
tmp/ tempDirectory r/w No** Yes*** No Yes

* 文件可以在应用重启还有升级过程中存留,但是系统可以随时清空该目录。你的应用应该能够重建任何可能被删除的内容。

** 文件在应用重启的时候可能会存留,但是并不要依赖于此。当应用更新的时候,文件并不能保证可以存留下来。你的应用应该在合适的时机将文件从此目录移除,因为系统随时有可能将它移除。

*** 系统会在必要的时候清空这个目录,但不要依赖于此。开发者应该在应用合适的时机清空该目录。

② Android

Device Path cordova.file.* AndroidExtraFileSystems r/w? persistent? OS clears private
file:///android_asset/ applicationDirectory r N/A N/A Yes
/data/data/<app-id>/ applicationStorageDirectory r/w N/A N/A Yes
cache cacheDirectory cache r/w Yes Yes* Yes
files dataDirectory files r/w Yes No Yes
Documents documents r/w Yes No Yes
<sdcard>/ externalRootDirectory sdcard r/w Yes No No
Android/data/<app-id>/ externalApplicationStorageDirectory r/w Yes No No
cache externalCacheDirectry cache-external r/w Yes No** No
files externalDataDirectory files-external r/w Yes No No

*系统会定期清理该目录,但不要依赖于此。在应用合适的时机应该自己清除它,如果用户手动清理缓存,该目录下的数据会被清除。

** 系统并不会自动清除该目录,必须自己管理其内容。如果,用户手动清理缓存,该目录下的数据会被清除。

注意:如果不可以安装外部存储,cordova.file.external*属性为空。

 

<4>Android注意事项

① Android持久存储位置

在Android设备上有多个可以保存持久化文件的目录,在此讨论多种可能性。

先前插件的版本会在应用启动时,根据设备是否安装SD卡来选择临时数据和持久数据的存储路径。如果安装了SD卡或者有较为宽裕的存储空间,则持久文件则会被存储到该空间的根目录。这意味着所有的应用均可以访问卡上所有的可用文件。

如果SD卡不可用的话,之前的版本将会把数据存储到“/data/data/<packageId>”目录下,应用间互相隔离,但也有可能导致数据被用户共享。

现在,我们可以通过应用中的xml文件中的设置来实现将文件存储到内部存储或者是继续使用先前的逻辑。xml文件中的配置如下:


preference name="AndroidPersistentFileLocation" value="Internal";

preference name="AndroidPersistentFileLocation" value="Compatibility" ;

没有这句代码,插件将会把应用内作为默认的存储路径。如果存在“preference”标签,但是取值并不是这两个中的一个,那么应用将会无法启动。

如果你的应用已经发布,用的是插件的3.0以下的版本,并且已经在文件系统中存储了持久化的文件,假如你的xml文件还没有指定持久化文件存储的配置信息,那么你应该将其设为“Compatibility”。将“Compatibility”转换为“Internal”意味着用户一旦更新应用很有可能无法访问之前的文件,当然,这取决于不同的设备。

如果您是新开发一个应用,或者您的应用并未存储持久化文件,那么我们推荐使用“Internal”作为默认的文件存储路径。

②Android 6.0

Android6.0中,当应用读写外部存储的时候需要请求许可。默认情况下,应用可以直接将文件写入“cordova.file.applicationStorageDirectory”和“cordova.file.externalApplicationStorageDirectory”,并不需要请求许可,除非外部存储器没有安装。当外部存储器没有安装的时候,将文件写入“cordova.file.externalApplicationStorageDirectory”的时候需要请求权限。

<5>iOS注意事项

  • “cordova.file.applicationStorageDirectory”是只读的,往根目录写文件的话会失败。请使用其他的cordova.file.*的属性。
  • FileReader.readAsText(blob, encoding)
    • 不支持编码参数,utf – 8编码有效。

在iOS设备上有两个路径可以保存持久化数据:Documents目录还有Library目录,之前版本的插件只是将文件存储到Documents目录这会导致无法再iTunes中显示所有的文件。现在的版本我们可以通过xml文件配置持久化文件的保存路径为Documents/Library,代码如下:

preference name="iosPersistentFileLocation" value="Library" 

preference name="iosPersistentFileLocation" value="Compatibility" 

没有这些设置,系统将会以“Compatibility”作为默认,如果存在“preference”标签,但是取值并不是这两个中的一个,那么应用将会无法启动。

<6>cdvfile协议

目标:

“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()”方法将它转换为本地路径,至于“fileEntry”对象,你可以使用“resolveLocalFileSystemURL”获得,具体看下面的例子:

也可以直接在DOM中使用“cdvfile://”路径,例如:

src="cdvfile://localhost/persistent/img/logo.png";

注意:这个方法需要有下面的安全规则:

  • Add cdvfile: scheme to Content-Security-Policy meta tag of the index page, e.g.:
    • <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 *">
  • Add <access origin="cdvfile://*" /> to 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://路径

</pre>
<pre><span class="pl-en">resolveLocalFileSystemURL</span>(nativePath, <span class="pl-k">function</span>(<span class="pl-smi">entry</span>) {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>cdvfile URI: <span class="pl-pds">'</span></span> <span class="pl-k">+</span> <span class="pl-smi">entry</span>.<span class="pl-en">toInternalURL</span>());</pre>
<pre>

在核心插件中使用“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();
 

<7>错误码


当有错误抛出时,我们就需要下面的列表:

Code Constant
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

 

<8>手机应用目录结构


在介绍插件API使用之前,我们首先需要了解一些关于iOS与Android应用下的目录结构。

### Android

  • files: 应用程序的内部文件存储目录
  • files-external: 应用程序的外部文件存储目录
  • sdcard: 外部全局文件存储目录(如果安装了SD卡,这是SD卡的根目录),必须有Android的“WRITE_EXTERNAL_STORAGE”许可,才可以使用该目录
  • cache: 应用程序的内部缓存目录
  • cache-external: 应用程序的外部缓存目录
  • root: 整个设备的文件系统

Android还支持一个特殊的名为“documents”的文件系统,它代表了“files”文件系统内“/Documents/”字目录

### iOS

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

默认情况下,库和文档目录是可以同步到iCloud的,你也可以请求两个额外的文件系统“library-nosync”和“documents-nosync”,代表“/Library”或者“/Documents”目录下的特殊的“non-synced”目录。

<9>示例代码


通过该插件,你可以存储应用文件到临时/持久化目录或者其他平台支持的位置。以下代码分别做不同的展示:

  • 访问文件系统
  • Using cross-platform Cordova file URLs to store your files (see Where to Store Files for more info)
  • 创建文件和路径
  • 写入文件
  • 读取文件
  • 追加文件
  • 展示图片文件

①创建持久化文件

在你使用插件API之前,你可以通过“requestFileSystem”来访问文件系统。在这之后,你可以请求保存临时文件或者持久化文件,持久化文件不会被删除,除非用户许可。

当你使用“requestFileSystem”访问文件系统的时候,访问权限仅限于沙箱文件系统,并不是设备上的任意文件。(当需要访问沙箱外的文件时,可以使用其他的方法,比如:“window.requestLocalFileSystemURL”,具体例子可以看追加文件)。

下面是持久化存储的请求:

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);

成功回调会接受一个文件系统对象(fs),使用“fs.root ”返回“DirectoryEntry”对象,你可以用它来创建或者获取一个文件。在这个简单的例子中,“fs.root”是一个沙箱中的持久化文件存储对象。

“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”的对象,你可以用它来实现读写操作。

③写入文件

一旦你有了“FileEntry”对象后,你可以调用“createWriter”来写文件,在成功回调中会返回一个“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 read...");
            readFile(fileEntry);
        };

        fileWriter.onerror = function (e) {
            console.log("Failed file read: " + 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”对象的文件属性来获取文件的引用,然后创建一个新的“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 ”来访问沙箱外的文件系统,可以参考文件存储,他们大多数都有平台的区别。

 

一旦有了“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);
    });
}

⑥展示图片文件

使用“FileEntry”显示一个图像,你可以调用“toURL”方法。

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

⑦创建目录

使用下面的代码,你可以在APP的根目录下创建目录,在任何可写的位置,都可以使用该代码。这个例子,在应用的缓存区创建了“/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);
}