angular download file

如何使用 Angular 進行檔案下載

黃紀豪 2018/12/24 15:15:17
5122

如何使用 Angular 進行檔案下載


簡介

當我們遇到需要從後端進行檔案下載時, 因回傳的檔案格式內容常是Binary 資料格式, 此文章說明如何使用 Angular 將後端 Binary 資料格式轉換成檔案格式進行下載動作

作者

黃紀豪


Angular2 要透過 http post 方法請求下載二進位的檔案文件時, 此時後台返回的檔頭如下

 
並且返回的Response 內的資料如下
 
 
Angular框架下我們要如何將其資料轉換成檔案格式進行下載
 

取得完整的HTTP Response

首先, 由於檔案名稱是放在response 內的檔頭中, 所以我們在取得response 資料不只要回傳 body,
還需要 http header的資料, 因此這時候我們需要將 observe的值設為 response. 
 
接收非JSON格式的資料
另外, 由於我們接收的是二進位資料, 此資料被儲存為 Blob 格式, angular預設值是json, 所以我們需要修改responseType,
將預期會接收到的資料格式設定為『blob, 其中BlobBinary Large Object)類型的物件是一個不可變的原始資料(Immutable Raw Data),
類似檔案的二進制資料,通常像是一些圖片、音訊或者是可執行的二進制程式碼也可以被儲存為 Blob
實際下載檔案的程式碼如下:
downloadPdfPost(paramUrl: string, paramObject: any):  Observable<any> {
    return this.http.post(paramUrl, paramObject, {headers: this.getHttpHeader(), responseType: 'blob', observe: 'response'})
    .map(res => {
      return {
        filename: this.getPdfFileName(res.headers.get('Content-Disposition')),
        data: res.body
      };
    })
    .catch(err => {
      if (err instanceof HttpErrorResponse) {
        return _throw(this.error(err));
      }
    });
  }
 

我們將收到的Response 轉換成 {filename: xxx, data: xxx} 結構傳回 component 處理, 在這邊我們還要特別處理 filename 的格式, 其中 filename 的解析說明如下

我們需要將後面那段base64的編碼解碼成字串, 並將此字串轉換成 utf-8格式,這樣才能正確的讀取到檔案的名稱,程式碼如下

  private getPdfFileName(paramFileName: string): string {
    const filename_token = paramFileName.split(';')[1].trim().split('filename=')[1];
    const token : string[] = filename_token.split('?');
    let filename;
    if(token.length >3) {
      filename = (new Buffer(token[3], 'base64')).toString(token[1]);
    } else {
      filename = filename_token;
    }
    return filename;

  }

 

最後,在component 內收到資料後, 可透過 a 標籤的形式來下載檔案, 方法主要新建一個帶下載地址的 a 標籤,然後被動觸發點擊,

結束後釋放url 就可以正確下載檔案了, 完整程式碼參考如下:

downloadPdf() {
    this._service.downloadPdf(this.seq).subscribe(res => {
      // console.log('start download:',res);
      const url = window.URL.createObjectURL(res.data);
      const a = document.createElement('a');
      document.body.appendChild(a);
      a.setAttribute('style', 'display: none');
      a.href = url;
      a.download = res.filename;
      a.click();
      window.URL.revokeObjectURL(url);
      a.remove(); // remove the element
    }, error => {
      this._coreService.setAlter(TypeAlter.Error, '列印', error.detail);
    }, () => {
    });
  }
黃紀豪