前端下載文件與讀取文件內容(多種類型的文件)

寫在前面

在實際開發過程當中常常會碰到用戶要下載或者導出一個文件的需求。傳統的作法是在後端存儲或者即時生成一個文件來提供下載功能,這樣的優點是能夠作權限控制、方便數據二次處理,但缺點是須要額外發起請求、增大服務端壓力、下載速度慢。但隨着HTML5的標準發佈,我大前端已經徹底能夠獨立實現文件下載與導出啦~javascript

利用a標籤的 download 屬性下載文件

download屬性指示瀏覽器下載 URL而不是導航到它,所以將提示用戶將其保存爲本地文件。若是屬性有一個值,那麼此值將在下載保存過程當中做爲預填充的文件名(若是用戶須要,仍然能夠更改文件名)。此屬性對容許的值沒有限制,可是 / 和 \ 會被轉換爲下劃線。大多數文件系統限制了文件名中的標點符號,故此,瀏覽器將相應地調整建議的文件名。html

<a download="文件名" href="文件地址">下載測試</a>
複製代碼

須要注意的是:前端

  1. download 僅適用於同源 URL,可是可使用 blob: URL 和 data: URL。
  2. 若是 HTTP 頭中的 Content-Disposition 屬性賦予了一個不一樣於此屬性的文件名,HTTP 頭屬性優先於此屬性。
  3. 若是 HTTP 頭屬性 Content-Disposition 被設置爲inline 即Content-Disposition='inline',那麼 Firefox 優先考慮 HTTP 頭 Content-Disposition download 屬性。

生成Data URLs 並下載文件

Data URL 即前綴爲 data: 協議的URL,其容許內容建立者向文檔中嵌入小文件。它 由四個部分組成:前綴(data:)、指示數據類型的MIME類型、若是非文本則爲可選的base64標記、數據自己。java

data:[<mediatype>][;base64],<data>

mediatype 是個 MIME 類型的字符串
例如 "image/jpeg" 表示 JPEG 圖像文件。
若是被省略,則默認值爲 text/plain;charset=US-ASCII
若是數據是文本類型,你能夠直接將文本嵌入 
若是是二進制數據,你能夠將數據進行base64編碼以後再進行嵌入。
複製代碼

導出文件代碼示例:git

//導出Json文件
exportJson(){
    const downloadData = {
        name:"April",
        ager:"18",
        hobby:"學習"
    };
    let dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(downloadData));
    let downloadAnchorNode = document.createElement('a')
    downloadAnchorNode.setAttribute("href", dataStr);
    downloadAnchorNode.setAttribute("download", "文件名.json")
    downloadAnchorNode.click();
    downloadAnchorNode.remove();
},
複製代碼

生成blob: URL 並下載文件

Blob()構造函數返回一個新的 Blob 對象。 blob的內容由參數數組中給出的值的串聯組成。github

let aBlob = new Blob( array, options );
複製代碼

array 是一個由ArrayBuffer, ArrayBufferView, Blob, DOMString 等對象構成的 Array ,或者其餘相似對象的混合體,它將會被放進 Blob。DOMStrings會被編碼爲UTF-8。npm

options 是一個可選的BlobPropertyBag字典,它可能會指定以下兩個屬性:json

  1. type,默認值爲 "",它表明了將會被放入到blob中的數組內容的MIME類型。
  2. endings,默認值爲"transparent",用於指定包含行結束符\n的字符串如何被寫入。 它是如下兩個值中的一個: "native",表明行結束符會被更改成適合宿主操做系統文件系統的換行符,或者 "transparent",表明會保持blob中保存的結束符不變 。

URL.createObjectURL() 靜態方法會建立一個 DOMString,其中包含一個表示參數中給出的對象的URL。這個 URL 的生命週期和建立它的窗口中的 document 綁定。這個新的URL 對象表示指定的 File 對象或 Blob 對象。後端

objectURL = URL.createObjectURL(object);
複製代碼

導出文件代碼示例:數組

