您好,登錄后才能下訂單哦!
這篇“用于web開發(fā)的文件上傳怎么實現(xiàn)”文章的知識點大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“用于web開發(fā)的文件上傳怎么實現(xiàn)”文章吧。
文件上傳是Web開發(fā)常見需求,上傳文件需要用到文件輸入框,如果給文件輸入框添加一個multiple屬性則可以一次選擇多個文件(不支持的瀏覽器會自動忽略這個屬性)
<inputmultipletype="file">
點擊這個輸入框就可以打開瀏覽文件對話框選擇文件了,一般一個輸入框上傳一個文件就行,要上傳多個文件也可以用多個輸入框來處理,這樣做是為了兼容那些不支持multiple屬性的瀏覽器,同時用戶一般也不會選擇多個文件
基本上傳方式
當(dāng)把文件輸入框放入表單中,提交表單的時候即可將選中的文件一起提交上傳到服務(wù)器,需要注意的是由于提交的表單中包含文件,因此要修改一下表單元素的enctype屬性為multipart/form-data
<formaction="#"enctype="multipart/form-data"method="post">
<inputname="file"type="file">
<buttontype="submit">Upload</button>
</form>
這樣上傳方式是傳統(tǒng)的同步上傳,上傳的文件如果很大,往往需要等待很久,上傳完成后頁面還會重新加載,并且必須等待上傳完成后才能繼續(xù)操作
早期的瀏覽器并不支持異步上傳,不過可以使用iframe來模擬,在頁面中隱藏一個<iframe>元素,指定一個name值,同時將<form>元素的target屬性值指定為<iframe>元素的name屬性的值,將兩者關(guān)聯(lián)起來
<formaction="#"enctype="multipart/form-data"method="post"target="upload-frame">
<inputname="file"type="file">
<buttontype="submit">Upload</button>
</form>
<iframeid="upload-frame"name="upload-frame"src="about:blank"style="display:none;"></iframe>
這樣在提交表單上傳的時候,頁面就不會重新加載了,取而代之的是iframe重新加載了,不過iframe原本就是隱藏的,即使重新加載也不會感知到
訪問文件
FileAPI提供了訪問文件的能力,通過輸入框的files屬性訪問,這會得到一個FileList,這是一個集合,如果只選擇了一個文件,那么集合中的第一個元素就是這個文件
varinput=document.querySelector('input[type="file"]')
varfile=input.files[0]
console.log(file.name)//文件名稱
console.log(file.size)//文件大小
console.log(file.type)//文件類型
支持FileAPI的瀏覽器可以參考caniuse
Ajax上傳
由于可以通過FileAPI直接訪問文件內(nèi)容,再結(jié)合XMLHttpRequest對象直接將文件上傳,將其作為參數(shù)傳給XMLHttpRequest對象的send方法即可
varxhr=newXMLHttpRequest()
xhr.open('POST','/upload/url',true)
xhr.send(file)
不過一些原因不建議直接這樣傳遞文件,而是使用FormData對象來包裝需要上傳的文件,F(xiàn)ormData是一個構(gòu)造函數(shù),使用的時候先new一個實例,然后通過實例的append方法向其中添加數(shù)據(jù),直接把需要上傳的文件添加進去
varformData=newFormData()
formData.append('file',file,file.name)//第3個參數(shù)是文件名稱
formData.append('username','Mary')//還可以添加額外的參數(shù)
甚至也可以直接把表單元素作為實例化參數(shù),這樣整個表單中的數(shù)據(jù)就全部包含進去了
varformData=newFormData(document.querySelector('form'))
數(shù)據(jù)準備好后,就是上傳了,同樣是作為參數(shù)傳給XMLHttpRequest對象的send方法
varxhr=newXMLHttpRequest()
xhr.open('POST','/upload/url',true)
xhr.send(formData)
監(jiān)測上傳進度
XMLHttpRequest對象還提供了一個progress事件,基于這個事件可以知道上傳進度如何
varxhr=newXMLHttpRequest()
xhr.open('POST','/upload/url',true)
xhr.upload.onprogress=progressHandler//這個函數(shù)接下來定義
上傳的progress事件由xhr.upload對象觸發(fā),在事件處理程序中使用這個事件對象的loaded(已上傳字節(jié)數(shù))和total(總數(shù))屬性來計算上傳的進度
functionprogressHandler(e){
varpercent=Math.round((e.loaded/e.total)*100)
}
上面的計算會得到一個表示完成百分比的數(shù)字,不過這兩個值也不一定總會有,保險一點先判斷一下事件對象的lengthComputable屬性
functionprogressHandler(e){
if(e.lengthComputable){
varpercent=Math.round((e.loaded/e.total)*100)
}
}
支持Ajax上傳的瀏覽器可以參考caniusehttps://caniuse.com/#feat=xhr2
分割上傳
使用文件對象的slice方法可以分割文件,給該方法傳遞兩個參數(shù),一個起始位置和一個結(jié)束位置,這會返回一個新的Blob對象,包含原文件從起始位置到結(jié)束位置的那一部分(文件File對象其實也是Blob對象,這可以通過fileinstanceofBlob確定,Blob是File的父類)
varblob=file.slice(0,1024)//文件從字節(jié)位置0到字節(jié)位置1024那1KB
將文件分割成幾個Blob對象分別上傳就能實現(xiàn)將大文件分割上傳
functionupload(file){
letformData=newFormData()
formData.append('file',file)
letxhr=newXMLHttpRequest()
xhr.open('POST','/upload/url',true)
xhr.send(formData)
}
varblob=file.slice(0,1024)
upload(blob)//上傳第一部分
varblob2=file.slice(1024,2048)
upload(blob2)//上傳第二部分
//上傳剩余部分
通常用一個循環(huán)來處理更方便
varpos=0//起始位置
varsize=1024//塊的大小
while(pos<file.size){
letblob=file.slice(pos,pos+size)//結(jié)束位置=起始位置+塊大小
upload(blob)
pos+=size//下次從結(jié)束位置開始繼續(xù)分割
}
服務(wù)器接收到分塊文件進行重新組裝的代碼就不在這里展示了
使用這種方式上傳文件會一次性發(fā)送多個HTTP請求,那么如何處理這種多個請求同時發(fā)送的情況呢?方法有很多,可以用Promise來處理,讓每次上傳都返回一個promise對象,然后用Promise.all方法來合并處理,Promise.all方法接受一個數(shù)組作為參數(shù),因此將每次上傳返回的promise對象放在一個數(shù)組中
varpromises=[]
while(pos<file.size){
letblob=file.slice(pos,pos+size)
promises.push(upload(blob))//upload應(yīng)該返回一個promise
pos+=size
}
同時改造一下upload函數(shù)使其返回一個promise
functionupload(file){
returnnewPromise((resolve,reject)=>{
letformData=newFormData()
formData.append('file',file)
letxhr=newXMLHttpRequest()
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('Uploadsuccess!')
}).catch((err)=>{
console.log(err)
})
支持文件分割的瀏覽器可以參考caniuse
判斷一下文件對象是否有該方法就能知道瀏覽器是否支持該方法,對于早期的部分版本瀏覽器需要加上對應(yīng)的瀏覽器廠商前綴
varslice=file.slice||file.webkitSlice||file.mozSlice
if(slice){
letblob=slice.call(file,0,1024)//call
upload(blob)
}else{
upload(file)//不支持分割就只能直接上傳整個文件了,或者提示文件過大
}
拖拽上傳
通過拖拽API可以實現(xiàn)拖拽文件上傳,默認情況下,拖拽一個文件到瀏覽器中,瀏覽器會嘗試打開這個文件,要使用拖拽功能需要阻止這個默認行為
document.addEventListener('dragover',function(e){
e.preventDefault()
e.stopPropagation()
})
任意指定一個元素來作為釋放拖拽的區(qū)域,給一個元素綁定drop事件
varelement=document.querySelector('label')
element.addEventListener('drop',function(e){
e.preventDefault()
e.stopPropagation()
//...
})
通過該事件對象的dataTransfer屬性獲取文件,然后上傳即可
varfile=e.dataTransfer.files[0]
upload(file)//upload函數(shù)前面已經(jīng)定義
選擇類型
給文件輸入框添加accept屬性即可指定選擇文件的類型,比如要選擇png格式的圖片,則指定其值為image/png,如果要允許選擇所有類型的圖片,就是image/*
<inputaccept="image/*"type="file">
添加capture屬性可以調(diào)用設(shè)備機能,比如capture="camera"可以調(diào)用相機拍照,不過這并不是一個標準屬性,不同設(shè)備實現(xiàn)方式也不一樣,需要注意
<inputaccept="image/*"capture="camera"type="file">
經(jīng)測iOS設(shè)備添加該屬性后只能拍照而不能從相冊選擇文件了,所以判斷一下
if(iOS){//iOS用navigator.userAgent判斷
input.removeAttribute('capture')
}
不支持的瀏覽器會自動忽略這些屬性
自定義樣式
文件輸入框在各個瀏覽器中呈現(xiàn)的樣子都不大相同,而且給input定義樣式也不是那么方便,如果有需要應(yīng)用自定義樣式,有一個技巧,可以用一個label關(guān)聯(lián)到這個文件輸入框,當(dāng)點擊這個label元素的時候就會觸發(fā)文件輸入框的點擊,打開瀏覽文件的對話框,相當(dāng)于點擊了文件輸入框一樣的效果
<labelfor="file-input"></label>
<inputid="file-input"style="clip:rect(0,0,0,0);position:absolute;"type="file">
這時就可以將原本的文件輸入框隱藏了,然后給label元素任意地應(yīng)用樣式,畢竟要給label元素應(yīng)用樣式比input方便得多
以上就是關(guān)于“用于web開發(fā)的文件上傳怎么實現(xiàn)”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對大家有幫助,若想了解更多相關(guān)的知識內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。