web實(shí)現(xiàn)文件上傳的方式有哪些?可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
成都創(chuàng)新互聯(lián)公司專注于五華網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供五華營銷型網(wǎng)站建設(shè),五華網(wǎng)站制作、五華網(wǎng)頁設(shè)計(jì)、五華網(wǎng)站官網(wǎng)定制、小程序開發(fā)服務(wù),打造五華網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供五華網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。文件上傳是 Web 開發(fā)常見需求,上傳文件需要用到文件輸入框,如果給文件輸入框添加一個 multiple 屬性則可以一次選擇多個文件(不支持的瀏覽器會自動忽略這個屬性)
點(diǎn)擊這個輸入框就可以打開瀏覽文件對話框選擇文件了,一般一個輸入框上傳一個文件就行,要上傳多個文件也可以用多個輸入框來處理,這樣做是為了兼容那些不支持 multiple 屬性的瀏覽器,同時用戶一般也不會選擇多個文件
基本上傳方式
當(dāng)把文件輸入框放入表單中,提交表單的時候即可將選中的文件一起提交上傳到服務(wù)器,需要注意的是由于提交的表單中包含文件,因此要修改一下表單元素的 enctype 屬性為 multipart/form-data
這樣上傳方式是傳統(tǒng)的同步上傳,上傳的文件如果很大,往往需要等待很久,上傳完成后頁面還會重新加載,并且必須等待上傳完成后才能繼續(xù)操作
早期的瀏覽器并不支持異步上傳,不過可以使用 iframe 來模擬,在頁面中隱藏一個
這樣在提交表單上傳的時候,頁面就不會重新加載了,取而代之的是 iframe 重新加載了,不過 iframe 原本就是隱藏的,即使重新加載也不會感知到
訪問文件
File API 提供了訪問文件的能力,通過輸入框的 files 屬性訪問,這會得到一個 FileList,這是一個集合,如果只選擇了一個文件,那么集合中的第一個元素就是這個文件
var input = document.querySelector('input[type="file"]') var file = input.files[0] console.log(file.name) // 文件名稱 console.log(file.size) // 文件大小 console.log(file.type) // 文件類型
支持 File API 的瀏覽器可以參考 caniuse
Ajax 上傳
由于可以通過 File API 直接訪問文件內(nèi)容,再結(jié)合 XMLHttpRequest 對象直接將文件上傳,將其作為參數(shù)傳給 XMLHttpRequest 對象的 send 方法即可
var xhr = new XMLHttpRequest() xhr.open('POST', '/upload/url', true) xhr.send(file)
不過一些原因不建議直接這樣傳遞文件,而是使用 FormData 對象來包裝需要上傳的文件,F(xiàn)ormData 是一個構(gòu)造函數(shù),使用的時候先 new 一個實(shí)例,然后通過實(shí)例的 append 方法向其中添加數(shù)據(jù),直接把需要上傳的文件添加進(jìn)去
var formData = new FormData() formData.append('file', file, file.name) // 第 3 個參數(shù)是文件名稱 formData.append('username', 'Mary') // 還可以添加額外的參數(shù)
甚至也可以直接把表單元素作為實(shí)例化參數(shù),這樣整個表單中的數(shù)據(jù)就全部包含進(jìn)去了
var formData = new FormData(document.querySelector('form'))
數(shù)據(jù)準(zhǔn)備好后,就是上傳了,同樣是作為參數(shù)傳給 XMLHttpRequest 對象的 send 方法
var xhr = new XMLHttpRequest() xhr.open('POST', '/upload/url', true) xhr.send(formData)監(jiān)測上傳進(jìn)度
XMLHttpRequest 對象還提供了一個 progress 事件,基于這個事件可以知道上傳進(jìn)度如何
var xhr = new XMLHttpRequest() xhr.open('POST', '/upload/url', true) xhr.upload.onprogress = progressHandler // 這個函數(shù)接下來定義
上傳的 progress 事件由 xhr.upload 對象觸發(fā),在事件處理程序中使用這個事件對象的 loaded(已上傳字節(jié)數(shù)) 和 total(總數(shù)) 屬性來計(jì)算上傳的進(jìn)度
function progressHandler(e) { var percent = Math.round((e.loaded / e.total) * 100) }
上面的計(jì)算會得到一個表示完成百分比的數(shù)字,不過這兩個值也不一定總會有,保險一點(diǎn)先判斷一下事件對象的 lengthComputable 屬性
function progressHandler(e) { if (e.lengthComputable) { var percent = Math.round((e.loaded / e.total) * 100) } }
支持 Ajax 上傳的瀏覽器可以參考 caniuse https://caniuse.com/#feat=xhr2
分割上傳
使用文件對象的 slice 方法可以分割文件,給該方法傳遞兩個參數(shù),一個起始位置和一個結(jié)束位置,這會返回一個新的 Blob 對象,包含原文件從起始位置到結(jié)束位置的那一部分(文件 File 對象其實(shí)也是 Blob 對象,這可以通過 file instanceof Blob 確定,Blob 是 File 的父類)
var blob = file.slice(0, 1024) // 文件從字節(jié)位置 0 到字節(jié)位置 1024 那 1KB
將文件分割成幾個 Blob 對象分別上傳就能實(shí)現(xiàn)將大文件分割上傳
function upload(file) { let formData = new FormData() formData.append('file', file) let xhr = new XMLHttpRequest() xhr.open('POST', '/upload/url', true) xhr.send(formData) } var blob = file.slice(0, 1024) upload(blob) // 上傳第一部分 var blob2 = file.slice(1024, 2048) upload(blob2) // 上傳第二部分 // 上傳剩余部分
通常用一個循環(huán)來處理更方便
var pos = 0 // 起始位置 var size = 1024 // 塊的大小 while (pos < file.size) { let blob = file.slice(pos, pos + size) // 結(jié)束位置 = 起始位置 + 塊大小 upload(blob) pos += size // 下次從結(jié)束位置開始繼續(xù)分割 }
服務(wù)器接收到分塊文件進(jìn)行重新組裝的代碼就不在這里展示了
使用這種方式上傳文件會一次性發(fā)送多個 HTTP 請求,那么如何處理這種多個請求同時發(fā)送的情況呢?方法有很多,可以用 Promise 來處理,讓每次上傳都返回一個 promise 對象,然后用 Promise.all 方法來合并處理,Promise.all 方法接受一個數(shù)組作為參數(shù),因此將每次上傳返回的 promise 對象放在一個數(shù)組中
var promises = [] while (pos < file.size) { let blob = file.slice(pos, pos + size) promises.push(upload(blob)) // upload 應(yīng)該返回一個 promise pos += size }
同時改造一下 upload 函數(shù)使其返回一個 promise
function upload(file) { return new Promise((resolve, reject) => { let formData = new FormData() formData.append('file', file) let xhr = new XMLHttpRequest() xhr.open('POST', '/upload/url', true) xhr.onload = () => resolve(xhr.responseText) xhr.onerror = () => reject(xhr.statusText) xhr.send(formData) }) }
當(dāng)一切完成后
Promise.all(promises).then((response) => { console.log('Upload success!') }).catch((err) => { console.log(err) })
支持文件分割的瀏覽器可以參考 caniuse
判斷一下文件對象是否有該方法就能知道瀏覽器是否支持該方法,對于早期的部分版本瀏覽器需要加上對應(yīng)的瀏覽器廠商前綴
var slice = file.slice || file.webkitSlice || file.mozSlice if (slice) { let blob = slice.call(file, 0, 1024) // call upload(blob) } else { upload(file) // 不支持分割就只能直接上傳整個文件了,或者提示文件過大 }
拖拽上傳
通過拖拽 API 可以實(shí)現(xiàn)拖拽文件上傳,默認(rèn)情況下,拖拽一個文件到瀏覽器中,瀏覽器會嘗試打開這個文件,要使用拖拽功能需要阻止這個默認(rèn)行為
document.addEventListener('dragover', function(e) { e.preventDefault() e.stopPropagation() })
任意指定一個元素來作為釋放拖拽的區(qū)域,給一個元素綁定 drop 事件
var element = document.querySelector('label') element.addEventListener('drop', function(e) { e.preventDefault() e.stopPropagation() // ... })
通過該事件對象的 dataTransfer 屬性獲取文件,然后上傳即可
var file = e.dataTransfer.files[0] upload(file) // upload 函數(shù)前面已經(jīng)定義
選擇類型
給文件輸入框添加 accept 屬性即可指定選擇文件的類型,比如要選擇 png 格式的圖片,則指定其值為 image/png,如果要允許選擇所有類型的圖片,就是 image/*
添加 capture 屬性可以調(diào)用設(shè)備機(jī)能,比如 capture="camera" 可以調(diào)用相機(jī)拍照,不過這并不是一個標(biāo)準(zhǔn)屬性,不同設(shè)備實(shí)現(xiàn)方式也不一樣,需要注意
經(jīng)測 iOS 設(shè)備添加該屬性后只能拍照而不能從相冊選擇文件了,所以判斷一下
if (iOS) { // iOS 用 navigator.userAgent 判斷 input.removeAttribute('capture') }
不支持的瀏覽器會自動忽略這些屬性
自定義樣式
文件輸入框在各個瀏覽器中呈現(xiàn)的樣子都不大相同,而且給 input 定義樣式也不是那么方便,如果有需要應(yīng)用自定義樣式,有一個技巧,可以用一個 label 關(guān)聯(lián)到這個文件輸入框,當(dāng)點(diǎn)擊這個 label 元素的時候就會觸發(fā)文件輸入框的點(diǎn)擊,打開瀏覽文件的對話框,相當(dāng)于點(diǎn)擊了文件輸入框一樣的效果
這時就可以將原本的文件輸入框隱藏了,然后給 label 元素任意地應(yīng)用樣式,畢竟要給 label 元素應(yīng)用樣式比 input 方便得多
看完上述內(nèi)容,你們對web實(shí)現(xiàn)文件上傳的方式有進(jìn)一步的了解嗎?如果還想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)網(wǎng)站制作公司行業(yè)資訊頻道,感謝各位的閱讀。