Heim  >  Artikel  >  Web-Frontend  >  Detaillierte Erläuterung der Codefälle der HTML5-Dateioperations-API

Detaillierte Erläuterung der Codefälle der HTML5-Dateioperations-API

黄舟
黄舟Original
2018-05-26 10:14:064872Durchsuche

Einführung

Ich denke oft, dass es sehr praktisch wäre, wenn Webanwendungen Dateien und Verzeichnisse lesen und schreiben könnten. Nach der Umstellung von offline auf online sind Anwendungen komplexer geworden und das Fehlen einer API im Dateisystem behindert den Fortschritt des Netzwerks. Das Speichern oder Interagieren mit Binärdaten sollte nicht auf den Desktop beschränkt sein. Glücklicherweise hat sich diese Situation durch das Aufkommen von FileSystemAPI endlich geändert. Mit der FileSystemAPI können Webanwendungen Sandbox-Teile des lokalen Dateisystems des Benutzers erstellen, lesen, navigieren und in diese schreiben.

API ist in verschiedene Themen unterteilt:

  • Dateien lesen und verarbeiten: File/Blob, FileList, FileReader

  • Erstellen und schreiben: BlobBuilder, FileWriter

  • Verzeichnis- und Dateisystemzugriff: DirectoryReader, FileEntry/DirectoryEntry, LocalFileSystem

Browser-Unterstützung und Speicherbeschränkungen

Zum Zeitpunkt des Schreibens dieses Artikels gibt es nur GoogleChrome Browser This FileSystemAPI kann implementiert werden. Derzeit gibt es keine dedizierte Browser-Benutzeroberfläche für die Datei-/-Kontingentverwaltung. Um Daten auf dem System des Benutzers zu speichern, muss Ihre App möglicherweise Kontingente anfordern. Sie können jedoch das Tag --unlimited-quota-for-files verwenden, um den Browser Chrome zum Testen auszuführen. Wenn Sie außerdem eine App oder Erweiterung für den Chrome Web Store entwickeln, können Sie die Manifestdateiberechtigungen unlimitedStorage verwenden, ohne Kontingente anzufordern. Abschließend erhält der Benutzer ein Berechtigungsdialogfeld, in dem er der App Speicher gewähren, verweigern oder hinzufügen kann.

Wenn Sie Ihre App über file:// debuggen möchten, benötigen Sie möglicherweise das Tag --allow-file-access-from-files. Die Nichtverwendung dieser Tags führt zu SECURITY_ERR oder QUOTA_EXCEEDED_ERRFileError.

Dateisystem anfordern

Eine Webanwendung kann Zugriff auf das Sandbox-Dateisystem anfordern, indem sie window.requestFileSystem() aufruft:

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

type

Ob die Dateispeicherung persistent sein soll. Mögliche Werte sind window.TEMPORARY und window.PERSISTENT. Über TEMPORARY gespeicherte Daten können nach Ermessen des Browsers gelöscht werden (z. B. wenn mehr Speicherplatz benötigt wird). Das Löschen des PERSISTENT-Speichers erfordert eine ausdrückliche Autorisierung durch den Benutzer oder die App und erfordert, dass der Benutzer Ihrer App ein Kontingent gewährt. Siehe Kontingente anfordern.

Größe

Die Größe in Bytes, die die App zum Speichern benötigt.

successCallback

Callback wird aufgerufen, wenn die Dateisystemanforderung erfolgreich ist. Sein Parameter ist ein FileSystem-Objekt.

opt_errorCallback

Ein optionaler Rückruf, der zur Behandlung von Fehlern verwendet wird oder wenn die Anforderung zum Abrufen des Dateisystems abgelehnt wird. Sein Parameter ist ein FileError-Objekt.

Wenn Sie requestFileSystem() zum ersten Mal aufrufen, erstellt das System neuen Speicher für Ihre Anwendung. Beachten Sie, dass es sich um ein Sandbox-Dateisystem handelt, was bedeutet, dass eine Web-App nicht auf die Dateien einer anderen App zugreifen kann. Dies bedeutet auch, dass Sie keine Dateien in beliebigen Ordnern auf der Festplatte des Benutzers lesen/schreiben können (z. B. „Meine Bilder“, „Meine Dateien“ usw.).

Verwendungsbeispiel:

function onInitFs(fs) {
  console.log('Opened file system: ' + fs.name);}
  window.requestFileSystem(window.TEMPORARY, 5*1024*1024 /*5MB*/, onInitFs, errorHandler);

