Home > Article > Web Front-end > Detailed explanation of code cases of HTML5 file operation API
I often think that it would be very convenient if network applications could read and write files and directories. After moving from offline to online, applications have become more complex, and the lack of API in the file system has always hindered the progress of the network. Storing or interacting with binary data should not be limited to the desktop. What is gratifying is that due to the emergence of FileSystemAPI, this status quo has finally been changed. With FileSystemAPI, web applications can create, read, navigate, and write data to sandboxed portions of the user's local file system.
API is divided into the following different topics:
Reading and processing files: File
/ Blob
, FileList
, FileReader
Create and write: BlobBuilder
, FileWriter
DirectoryReader, FileEntry
/
DirectoryEntry, LocalFileSystem
GoogleChrome browser can implement this FileSystemAPI . There is currently no dedicated browser user interface for file / quota management. To store data on the user's system, your app may need to request quotas. However, you can use the --unlimited-quota-for-files flag to run the Chrome browser for testing. Additionally, if you are developing an app or extension for the Chrome Web Store, you can use unlimitedStorage
manifest file permissions without requesting quotas. Finally, the user receives a permission dialog box to grant, deny, or add storage to the app.
file://, you may need --allow-file-access-from-files
mark. Failure to use these tags results in SECURITY_ERR
or QUOTA_EXCEEDED_ERR
FileError
.
// Note: The file system has been prefixed as of Google Chrome 12: window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem; window.requestFileSystem(type, size, successCallback, opt_errorCallback)typeFile Whether the storage should be durable. Possible values include window.TEMPORARY and window.PERSISTENT. Data stored via TEMPORARY may be deleted at the browser's discretion (e.g. if more space is required). Clearing PERSISTENT storage requires explicit authorization from the user or app, and requires the user to grant a quota to your app. See Request Quotas. sizeThe size in bytes that the application requires for storage. successCallbackCallback called when the file system request is successful. Its parameter is a FileSystem object. opt_errorCallbackAn optional callback used to handle errors or when the request to obtain the file system is rejected. Its parameter is a FileError object. If you call requestFileSystem() for the first time, the system will create new storage for your application. Note that this is a sandboxed file system, which means that one web app cannot access another app's files. This also means that you cannot read/write files in arbitrary folders on the user's hard drive (e.g. My Pictures, My Documents, etc.). Usage example:
function onInitFs(fs) { console.log('Opened file system: ' + fs.name);} window.requestFileSystem(window.TEMPORARY, 5*1024*1024 /*5MB*/, onInitFs, errorHandler);FileSystem The specification also defines the synchronization API (LocalFileSystemSync) interface planned for WebWorkers. However, this tutorial does not cover the synchronization API. In the rest of this document, we will use the same handler to handle errors raised by asynchronous calls:
function errorHandler(e) { var msg = ''; switch (e.code) { case FileError.QUOTA_EXCEEDED_ERR: msg = 'QUOTA_EXCEEDED_ERR'; break; case FileError.NOT_FOUND_ERR: msg = 'NOT_FOUND_ERR'; break; case FileError.SECURITY_ERR: msg = 'SECURITY_ERR'; break; case FileError.INVALID_MODIFICATION_ERR: msg = 'INVALID_MODIFICATION_ERR'; break; case FileError.INVALID_STATE_ERR: msg = 'INVALID_STATE_ERR'; break; default: msg = 'Unknown Error'; break; }; console.log('Error: ' + msg); }
解的讯息。请求存储配额要使用 PERSISTENT 存储,您必须向用户取得存储持久数据的许可。由于浏览器可自行决定删除临时存储的数据,因此这一限制不适用于 TEMPORARY 存储。为了将 PERSISTENT 存储与 FileSystem API 配合使用,Chrome 浏览器使用基于 window.webkitStorageInfo 的新 API 以请求存储:
window.webkitStorageInfo.requestQuota(PERSISTENT, 1024*1024, function(grantedBytes) { window.requestFileSystem(PERSISTENT, grantedBytes, onInitFs, errorHandler); }, function(e) { console.log('Error', e); });
用户授予许可后,就不必再调用 requestQuota() 了。后续调用为无操作指令。您还可以使用 API 查询源的当前配额使用情况和分配情况:window.webkitStorageInfo.queryUsageAndQuota()使用文件沙盒环境中的文件通过 FileEntry 接口表示。FileEntry 包含标准文件系统中会有的属性类型(name、isFile...)和方法(remove、moveTo、copyTo...)。FileEntry 的属性和方法:
fileEntry.isFile === true fileEntry.isDirectory === false fileEntry.name fileEntry.fullPath ... fileEntry.getMetadata(successCallback, opt_errorCallback); fileEntry.remove(successCallback, opt_errorCallback); fileEntry.moveTo(dirEntry, opt_newName, opt_successCallback, opt_errorCallback); fileEntry.copyTo(dirEntry, opt_newName, opt_successCallback, opt_errorCallback); fileEntry.getParent(successCallback, opt_errorCallback); fileEntry.toURL(opt_mimeType); fileEntry.file(successCallback, opt_errorCallback); fileEntry.createWriter(successCallback, opt_errorCallback); ...
为了更好地理解 FileEntry,本部分还提供了执行常规任务的众多技巧。创建文件您可以使用文件系统的 getFile()(DirectoryEntry 接口的一种方法)查找或创建文件。请求文件系统后,系统会向成功回调传递FileSystem 对象,其中包含指向该应用相应文件系统的根的 DirectoryEntry (fs.root)。以下代码会在该应用相应文件系统的根中创建名为“log.txt”的空白文件:
function onInitFs(fs) { fs.root.getFile('log.txt', {create: true, exclusive: true}, function(fileEntry) { // fileEntry.isFile === true // fileEntry.name == 'log.txt' // fileEntry.fullPath == '/log.txt' }, errorHandler); } window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler);
请求文件系统后,系统会向成功处理程序传递 FileSystem 对象。我们可以将回调中的 fs.root.getFile() 命名为要创建的文件的文件名。您可以传递绝对路径或相对路径,但该路径必须有效。例如,如果您尝试创建一个其直接父级文件不存在的文件,将会导致出错。getFile() 的第二个参数是在文件不存在时从字面上说明函数行为的对象。在此示例中,create: true 会在文件不存在时创建文件,并在文件存在时 (exclusive: true) 引发错误。如果 create: false,系统只会获取并返回文件。无论是哪种情况,系统都不会覆盖文件内容,因为我们只是获取相关文件的引用路径。通过名称读取文件以下代码会检索名为“log.txt”的文件,并使用 FileReader API 读取文件内容,然后将其附加到页面上新的 4750256ae76b6b9d804861d8f69e79d3。如果 log.txt 不存在,系统将引发错误。
function onInitFs(fs) { fs.root.getFile('log.txt', {}, function(fileEntry) { // Get a File object representing the file, // then use FileReader to read its contents. fileEntry.file(function(file) { var reader = new FileReader(); reader.onloadend = function(e) { var txtArea = document.createElement('textarea'); txtArea.value = this.result; document.body.appendChild(txtArea); }; reader.readAsText(file); }, errorHandler); }, errorHandler); } window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler);
写入到文件以下代码会创建名为“log.txt”的空白文件(如果该文件不存在),并在文件中填入“Lorem Ipsum”文字。
function onInitFs(fs) { fs.root.getFile('log.txt', {create: true}, function(fileEntry) { // Create a FileWriter object for our FileEntry (log.txt). fileEntry.createWriter(function(fileWriter) { fileWriter.onwriteend = function(e) { console.log('Write completed.'); }; fileWriter.onerror = function(e) { console.log('Write failed: ' + e.toString()); }; // Create a new Blob and write it to log.txt. var bb = new BlobBuilder(); // Note: window.WebKitBlobBuilder in Chrome 12. bb.append('Lorem Ipsum'); fileWriter.write(bb.getBlob('text/plain')); }, errorHandler); }, errorHandler); } window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler);
此时,我们会调用 FileEntry 的 createWriter() 方法获取 FileWriter 对象。在成功回调中为error 事件和 writeend 事件设置事件处理程序。通过以下操作将文字数据写入文件:创建 Blob,向 Blob 附加文字,然后将 Blob 传递到FileWriter.write()。向文件附加文字以下代码会将“Hello World”文字附加到日志文件结尾。如果该文件不存在,系统将引发错误。
function onInitFs(fs) { fs.root.getFile('log.txt', {create: false}, function(fileEntry) { // Create a FileWriter object for our FileEntry (log.txt). fileEntry.createWriter(function(fileWriter) { fileWriter.seek(fileWriter.length); // Start write position at EOF. // Create a new Blob and write it to log.txt. var bb = new BlobBuilder(); // Note: window.WebKitBlobBuilder in Chrome 12. bb.append('Hello World'); fileWriter.write(bb.getBlob('text/plain')); }, errorHandler); }, errorHandler); } window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler);
以下代码可让用户使用 724acc485e2bb01e9bb3d8b9d88e9aff
选择多个文件,并在应用的沙盒文件系统中复制这些文件。
<input type="file" id="myfile" multiple /> document.querySelector('#myfile').onchange = function(e) { var files = this.files; window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) { // Duplicate each file the user selected to the app's fs. for (var i = 0, file; file = files[i]; ++i) { // Capture current iteration's file in local scope for the getFile() callback. (function(f) { fs.root.getFile(file.name, {create: true, exclusive: true}, function(fileEntry) { fileEntry.createWriter(function(fileWriter) { fileWriter.write(f); // Note: write() can take a File or Blob object. }, errorHandler); }, errorHandler); })(file); } }, errorHandler); };
虽然我们通过输入导入文件,您也可以使用 HTML5 拖放功能轻松实现相同的目标。
正如评论中所说的,FileWriter.write() 可接受 Blob 或 File。这是因为 File 继承自 Blob,所以文件对象也是 Blob。
以下代码会删除“log.txt”文件。
window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) { fs.root.getFile('log.txt', {create: false}, function(fileEntry) { fileEntry.remove(function() { console.log('File removed.'); }, errorHandler); }, errorHandler); }, errorHandler);
沙盒中的目录通过 DirectoryEntry
接口表示,该接口共享了 FileEntry 的大部分属性(继承自常用 Entry
接口)。不过,DirectoryEntry
还可使用其他方法处理目录。
DirectoryEntry
的属性和方法:
dirEntry.isDirectory === true // See the section on FileEntry for other inherited properties/methods. ... var dirReader = dirEntry.createReader(); dirEntry.getFile(path, opt_flags, opt_successCallback, opt_errorCallback); dirEntry.getDirectory(path, opt_flags, opt_successCallback, opt_errorCallback); dirEntry.removeRecursively(successCallback, opt_errorCallback); ...
使用 DirectoryEntry
的 getDirectory()
方法读取或创建目录。您可以递交名称或路径作为查找或创建所用的目录。
例如,以下代码会在根目录中创建名为“MyPictures”的目录:
[html] view plaincopy window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) { fs.root.getDirectory('MyPictures', {create: true}, function(dirEntry) { ... }, errorHandler); }, errorHandler);
创建子目录的方法与创建其他任何目录的方法完全相同。不过,如果您尝试创建其直接父目录不存在的目录,API 将引发错误。相应的解决方法是,依次创建各级目录,而这对异步 API 而言非常麻烦。
以下代码会在系统创建父文件夹后以递归方式添加各个子文件夹,从而在应用相应 FileSystem 的根中创建新的层次结构 (music/genres/jazz)。
[html] view plaincopy var path = 'music/genres/jazz/'; function createDir(rootDirEntry, folders) { // Throw out './' or '/' and move on to prevent something like '/foo/.//bar'. if (folders[0] == '.' || folders[0] == '') { folders = folders.slice(1); } rootDirEntry.getDirectory(folders[0], {create: true}, function(dirEntry) { // Recursively add the new subfolder (if we still have another to create). if (folders.length) { createDir(dirEntry, folders.slice(1)); } }, errorHandler); }; function onInitFs(fs) { createDir(fs.root, path.split('/')); // fs.root is a DirectoryEntry. } window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler);
在“music/genres/jazz”处于合适的位置后,我们就可以将完整路径传递到 getDirectory()
,然后在其下方创建新的子文件夹。例如:
window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) { fs.root.getFile('/music/genres/jazz/song.mp3', {create: true}, function(fileEntry) { ... }, errorHandler); }, errorHandler);
要读取目录的内容,可先创建 DirectoryReader
,然后调用 readEntries()
方法。我们不能保证所有目录条目都能在仅调用一次 readEntries()
的情况下同时返回。也就是说,您需要一直调用 DirectoryReader.readEntries()
,直到系统不再返回结果为止。以下代码对此作了说明:
[html] view plaincopy <ul id="filelist"></ul> function toArray(list) { return Array.prototype.slice.call(list || [], 0); } function listResults(entries) { // Document fragments can improve performance since they're only appended // to the DOM once. Only one browser reflow occurs. var fragment = document.createDocumentFragment(); entries.forEach(function(entry, i) { var img = entry.isDirectory ? '<img src="folder-icon.gif">' : '<img src="file-icon.gif">'; var li = document.createElement('li'); li.innerHTML = [img, '<span>', entry.name, '</span>'].join(''); fragment.appendChild(li); }); document.querySelector('#filelist').appendChild(fragment); } function onInitFs(fs) { var dirReader = fs.root.createReader(); var entries = []; // Call the reader.readEntries() until no more results are returned. var readEntries = function() { dirReader.readEntries (function(results) { if (!results.length) { listResults(entries.sort()); } else { entries = entries.concat(toArray(results)); readEntries(); } }, errorHandler); }; readEntries(); // Start reading dirs. } window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler);
DirectoryEntry.remove()
方法的行为与 FileEntry
相应方法的行为非常相似。差别在于:尝试删除非空目录时会引发错误。
以下代码会从“/music/genres/”删除空的“jazz”目录:
[html] view plaincopy window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) { fs.root.getDirectory('music/genres/jazz', {}, function(dirEntry) { dirEntry.remove(function() { console.log('Directory removed.'); }, errorHandler); }, errorHandler); }, errorHandler);
如果您不需要某个包含条目的目录,不妨使用 removeRecursively()
。该方法将以递归方式删除目录及其内容。
以下代码会以递归方式删除“music”目录及其包含的所有文件和目录:
[html] view plaincopy window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) { fs.root.getDirectory('/misc/../music', {}, function(dirEntry) { dirEntry.removeRecursively(function() { console.log('Directory removed.'); }, errorHandler); }, errorHandler); }, errorHandler);
FileEntry
和 DirectoryEntry
享有共同的操作。
FileEntry
和 DirectoryEntry
均可使用 copyTo()
复制现有条目。该方法会自动以递归方式复制文件夹。
以下代码示例会将“me.png”文件从一个目录复制到另一个目录:
[html] view plaincopy function copy(cwd, src, dest) { cwd.getFile(src, {}, function(fileEntry) { cwd.getDirectory(dest, {}, function(dirEntry) { fileEntry.copyTo(dirEntry); }, errorHandler); }, errorHandler); } window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) { copy(fs.root, '/folder1/me.png', 'folder2/mypics/'); }, errorHandler);
FileEntry
和 DirectoryEntry
的 moveTo()
方法可让您移动或重命名文件或目录。其第一个参数是文件要移动到的目标父目录,其第二个参数是文件可选的新名称。如未提供新名称,系统将使用文件的原名称。
以下示例将“me.png”重命名为“you.png”,但并不移动该文件:
[html] view plaincopy function rename(cwd, src, newName) { cwd.getFile(src, {}, function(fileEntry) { fileEntry.moveTo(cwd, newName); }, errorHandler); } window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) { rename(fs.root, 'me.png', 'you.png'); }, errorHandler); 以下示例将“me.png”(位于根目录中)移动到名为“newfolder”的文件夹。 function move(src, dirName) { fs.root.getFile(src, {}, function(fileEntry) { fs.root.getDirectory(dirName, {}, function(dirEntry) { fileEntry.moveTo(dirEntry); }, errorHandler); }, errorHandler); } window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) { move('/me.png', 'newfolder/'); }, errorHandler);
FileSystem API 使用新的网址机制,(即 filesystem:),可用于填充 src 或 href 属性。例如,如果您要显示某幅图片且拥有相应的 fileEntry,您可以调用 toURL() 获取该文件的 filesystem: 网址:
var img = document.createElement('img'); img.src = fileEntry.toURL(); // filesystem:http://example.com/temporary/myfile.png document.body.appendChild(img);
另外,如果您已具备 filesystem: 网址,可使用 resolveLocalFileSystemURL() 找回 fileEntry:
window.resolveLocalFileSystemURL = window.resolveLocalFileSystemURL || window.webkitResolveLocalFileSystemURL; var url = 'filesystem:http://example.com/temporary/myfile.png'; window.resolveLocalFileSystemURL(url, function(fileEntry) { ... });
The above is the detailed content of Detailed explanation of code cases of HTML5 file operation API. For more information, please follow other related articles on the PHP Chinese website!