簡介
我常常想,如果網絡應用能夠讀取和寫入文件與目錄,將會非常方便。從離線轉移到在線后,應用變得更加復雜,而文件系統方面的API的缺乏也一直阻礙著網絡前進。存儲二進制數據或與其進行交互不應局限于桌面。令人欣慰的是,由于FileSystemAPI的出現,這一現狀終于得到了改變。有了FileSystemAPI,網絡應用就可以創建、讀取、導航用戶本地文件系統中的沙盒部分以及向其中寫入數據。
API 被分為以下不同的主題:
-
讀取和處理文件:File/Blob、FileList、FileReader
-
創建和寫入:BlobBuilder、FileWriter
-
目錄和文件系統訪問:DirectoryReader、FileEntry/DirectoryEntry、LocalFileSystem
瀏覽器支持與存儲限制
在寫這篇文章時,只有 GoogleChrome 瀏覽器可以實施此FileSystemAPI。目前尚不存在專門用于文件/配額管理的瀏覽器用戶界面。要在用戶的系統上存儲數據,您的應用可能需要請求配額。不過,可使用--unlimited-quota-for-files標記運行Chrome瀏覽器進行測試。此外,如果您要開發的是用于Chrome網上應用店的應用或擴展程序,可使用unlimitedStorage清單文件權限,而無需請求配額。最后,用戶會收到授予、拒絕或為應用增加存儲的權限對話框。
如果您要通過 file://調試您的應用,可能需要--allow-file-access-from-files標記。不使用這些標記會導致SECURITY_ERR或QUOTA_EXCEEDED_ERRFileError。
請求文件系統
網絡應用可通過調用 window.requestFileSystem()請求對沙盒文件系統的訪問權限:
[html] view plaincopy
//?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文件存儲是否應該是持久的。可能的值包括window.TEMPORARY和window.PERSISTENT。通過TEMPORARY存儲的數據可由瀏覽器自行決定刪除(例如在需要更多空間的情況下)。要清除PERSISTENT存儲,必須獲得用戶或應用的明確授權,并且需要用戶向您的應用授予配額。請參閱請求配額。size應用需要用于存儲的大小(以字節為單位)。successCallback文件系統請求成功時調用的回調。其參數為 FileSystem對象。opt_errorCallback用于處理錯誤或獲取文件系統的請求遭到拒絕時可選的回調。其參數為FileError對象。如果您是首次調用requestFileSystem(),系統會為您的應用創建新的存儲。請注意,這是沙箱文件系統,也就是說,一個網絡應用無法訪問另一個應用的文件。這也意味著您無法在用戶硬盤上的任意文件夾(例如“我的圖片”、“我的文檔”等)中讀/寫文件。
用法示例:
function onInitFs(fs) {console.log('Opened file system: ' + fs.name);
}window.requestFileSystem(window.TEMPORARY, 5*1024*1024 /*5MB*/, onInitFs, errorHandler); FileSystem 規范還定義了計劃用于WebWorkers的同步API(LocalFileSystemSync)接口。不過,本教程不涉及該同步API。
在本文檔的其余部分中,我們將使用相同的處理程序處理異步調用引發的錯誤:
[html] view plaincopy
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 以請求存儲:
[html] view plaincopy
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 的屬性和方法:
[html] view plaincopy
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”的空白文件:
[html] view plaincopy
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 讀取文件內容,然后將其附加到頁面上新的
<textarea>。如果
log.txt 不存在,系統將引發錯誤。
[html] view plaincopy
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”文字。
[html] view plaincopy
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”文字附加到日志文件結尾。如果該文件不存在,系統將引發錯誤。
[html] view plaincopy
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);?? 復制用戶選定的文件
以下代碼可讓用戶使用 <input type="file" multiple /> 選擇多個文件,并在應用的沙盒文件系統中復制這些文件。
[html] view plaincopy
<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”文件。
[html] view plaincopy
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 的屬性和方法:
[html] view plaincopy
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: 網址
FileSystem API 使用新的網址機制,(即 filesystem:),可用于填充 src 或 href 屬性。例如,如果您要顯示某幅圖片且擁有相應的 fileEntry,您可以調用 toURL() 獲取該文件的 filesystem: 網址:
[html] view plaincopy
var?img?=?document.createElement('img');??img.src?=?fileEntry.toURL();?//?filesystem:http://example.com/temporary/myfile.png??document.body.appendChild(img);?? 另外,如果您已具備 filesystem: 網址,可使用 resolveLocalFileSystemURL() 找回 fileEntry:
[html] view plaincopy
window.resolveLocalFileSystemURL?=?window.resolveLocalFileSystemURL?||?????????????????????????????????????window.webkitResolveLocalFileSystemURL;????var?url?=?'filesystem:http://example.com/temporary/myfile.png';??window.resolveLocalFileSystemURL(url,?function(fileEntry)?{????...??});?? 支持的瀏覽器:
Opera僅支持FileAPI ?IE不支持 ? Safari不支持 ? Firefox僅支持FileAPI ? Chrome/Chromium瀏覽器支持。
轉載于:https://www.cnblogs.com/zhwl/p/3201975.html
總結
以上是生活随笔為你收集整理的HTML5 文件操作API的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。