Die FileSystem-Spezifikation definiert auch die für WebWorker geplante Synchronisierungs-API-Schnittstelle (LocalFileSystemSync). Dieses Tutorial behandelt jedoch nicht die Synchronisierungs-API.

Im Rest dieses Dokuments verwenden wir denselben Handler für Fehler, die durch asynchrone Aufrufe ausgelöst werden:

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(&#39;#myfile&#39;).onchange = function(e) {  
  var files = this.files;  
  
  window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) {  
    // Duplicate each file the user selected to the app&#39;s fs.  
    for (var i = 0, file; file = files[i]; ++i) {  
  
      // Capture current iteration&#39;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(&#39;log.txt&#39;, {create: false}, function(fileEntry) {  
  
    fileEntry.remove(function() {  
      console.log(&#39;File removed.&#39;);  
    }, 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);  
...

创建目录

使用 DirectoryEntrygetDirectory() 方法读取或创建目录。您可以递交名称或路径作为查找或创建所用的目录。

例如,以下代码会在根目录中创建名为“MyPictures”的目录:

[html] view plaincopy
window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) {  
  fs.root.getDirectory(&#39;MyPictures&#39;, {create: true}, function(dirEntry) {  
    ...  
  }, errorHandler);  
}, errorHandler);

子目录

创建子目录的方法与创建其他任何目录的方法完全相同。不过,如果您尝试创建其直接父目录不存在的目录,API 将引发错误。相应的解决方法是,依次创建各级目录,而这对异步 API 而言非常麻烦。

以下代码会在系统创建父文件夹后以递归方式添加各个子文件夹,从而在应用相应 FileSystem 的根中创建新的层次结构 (music/genres/jazz)。

[html] view plaincopy
var path = &#39;music/genres/jazz/&#39;;  
function createDir(rootDirEntry, folders) {  
  // Throw out &#39;./&#39; or &#39;/&#39; and move on to prevent something like &#39;/foo/.//bar&#39;.  
  if (folders[0] == &#39;.&#39; || folders[0] == &#39;&#39;) {  
    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(&#39;/&#39;)); // 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(&#39;/music/genres/jazz/song.mp3&#39;, {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&#39;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 ? &#39;<img src="folder-icon.gif">&#39; :  
                                  &#39;<img src="file-icon.gif">&#39;;  
    var li = document.createElement(&#39;li&#39;);  
    li.innerHTML = [img, &#39;<span>&#39;, entry.name, &#39;</span>&#39;].join(&#39;&#39;);  
    fragment.appendChild(li);  
  });  
  document.querySelector(&#39;#filelist&#39;).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(&#39;music/genres/jazz&#39;, {}, function(dirEntry) {  
    dirEntry.remove(function() {  
      console.log(&#39;Directory removed.&#39;);  
    }, errorHandler);  
  }, errorHandler);  
}, errorHandler);

以递归方式删除目录

如果您不需要某个包含条目的目录,不妨使用 removeRecursively()。该方法将以递归方式删除目录及其内容。

以下代码会以递归方式删除“music”目录及其包含的所有文件和目录:

[html] view plaincopy
window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) {  
  fs.root.getDirectory(&#39;/misc/../music&#39;, {}, function(dirEntry) {  
    dirEntry.removeRecursively(function() {  
      console.log(&#39;Directory removed.&#39;);  
    }, errorHandler);  
  }, errorHandler);  
}, errorHandler);

复制、重命名和移动

FileEntryDirectoryEntry 享有共同的操作。

复制条目

FileEntryDirectoryEntry 均可使用 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, &#39;/folder1/me.png&#39;, &#39;folder2/mypics/&#39;);  
}, errorHandler);

移动或重命名条目

FileEntryDirectoryEntrymoveTo() 方法可让您移动或重命名文件或目录。其第一个参数是文件要移动到的目标父目录,其第二个参数是文件可选的新名称。如未提供新名称,系统将使用文件的原名称。

以下示例将“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, &#39;me.png&#39;, &#39;you.png&#39;);  
}, 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(&#39;/me.png&#39;, &#39;newfolder/&#39;);  
}, errorHandler);

filesystem: 网址

FileSystem API 使用新的网址机制,(即 filesystem:),可用于填充 src 或 href 属性。例如,如果您要显示某幅图片且拥有相应的 fileEntry,您可以调用 toURL() 获取该文件的 filesystem: 网址:

var img = document.createElement(&#39;img&#39;);  
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 = &#39;filesystem:http://example.com/temporary/myfile.png&#39;;  
window.resolveLocalFileSystemURL(url, function(fileEntry) {  
  ...  
});

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der Codefälle der HTML5-Dateioperations-API. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn