在平時開發的html頁面中,我們寫的Js是沒有讀取用戶電腦本地文件的權限的,這是出於瀏覽器運行時的安全考慮的,但在我們在使用h5打包app時,如果再像瀏覽器上讓用戶下載,上傳文件,就會使用戶的使用體驗遠不如電腦上操作,而且經常會有一些想要緩存本地的數據,cookies太小,滿足不了需求,所以就想到能不能有內容時,我能直接操作手機的存取,我使用的是hbuilder打包app的,所以到官網上找找,就找到了有一節專門關於io的介紹,研究了一下使用方法,所以特此記錄一下
本文所參考官方文檔 https://www.html5plus.org/doc/zh_cn/io.html
先明確幾個主要對象及方法,這里與java里的對照來看,會java的朋友可能會更容易理解
requestFileSystem 請求本地文件系統對象的方法,獲取指定的文件系統,可通過type指定獲取文件系統的類型。 獲取指定的文件系統對象成功通過succesCB回調返回,失敗則通過errorCB返回。
參數:
-
- type: ( Number ) 必選 本地文件系統常量
- 可取plus.io下的常量,如plus.io.PRIVATE_DOC、plus.io.PUBLIC_DOCUMENTS等。
- succesCB: ( FileSystemSuccessCallback ) 必選 請求文件系統成功的回調
- errorCB: ( FileErrorCallback ) 可選 請求文件系統失敗的回調
注意,這里的type取值共4個,對應於不同的空間,也有不同的訪問權限限制,一定要注意區分,如果讀取或寫入文件時報錯時,記得檢查這個地方, 以下為原文
為了安全管理應用的資源目錄,規范對文件系統的操作,5+ API在系統應用目錄的基礎設計了應用沙盒目錄, 分為私有目錄和公共目錄兩種類型,私有目錄僅應用自身可以訪問,公共目錄在多應用環境時(如小程序SDK)所有應用都可訪問。
-
- 應用私有資源目錄,對應常量plus.io.PRIVATE_WWW,僅應用自身可讀
- 應用私有文檔目錄,對應常量plus.io.PRIVATE_DOC,僅應用自身可讀寫
- 應用公共文檔目錄,對應常量plus.io.PUBLIC_DOCUMENTS,多應用時都可讀寫,常用於保存應用間共享文件
- 應用公共下載目錄,對應常量plus.io.PUBLIC_DOWNLOADS,多應用時都可讀寫,常用於保存下載文件
DirectoryEntry 文件系統中的目錄對象,用於管理特定的本地目錄或者文件,對應於java中可以理解為File對象,這是操作文件最基礎的對象,一個directoryEntry對象就對應於一個文件或文件夾
屬性:
-
- isFile: 操作對象的是否為文件,DirectoryEntry對象固定其值為false
- isDirectory: 操作對象是否為目錄,DirectoryEntry對象固定其值為true
- name: 目錄操作對象的名稱,不包括路徑
- fullPath: 目錄操作對象的完整路徑,文件系統的絕對路徑
- fileSystem: 文件操作對象所屬的文件系統對象,參考FileSystem
方法:
-
- getMetadata: 獲取目錄的屬性
- moveTo: 移動目錄
- copyTo: 拷貝目錄
- toURL: 獲取目錄路徑轉換為URL地址
- toLocalURL: 獲取目錄路徑轉換為本地路徑URL地址
- toRemoteURL: 獲取目錄路徑轉換為網絡路徑URL地址
- remove: 刪除目錄
- getParent: 獲取目錄所屬的父目錄
- createReader: 創建目錄讀取對象
- getDirectory: 創建或打開子目錄
- getFile: 創建或打開文件
- removeRecursively: 遞歸刪除目錄
FileEntry 文件系統中的文件對象,用於管理特定的本地文件,對應於java中可以理解為File對象
屬性:
-
- isFile: 文件操作對象的是否為文件,FileEntry對象固定其值為true
- isDirectory: 文件操作對象是否為目錄,FileEntry對象固定其值為false
- name: 文件操作對象的名稱,不包括路徑
- fullPath: 文件操作對象的完整路徑,文件系統的絕對路徑
- fileSystem: 文件操作對象所屬的文件系統對象,參考FileSystem
方法:
-
- getMetadata: 獲取文件的屬性信息
- moveTo: 移動文件
- copyTo: 拷貝文件
- toURL: 獲取文件路徑轉換為URL地址
- toLocalURL: 獲取文件路徑轉換為本地路徑URL地址
- toRemoteURL: 獲取文件路徑轉換為網絡路徑URL地址
- remove: 刪除文件
- getParent: 獲取文件所屬的父目錄
- createWriter: 獲取文件關聯的寫文件操作對象FileWriter
- file: 獲取文件數據對象
FileReader 創建讀取文件對象,主要是文件讀取相關的操作,文件以文本或者Base64編碼的字符串形式讀出來,對應於java中可理解為InputStream
屬性:
-
- readyState: 當前讀取文件所處的狀態
- result: 已讀取文件的內容
- error: 文件操作錯誤代碼
方法:
-
- abort: 終止文件讀取操作
- readAsDataURL: 以URL編碼格式讀取文件數據內容
- readAsText: 以文本格式讀取文件數據內容
事件:
-
- onloadstart: 讀取文件開始時的回調函數
- onload: 讀取文件成功完成的回調函數
- onabort: 取消讀取文件時的回調函數
- onerror: 文件讀取操作失敗時調用的回調函數
- onloadend: 文件讀取操作完成時的回調函數
FileWriter 文件系統中的寫文件對象,用於寫入文件內容,用戶注冊自己的事件監聽器來接收writestart、progress、write、writeend、error和abort事件,一個FileWriter對象是為單個文件的操作而創建,可以使用該對象多次對相應文件進行寫入操作。 FileWriter維護該文件的指針位置及長度屬性,這樣就可以尋找和寫入文件的任何地方。 默認情況下,FileWriter從文件的開頭開始寫入(將覆蓋現有數據),seek方法可設置文件操作指定位置,如fw.seek(fw.length-1)寫入操作就會從文件的末尾開始 ,對應於java中理解為OutputStream
屬性:
-
- readyState: 當前寫入文件所處的狀態
- length: 文件當前的長度,單位為字節
- position: 文件當前操作的指針位置
- error: 文件寫入操作錯誤代碼
方法:
事件:
-
- onwritestart: 寫入文件開始時的回調函數
- onwrite: 寫入文件成功完成的回調函數
- onabort: 取消寫入文件時的回調函數
- onerror: 文件寫入操作失敗時調用的回調函數
- onwriteend: 文件寫入操作完成時的回調函數
下面是讀寫文件的主要代碼及說明
1、獲取文件讀寫的基礎,FileEntry對象
1 //讀取應用公共文檔目錄 2 plus.io.requestFileSystem(plus.io.PUBLIC_DOCUMENTS, function(fs) { 3 // 通過fs.root獲取DirectoryEntry對象進行操作,獲取文件操作的根目錄 4 //這里的filePath即你要讀取的文件所在的相對路徑,可隨意定義,但不得是 _www開頭,因為_www開頭是應用私有資源目錄的專用,只有讀權限,不能寫入,寫入時會報錯 5 var filePath = 'abc/haha/test.txt'; 6 var rootDirectoryEntry = fs.root; 7 rootDirectoryEntry.getFile(filePath, { 8 //這個參數的作用是 指示如果文件或目錄不存在時是否進行創建,默認值為false,設為true表示如果這個filePath下的test.txt文件不存在就創建,當然,如果存在就直接返回,不會創建 9 create: true 10 }, function(fileEntry) { 11 //FileEntry對象獲取成功,對應就是test.txt文件了,可以接着進行相應的讀寫操作了 12 13 }, function(e) { 14 console.log(e.message); 15 }); 16 }, function(e) { 17 console.log(e.message); 18 });
2、在上一步獲取到文件操作對象FileEntry的基礎上,開始讀操作
1 //獲取文件 2 fileEntry.file(function (file) { 3 //console.log(file.size + ' <--> ' + file.name); 4 //創建一個文件讀取工具,在java中理解就是InputStream輸入流 5 var fileReader = new plus.io.FileReader(); 6 //成功讀取到文件內容時的回調,其中evt.target.result就是文件中的文本內容 7 fileReader.onloadend = function (evt) { 8 console.log(evt.target.result); 9 } 10 //文件讀取操作失敗時調用的回調函數 11 fileReader.onerror = function (e) { 12 console.log(e.message); 13 } 14 //將剛才請求到的文件以utf-8編碼,文本的形式讀出 15 fileReader.readAsText(file, 'utf-8'); 16 });
3、還是以文件操作對象FileEntry為基礎,進行寫入文件的操作
1 //通過fileEntry的createWriter創建輸出流,向文件寫入內容,對應java中的OutputStream 2 fileEntry.createWriter(writer => { 3 //文件寫入成功后的回調 4 writer.onwrite = function(event) { 5 //寫入成功 6 console.log('寫入成功'); 7 } 8 //文件寫入操作失敗時調用的回調函數 9 writer.onerror = function(e) { 10 console.log(e.message); 11 } 12 //設置文件寫入的起點,writer.length就是上次文件里面內容的最后位置,這樣即將新的內容追加到文本最末尾,如果想覆蓋原先的內容,直接設置為0即可 13 var cursor = writer.length; 14 writer.seek(cursor); 15 //將要寫入的文本dataStr寫入到文件中去 16 writer.write(dataStr); 17 }, function(e) { 18 console.log(e.message); 19 });
以上就是在Hbuilder中打包H5的App讀寫文件的主要代碼了及功能了
但是我們發現這樣寫會有無數的回調函數,不僅不好看,別人調用時也不好傳參,所以我們可以采用ES6的寫法加上Primse來封裝改造一下,讓這些方法看起來更加優雅一點
1 getFileEntry(filePath) { 2 return new Promise((resolve, reject) => { 3 plus.io.requestFileSystem(plus.io.PUBLIC_DOCUMENTS, fs => { 4 fs.root.getFile(filePath, {create: true}, fileEntry => { 5 resolve(fileEntry); 6 }, e => { 7 reject(e) 8 }); 9 }, e => { 10 reject(e) 11 }); 12 }) 13 }, 14 readData(fileEntry) { 15 return new Promise((resolve, reject) => { 16 fileEntry.file(function (file) { 17 let fileReader = new plus.io.FileReader(); 18 fileReader.onloadend = function (evt) { 19 resolve(evt.target.result); 20 } 21 fileReader.onerror = function (e) { 22 reject(e) 23 } 24 fileReader.readAsText(file, 'utf-8'); 25 }); 26 }) 27 }, 28 writeData(fileEntry, dataStr, cursorStart) { 29 return new Promise((resolve, reject) => { 30 fileEntry.createWriter(writer => { 31 writer.onwrite = e => { 32 resolve() 33 } 34 writer.onerror = e => { 35 reject(e) 36 } 37 //設置文件寫入的起點 38 let length = cursorStart != undefined ? cursorStart : writer.length; 39 writer.seek(length); 40 writer.write(dataStr); 41 }, e => { 42 reject(e) 43 }); 44 }) 45 }
這樣在調用時,代碼就會非常簡潔了
讀取文件內容
1 let filePath = '/abc/haha/test.json'; 2 this.getFileEntry(filePath) 3 .then(fileEntry => { 4 // fileEntry.remove(); //刪除文件 5 return this.readData(fileEntry); 6 }).then(fileText => { 7 alert("文件內容:>>> " + fileText) 8 }).catch(e => { 9 alert('文件讀取失敗!') 10 })
寫入文件內容
1 let writeDataStr = 'Hello World!!!' 2 this.getFileEntry() 3 .then(fileEntry => { 4 return this.writeData(fileEntry, writeDataStr); 5 }).then(writer => { 6 alert('保存成功'); 7 }).catch(e => { 8 alert('保存失敗!') 9 })
OK ! 打完收工