通過(guò)JavaScript下載文件到本地的方法(單文件)
最近在做一個(gè)文件下載的功能,這里把做的過(guò)程中用的技術(shù)和坑簡(jiǎn)要總結(jié)下。
1. 單文件下載(a標(biāo)簽)
同源單文件
針對(duì)單文件的情況下,同源的文件,可以通過(guò) < a> 標(biāo)簽的 download 屬性下載文件
const elt = document.createElement('a');
elt.setAttribute('href', url);
elt.setAttribute('download', 'file.png');
elt.style.display = 'none';
document.body.appendChild(elt);
elt.click();
document.body.removeChild(elt);
但是這個(gè)方案并不適用于非同源的資源,此時(shí)它相當(dāng)于普通的超鏈接,點(diǎn)擊會(huì)跳轉(zhuǎn)到資源頁(yè)面,而不是下載。
非同源圖片
如果不存在CORS問(wèn)題, 可以借助Blob實(shí)現(xiàn)下載(構(gòu)造xhr請(qǐng)求文件地址, 以Blob的形式接收Response):
function downloadWithBlob(url) {
fetch(url).then(res => res.blob().then(blob => {
var a = document.createElement('a');
var url = window.URL.createObjectURL(blob);
var filename = 'file.png';
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
}));
}
如果存在CORS問(wèn)題,可以考慮使用 canvas 將圖片轉(zhuǎn)換成 base64 編碼之后再通過(guò) 標(biāo)簽的 download 屬性下載
function downloadPic(url) {
const img = new Image;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
img.onload = function() {
canvas.width = this.width;
canvas.height = this.height;
ctx.drawImage(this, 0, 0);
const elt = document.createElement('a');
elt.setAttribute('href', canvas.toDataURL('image/png'));
elt.setAttribute('download', 'file.png');
elt.style.display = 'none';
document.body.appendChild(elt);
elt.click();
document.body.removeChild(elt);
};
img.crossOrigin = 'anonymous';
img.src = url;
}
2. 單文件下載(iframe)
iframe方式是在頁(yè)面內(nèi)隱藏iframe, 然后將下載地址加載到iframe中, 從而觸發(fā)瀏覽器的下載行為
const iframe = document.createElement('iframe');
iframe.src = url;
iframe.style.display = 'none';
document.body.appendChild(iframe);
但是這里發(fā)現(xiàn),即使是同域的圖片,也無(wú)法完成下載,這是為啥呢?
這里就有個(gè)上面的a鏈接下載沒(méi)有提到的問(wèn)題:什么樣的鏈接才能觸發(fā)瀏覽器的下載:
url如何觸發(fā)瀏覽器自動(dòng)下載
一個(gè)url能否觸發(fā)瀏覽器自動(dòng)下載,主要看該請(qǐng)求響應(yīng)頭response header是否滿足,一般是看Content-Disposition和Content-Type這兩個(gè)消息頭:
- response header中指定了Content-Disposition為attachment,它表示讓瀏覽器把消息體以附件的形式下載并保存到本地 (一般還會(huì)指定filename, 下載的文件名默認(rèn)就是filename)
- response header中指定了Content-Type 為 application/octet-stream(無(wú)類(lèi)型) 或 application/zip(zip包時(shí))等等。(其中 application/octet-stream表示http response為二進(jìn)制流(沒(méi)指定明確的type), 用在未知的應(yīng)用程序文件,瀏覽器一般不會(huì)自動(dòng)執(zhí)行或詢問(wèn)執(zhí)行。瀏覽器會(huì)像對(duì)待 設(shè)置了HTTP頭Content-Disposition 值為 attachment 的文件一樣來(lái)對(duì)待這類(lèi)文件)
只要url滿足上述觸發(fā)的要求,那么都可以通過(guò)iframe的形式來(lái)下載
3. 代理服務(wù)處理下載
如果后端自己也能控制的話,或者后端能配合的話,可以寫(xiě)一個(gè)代理服務(wù),在后端去請(qǐng)求文件數(shù)據(jù),然后設(shè)置好相應(yīng)的response header, 然后前端請(qǐng)求代理服務(wù)來(lái)做下載。
前端(假設(shè)代理服務(wù)接口是http://exampale.com/download):
const downloadUrl = 'http://exampale.com/download?url=' + encodeURIComponent(url) + '&name=xxx';
const elt = document.createElement('a');
elt.setAttribute('href', downloadUrl);
elt.setAttribute('download', 'file.png');
...
后端
const url = decodeURIComponent(req.query.url);
http.get(url, (response) => {
res.setHeader('Content-disposition', 'attachment;filename=' + req.query.name);
res.setHeader('Content-type', 'application/octet-stream');
response.pipe(res);
});
單文件的處理先寫(xiě)到這里,多文件的下載下篇在寫(xiě)。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- javascript使用Blob對(duì)象實(shí)現(xiàn)的下載文件操作示例
- 原生js實(shí)現(xiàn)文件上傳、下載、封裝等實(shí)例方法
- JavaScript實(shí)現(xiàn)文件下載并重命名代碼實(shí)例
- php+js實(shí)現(xiàn)的無(wú)刷新下載文件功能示例
- js使用文件流下載csv文件的實(shí)現(xiàn)方法
- Jsp+Servlet實(shí)現(xiàn)文件上傳下載 刪除上傳文件(三)
- Jsp+Servlet實(shí)現(xiàn)文件上傳下載 文件列表展示(二)
- Jsp+Servlet實(shí)現(xiàn)文件上傳下載 文件上傳(一)
- JSP servlet實(shí)現(xiàn)文件上傳下載和刪除
- JavaScript實(shí)現(xiàn)多文件下載方法解析
相關(guān)文章
Bootstrap 3 進(jìn)度條的實(shí)現(xiàn)
這篇文章主要介紹了Bootstrap 3 進(jìn)度條的實(shí)現(xiàn),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-02-02
標(biāo)題過(guò)長(zhǎng)使用javascript按字節(jié)截取字符串
在網(wǎng)頁(yè)展示中經(jīng)常會(huì)碰到,標(biāo)題過(guò)長(zhǎng),需要截取字符串,用CSS的實(shí)現(xiàn)的話各種兼容問(wèn)題,下面為大家介紹下javascript如何按字節(jié)截取字符串2014-04-04
JS實(shí)現(xiàn)仿UC瀏覽器前進(jìn)后退效果的實(shí)例代碼
這篇文章主要介紹了JS實(shí)現(xiàn)仿UC瀏覽器前進(jìn)后退效果的實(shí)例代碼,實(shí)現(xiàn)此功能前需要先測(cè)試下瀏覽器,具體實(shí)例代碼,大家參考下本文2017-07-07
使用element-ui的upload組件上傳代碼包時(shí)遇到的問(wèn)題小結(jié)
這篇文章主要介紹了使用element-ui的upload組件上傳代碼包時(shí)遇到的問(wèn)題及總結(jié),本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-12-12
淺談js控制li標(biāo)簽排序問(wèn)題 js調(diào)用php函數(shù)的方法
下面小編就為大家?guī)?lái)一篇淺談js控制li標(biāo)簽排序問(wèn)題 js調(diào)用php函數(shù)的方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-10-10
使用javascript函數(shù)編寫(xiě)簡(jiǎn)單銀行取錢(qián)存錢(qián)流程
本文通過(guò)實(shí)例代碼給大家講解了使用javascript函數(shù)編寫(xiě)簡(jiǎn)單銀行取錢(qián)存錢(qián)流程,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2018-05-05
JavaScript操作選擇對(duì)象的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)?lái)一篇JavaScript操作選擇對(duì)象的簡(jiǎn)單實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考,一起跟隨小編過(guò)來(lái)看看吧2016-05-05