exportJson() {
    const downloadData = {
        name: "April",
        ager: "18",
        hobby: "學習"
    };
                
    let blob = new Blob(
        [JSON.stringify(downloadData, null, 2)],
        {type: 'application/json'});
    let url = URL.createObjectURL(blob);
    let downloadAnchorNode = document.createElement('a')
    downloadAnchorNode.setAttribute("href", url);
    downloadAnchorNode.setAttribute("download", "文件名.json")
    downloadAnchorNode.click();
    downloadAnchorNode.remove();
 }
複製代碼

點擊下載圖片

雖然目前瀏覽器都支持保存圖片到本地的功能(右鍵>圖片另存爲)可是實際開發中會涉及到批量下載圖片、Canvas繪圖的保存功能,應運上面的知識,我大前端也能夠輕鬆實現。代碼以下:

<button @click="downloadImg">下載圖片</button>
複製代碼
// 經過src獲取圖片的blob對象
getImageBlob(url, cb) {
    let xhr = new XMLHttpRequest();
    xhr.open("get", url, true);
    xhr.responseType = "blob";
    xhr.onload = function () {
        if (this.status == 200) {
            cb(this.response);
        }
    };
    xhr.send();
},
// 點擊下載圖片
downloadImg(){
    let reader = new FileReader();
    this.getImageBlob('https://b-gold-cdn.xitu.io/v3/static/img/simplify-logo.3e3c253.svg', function(blob){
        // 讀取來看下下載的內容 最終生成的字符串
        reader.readAsDataURL(blob);
        // 生成下載用的URL對象
        let url = URL.createObjectURL(blob);
        // 生成一個a標籤,並模擬點擊,便可下載,批量下載同理
        let downloadAnchorNode = document.createElement('a')
        downloadAnchorNode.setAttribute("href", url);
        downloadAnchorNode.setAttribute("download", "下載圖片")
        downloadAnchorNode.click();
        downloadAnchorNode.remove();
    })
},
複製代碼

下載表格

推薦由SheetJS出品的js-xlsx是一款很是方便的只須要純JS便可讀取和導出excel的工具庫,功能強大,支持格式衆多,支持xls、xlsx、ods(一種OpenOffice專有表格文件格式)等十幾種格式。本文所有都是以xlsx格式爲例。 官方github:github.com/SheetJS/js-…

// 測試用的數據
staff: [
    {name: "April", job: "programmer", age: "18", hobby: "study"},
    {name: "Shawn", job: "student", age: "8", hobby: "study"},
    {name: "Leo", job: "teacher", age: "28", hobby: "play"},
    {name: "Todd", job: "programmer", age: "19", hobby: "sleep"},
    {name: "Scoot", job: "cook", age: "38", hobby: "paintting"},
]
複製代碼

安裝xlsx

npm install xlsx --save
複製代碼
import XLSX from 'xlsx';
複製代碼
auto_width(ws, data) {
    /*set worksheet max width per col*/
    const colWidth = data.map(row => row.map(val => {
        /*if null/undefined*/
        if (val == null) {
            return {'wch': 10};
        }
        /*if chinese*/
        else if (val.toString().charCodeAt(0) > 255) {
            return {'wch': val.toString().length * 2};
        } else {
            return {'wch': val.toString().length};
        }
    }))
    /*start in the first row*/
    let result = colWidth[0];
    for (let i = 1; i < colWidth.length; i++) {
        for (let j = 0; j < colWidth[i].length; j++) {
            if (result[j]['wch'] < colWidth[i][j]['wch']) {
                result[j]['wch'] = colWidth[i][j]['wch'];
            }
        }
    }
    ws['!cols'] = result;
},
json_to_array(key, jsonData) {
    return jsonData.map(v => key.map(j => {
        return v[j]
    }));
},
export_array_to_excel({key, data, title, filename, autoWidth}) {
    const wb = XLSX.utils.book_new();
    const arr = this.json_to_array(key, data);
    arr.unshift(title);
    const ws = XLSX.utils.aoa_to_sheet(arr);
    if (autoWidth) {
        this.auto_width(ws, arr);
    }
    XLSX.utils.book_append_sheet(wb, ws, filename);
    XLSX.writeFile(wb, filename + '.xlsx');
},
exportExcel() {
    let staff = this.staff;
    const params = {
        title: ['姓名', '工做', '年齡', '愛好'],
        key: ['name', 'job', 'age', 'hobby'],
        data: staff,
        autoWidth: true,
        filename: '文件名'
    }
    this.export_array_to_excel(params)
},
複製代碼

