在前端站點上下載文件,這是一個極其廣泛的需求,很早前就已經有各類解決方法了,爲何還寫這麼老的文章,只是最近在帶一個新人,他彷佛不少都只知其一;不知其二,也遇到了咱們必經問題之「不能下載txt、png等文件」的典型問題,我就給他總結下下載的幾個方式。順便分享出來,也許,真有人須要。前端
這是之前常使用的傳統方式,畢竟那個年代,沒那麼多好用的新特性呀。ajax
道理也很簡單,爲一個下載按鈕添加click
事件,點擊時動態生成一個表單,利用表單提交的功能來實現文件的下載(實際上表單的提交就是發送一個請求)後端
來看下如何生成一個表單,生成怎麼樣的一個表單:api
/** * 下載文件 * @param {String} path - 請求的地址 * @param {String} fileName - 文件名 */
function downloadFile (downloadUrl, fileName) {
// 建立表單
const formObj = document.createElement('form');
formObj.action = downloadUrl;
formObj.method = 'get';
formObj.style.display = 'none';
// 建立input,主要是起傳參做用
const formItem = document.createElement('input');
formItem.value = fileName; // 傳參的值
formItem.name = 'fileName'; // 傳參的字段名
// 插入到網頁中
formObj.appendChild(formItem);
document.body.appendChild(formObj);
formObj.submit(); // 發送請求
document.body.removeChild(formObj); // 發送完清除掉
}
複製代碼
最簡單最直接的方式,實際上跟a
標籤訪問下載連接同樣跨域
window.open('downloadFile.zip');
location.href = 'downloadFile.zip';
複製代碼
固然地址也能夠是接口api的地址,而不單純是個連接地址。瀏覽器
咱們知道,a
標籤能夠訪問下載文件的地址,瀏覽器幫助進行下載。可是對於瀏覽器支持直接瀏覽的txt、png、jpg、gif等文件,是不提供直接下載(可右擊從菜單裏另存爲)的。bash
爲了解決這個直接瀏覽不下載的問題,能夠利用download
屬性。app
download
屬性是HTML5新增的屬性,兼容性能夠了解下 can i use downloadui
整體兼容性算是很好了,基本能夠區分爲IE和其餘瀏覽。可是須要注意一些信息:this
基於上面描述,若是你嘗試下載跨域連接,那麼其實download
的效果就會沒了,跟不設置download
表現一致。即瀏覽器能預覽的仍是會預覽,而不是下載。
簡單用法:
<a href="example.jpg" download>點擊下載</a>
複製代碼
能夠帶上屬性值,指定下載的文件名,即重命名下載文件。不設置的話默認是文件本來名。
<a href="example.jpg" download="test">點擊下載</a>
複製代碼
如上,會下載了一個名叫test
的圖片
監測是否支持download
要知道瀏覽器是否支持download
屬性,簡單的一句代碼便可區分
const isSupport = 'download' in document.createElement('a');
複製代碼
對於在跨域下不能下載可瀏覽的文件,其實能夠跟後端協商好,在後端層作多一層轉發,最終返回給前端的文件連接跟下載頁同域就行了。
該方法較上面的直接使用a
標籤download
這種方法的優點在於,它除了能利用已知文件地址路徑進行下載外,還能經過發送ajax請求api獲取文件流進行下載。畢竟有些時候,後端不會直接提供一個下載地址給你直接訪問,而是要調取api。
利用Blob
對象能夠將文件流轉化成Blob
二進制對象。該對象兼容性良好,須要注意的是
Blob Url
或Object URL
當前是有缺陷的,以下文中經過URL.createObjectURL
生成的連接。caniuse
官網有指出Safari has a serious issue with blobs that are of the type application/octet-stream
進行下載的思路很簡單:發請求獲取二進制數據,轉化爲Blob
對象,利用URL.createObjectUrl
生成url地址,賦值在a
標籤的href
屬性上,結合download
進行下載。
/** * 下載文件 * @param {String} path - 下載地址/下載請求地址。 * @param {String} name - 下載文件的名字/重命名(考慮到兼容性問題,最好加上後綴名) */
downloadFile (path, name) {
const xhr = new XMLHttpRequest();
xhr.open('get', path);
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function () {
if (this.status === 200 || this.status === 304) {
// const blob = new Blob([this.response], { type: xhr.getResponseHeader('Content-Type') });
// const url = URL.createObjectURL(blob);
const url = URL.createObjectURL(this.response);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
};
}
複製代碼
該方法不能缺乏a
標籤的download
屬性的設置。由於發請求時已設置返回數據類型爲Blob
類型(xhr.responseType = 'blob'
),因此target.response
就是一個Blob
對象,打印出來會看到兩個屬性size
和type
。雖然type
屬性已指定了文件的類型,可是爲了穩妥起見,仍是在download
屬性值裏指定後綴名,如Firefox不指定下載下來的文件就會不識別類型。
你們可能會注意到,上述代碼有兩處註釋,其實除了上述的寫法外,還有另外一個寫法,改動一丟丟。若是發送請求時不設置xhr.responseType = 'blob'
,默認ajax請求會返回DOMString
類型的數據,即字符串。這時就須要兩處註釋的代碼了,對返回的文本轉化爲Blob
對象,而後建立blob url,此時須要註釋掉本來的const url = URL.createObjectURL(target.response)
。
這裏的用法跟上面用Blob
大同小異,基本上思路是同樣的,惟一不一樣的是,上面是利用Blob
對象生成Blob URL
,而這裏則是生成Data URL
,所謂Data URL
,就是base64
編碼後的url形式。
/** * 下載文件 * @param {String} path - 下載地址/下載請求地址。 * @param {String} name - 下載文件的名字(考慮到兼容性問題,最好加上後綴名) */
downloadFile (path, name) {
const xhr = new XMLHttpRequest();
xhr.open('get', path);
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function () {
if (this.status === 200 || this.status === 304) {
const fileReader = new FileReader();
fileReader.readAsDataURL(this.response);
fileReader.onload = function () {
const a = document.createElement('a');
a.style.display = 'none';
a.href = this.result;
a.download = name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
}
};
}
複製代碼
這裏額外提供個方法,該方法做用是,當你知道文件的全名(含後綴名),想要重命名,可是得後綴名同樣,來獲取後綴名。
function findType (name) {
const index = name.lastIndexOf('.');
return name.substring(index + 1);
}
複製代碼
未經容許,請勿私自轉載