在項目中有個需求是需要上傳文件,項目中使用的是antd-vue UI庫,用法就僅僅是將后端給的文件上傳接口放到antd-vue的a-upload組件的action屬性上面。然后將后端返回的文件網絡資源再次提交到后端。這樣功能是實現了,但是具體發生了什么確實沒有什么認知。在這里把我認為的需要知道的文件上傳的前端知識以及我用這些知識來解決了哪些問題寫一篇隨筆加以記錄。
1.<input type="file">
文件上傳是基於這個標簽實現的,通過約定這個標簽的屬性用來控制文件上傳的,比如控制文件的類型、change事件等,具體可查看MDN文檔。
在這個標簽的change事件中,可以查看到已經選擇的文件的信息:
<body> 上傳文件:<input type="file" id="file" multiple> <script> let file = document.getElementById("file") file.onchange = function (e) { console.log("當前元素", e); console.log("文件列表", e.target.files); } </script> </body>
通過控制台可以查看到當前node節點的信息以及這個file類型的input標簽中的文件信息:
node節點信息這里不談,這里主要了解文件信息的這幾個屬性(這些屬性都屬於JavaScript API中的File API):
- name:本地系統中的文件名
- size:以字節記的文件大小——這個屬性可以用來前端控制文件上傳的大小
- type:包含文件MIME類型的字符串
- lastModifiedDate:表示文件最后修改事件的字符串。這個屬性只有Chrome實現了
2.FileReader類型
FileReader類型屬於File API,作用是從文件中實際讀取數據。這個功能主要用於需要前端讀取上傳文件內容的需求上。
在項目中我遇到過讀取csv文件中的內容並將其讀取出來填充到表格中,這個功能就用到了Fi了Reader類型。
(1)讀取文件的方法
FileReader類型表示一種異步文件讀取機制。可以把FileReader想象成XMLHttpRequest,只不過是用於從文件系統讀取文件,而不是從服務器中。FileReader類型提供了幾個讀取文件數據的方法:
-
- (1)readAsText(file,encoding):從文件中讀取純文本內容並保存在result屬性中。
- ① file是需要讀取的文件;
- ② encoding表示編碼(如果讀取的文件可能有中文亂碼的情況,可以填"GB2312")
- ③ 使用場景:在某些需求下可能需要前端獲取到文件上傳的文本中的內容用來操作(當然這也可以把文件讀取放在后端操作,然后通過后端接口返回讀取到的數據)。上面說的讀取內容並填充就用到了這個功能,這里用原生的<input type = "file">實現一遍:
-
1 <body> 2 上傳文件:<input type="file" id="file"> 3 </body> 4 5 <script> 6 let file = document.getElementById("file") 7 file.onchange = function (e) { 8 let reader = new FileReader(); // 實例化FileReader對象 9 reader.readAsText(e.target.files[0], 'GB2312') // 這里第二個參數是可選的,且編碼類型需要根據不同的文件類型抉擇,下面的CSV文件與TXT文件就分別用了GB2312(CSV)和沒有傳入第二個參數 10 reader.onload = (res) => { // onload表示文件讀取成功時的回調 12 console.log("文件讀取內容", res); 13 console.log(res.target.result.split(/[(\r\n)\r\n]+/)); // 這里讀取的CSV文件每一行都會帶一個換行符,這里使用字符串拆解成根據行數分開的數組 14 } 15 } 16 </script>
- 文件內容及讀取結果:
- 文件內容
讀取結果:
- 文件內容
讀取結果:
- 文件內容
-
- (1)readAsText(file,encoding):從文件中讀取純文本內容並保存在result屬性中。
-
-
-
- 說明:
- readAsText第一個參數傳入的就是上面說的<input type = "file">標簽的文件上傳的文件列表中的某個文件(就是包含size、name、type、lastModifiedDate屬性的文件)。對應了第九行的代碼
- 這里讀取CSV的結果以及txt類型的文件中,換行符將會包含在其中,我這里使用正則表達式按行分割了讀取到的內容。因此這里需要根據實際情況自行處理
- 在antd-vue的upload組件的change屬性,實現同理,只不過change屬性將包含size、name、type、lastModifiedDate屬性的文件這個屬性放到了change回調函數中默認傳入的{file,files}屬性中了,如果使用的是這個組件的話,在change回調中打印一下兩個屬性找到即可
- 說明:
- (2)ReadAsDataURL(file):讀取文件並將內容的數據URL保存在result中。
- 讀取到的結果可以直接用於img標簽的src屬性:
-
1 <body> 2 上傳文件:<input type="file" id="file"> 3 <!-- 用於預覽的img標簽 --> 4 <img id="show-img" src="" alt=""> 5 </body> 6 7 <script> 8 let file = document.getElementById("file") 9 file.onchange = function (e) { 10 let reader = new FileReader(); // 11 reader.readAsDataURL(e.target.files[0]) 12 13 reader.onload = (res) => { 14 console.log("文件讀取內容", res); 15 let img = document.getElementById("show-img") 16 img.src = res.target.result 17 } 18 } 19 </script>
- 圖片讀取的結果:
讀取的結果是base64格式的圖片數據
- 且可以直接用於圖片展示:
-
- 讀取到的結果可以直接用於img標簽的src屬性:
- (3)readAsBinaryString(file): 讀取文件並將每個字符的二進制數據保存在result屬性中
- 這個讀取為二進制暫時不知如何使用,待補充
- (4)readAsArrayBuffer(file):讀取文件並將文件內容以 ArrayBuffer 形式保存在 result 屬性。
- 暫時也沒有用過,待補充
-
-
(2)事件
由於這些讀取方法是異步的,所以每個FileReader會發布幾個事件,最常用的三個是:
- process:(reader.onprocess)每50ms就會觸發一次,其與XHR的process事件具有相同的信息:lengthComputable、loaded、total。此外,在在process事件中可以讀取FileReader的reasult屬性,及時其中尚未包含全部數據
- error:(reader.onerror)由於某種原因無法讀取文件時觸發。包含了錯誤信息。只包含一個屬性:code。可能的值有:
- 1:未找到文件
- 2:安全錯誤
- 3:讀取被終端
- 4:文件不可讀
- 5:編碼錯誤
- load:(reader.onload)在文件成功加載后觸發。如果error被觸發,則不會觸發load事件
3.總結
- 如果只是簡單的文件上傳,則可以直接使用原生<input type = "file">以及各UI庫的upload組件配合后端給的文件上傳接口處理即可
- 如果有前端限制上傳文件大小的需求,則可以根據change回調中的size屬性試情況處理
- FileReader類則常用於讀取本地文件內容,如讀取txt或者CSV等格式文件的內容,不過需要注意編碼以及字符串切割
如果還有再補充