讀取上傳文件的數據

想要讀取Blob數據的惟一方法是FileReader。 FileReader 對象容許Web應用程序異步讀取存儲在用戶計算機上的文件(或原始數據緩衝區)的內容,使用 FileBlob 對象指定要讀取的文件或數據。

其中File對象能夠是來自用戶在一個<input>元素上選擇文件後返回的FileList對象,也能夠來自拖放操做生成的 DataTransfer對象,還能夠是來自在一個HTMLCanvasElement上執行mozGetAsFile()方法後返回結果。

包含5個方法:

  1. FileReader.abort() 停止讀取操做。在返回時,readyState屬性爲DONE。

  2. FileReader.readAsArrayBuffer() 開始讀取指定的 Blob中的內容, 一旦完成, result 屬性中保存的將是被讀取文件的 ArrayBuffer 數據對象.

  3. FileReader.readAsBinaryString() 開始讀取指定的Blob中的內容。一旦完成,result屬性中將包含所讀取文件的原始二進制數據。

  4. FileReader.readAsDataURL() 開始讀取指定的Blob中的內容。一旦完成,result屬性中將包含一個data: URL格式的字符串以表示所讀取文件的內容。

  5. FileReader.readAsText() 開始讀取指定的Blob中的內容。一旦完成,result屬性中將包含一個字符串以表示所讀取的文件內容。

將上傳的文件讀取爲字符串的代碼示例

handleUpload(blob) {
    // 新建一個FileReader
    const reader = new FileReader()
    // 讀取文件
    reader.readAsText(blob, "UTF-8")
    // 讀取完文件以後會回來這裏
    reader.onload = function (e) {
        // 讀取文件內容
        const fileString = e.target.result
        // 接下來可對文件內容進行處理
        const myData = JSON.parse(fileString);
        console.log(myData) // 打印讀取到的內容
     }
},

複製代碼

將上傳的文件讀取爲URL格式的字符串的代碼示例

handleUpload(file) {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = function (e) {
        const urlStr = reader.result
        console.log(urlStr)
    }
}
複製代碼

讀取上傳Excel文件

一樣推薦由SheetJS出品的js-xlsx

import XLSX from 'xlsx';
複製代碼
handleUpload(file) {
    let that = this;
    var reader = new FileReader();
    reader.readAsBinaryString(file);
    reader.onload = function (evt) {
      var data = evt.target.result;
      var workbook = XLSX.read(data, {
        type: 'binary'
      }) // 以二進制流方式讀取獲得整份excel表格對象
    }
    reader.onload = function (evt) {
      //當讀取完成後回調這個函數,而後此時文件的內容存儲到了result中,直接操做便可
      try {
        var data = evt.target.result,
          workbook = XLSX.read(data, {
            type: 'binary'
          }), // 以二進制流方式讀取獲得整份excel表格對象
          buildings = []; // 存儲獲取到的數據
        var fromTo = '';
        // 遍歷每張表讀取
        for (var sheet in workbook.Sheets) {
          if (workbook.Sheets.hasOwnProperty(sheet)) {
            fromTo = workbook.Sheets[sheet]['!ref'];
            buildings = buildings.concat(XLSX.utils.sheet_to_json(workbook.Sheets[sheet]));
          }
        }
        console.log(buildings) // 文件內容
      } catch (e) {
        console.log('文件類型不正確', e);
        return;
      }
    }
    return false;
},
複製代碼
相關文章
相關標籤/搜索