博客地址:https://ainyi.com/65javascript
看到標題有點懵逼,哈哈,其實是後端將文件處理成二進制流,返回到前端,前端處理這個二進制字符串,輸出文件或下載html
最近公司有個需求是用戶在點擊下載文件(==pdf==)的時候,下載地址不能暴露在接口的返回值,前端不要經過這個地址下載,容易發生泄露,不安全。因此通過討論,就在後端根據文件地址直接轉成二進制流形式,返回給前端合併,再進行下載前端
在 nodejs 中將文件轉換成二進制是比較簡單的,先經過接口獲取文件下載地址,因爲是不一樣域的地址,也就是必須經過網絡請求獲得這個文件,不能使用 ==fs.readFile== 讀取文件,可使用 ==get== 請求獲取讀寫,編碼設置成二進制 ==binary==html5
// 後端 node 所寫的接口(部分代碼) download() { let { ctx } = this // 根據傳入的參數 contractNumber,查詢獲得文件地址 data.formalPdfUrl / data.draftPdfUrl // 查詢... let url = data.formalPdfUrl || data.draftPdfUrl // 簡便寫法 // 上面是簡便寫法,至關於 // if (data.formalPdfUrl) { // url = data.formalPdfUrl // } else if (data.draftPdfUrl) { // url = data.draftPdfUrl // } let handle = this.handleFiles(url) let binaryFiles = await handle.then(data => { return data }) // 返回到前端 ctx.body = binaryFiles }, handleFiles(url) { return new Promise((resolve, reject) => { http.get(url, res => { res.setEncoding('binary') // 二進制 let files = '' res.on('data', chunk => { // 加載到內存 files += chunk }).on('end', () => { // 加載完 resolve(binaryFiles) }) }) }) }
固然也能夠在後端直接下載這個文件,而後使用 ==fs.readFile== 以 ==binary== 編碼讀取獲得,但不必下載,下載完還要刪除,畫蛇添足java
問題來了,也是坑了我一個下午的問題,如何在前端 js 中處理這個二進制流,合併成文件,供下載node
找了找,發現 html5 有個 Blob 對象,此對象在數據庫中也見過,保存龐大數據的字段,那麼在 html5 中,Blob 容許咱們能夠經過 js 直接操做二進制數據數據庫
一個 Blob 對象表示一個不可變的,原始數據的相似文件對象
Blob 表示的數據不必定是一個 JavaScript 原生格式,本質上是 js 中的一個對象,裏面能夠儲存大量的二進制編碼格式的數據後端
建立 blob 對象本質上和建立一個其餘對象的方式是同樣的,都是使用 Blob() 的構造函數來進行建立安全
構造函數接受兩個參數:
第一個參數爲一個數據序列,能夠是任意格式的值
第二個參數是一個包含兩個屬性的對象網絡
{ type: MIME 類型, endings: 決定第一個參數的數據格式,能夠取值爲 "transparent" 或者 "native" (transparent:不變,是默認值;native:按操做系統轉換) }
關於 MIME 類型的可看:http://www.w3school.com.cn/media/media_mimeref.asp
關於 Blob 對象在這篇博客不講太多說明,主要講解如何使用 Blob 對象解決二進制流轉文件的問題
代碼以下:
// 前端調用 download() { let params = { contractNumber: num } // 調用下載文件接口,實質轉成二進制流 let content = await downloadContract(params) // 拿到二進制字符串 content // 再利用 Buffer 轉爲對象 const buf = Buffer.from(content, 'binary') // 再輸入到 Blob 生成文件 let blob = new Blob([buf], {type: 'application/pdf'}); let a = document.createElement('a') // 指定生成的文件名 a.download = num + '.pdf' a.href = URL.createObjectURL(blob) document.body.appendChild(a) a.click() document.body.removeChild(a) }
獲得 Blob 對象建立的文件 url(格式相似:「blob:http://.....」),賦值到動態建立的 a 標籤的 href 屬性,設置好 download 屬性,點擊下載後移除 a 標籤
要注意的是
在 node 層沒必要使用 Buffer 處理輸出二進制對象,由於返回給前端的時候仍是二進制字符串形式,因此 node 層可直接返回二進制流字符串
在前端在調用 Blob 構造函數的時候,先利用 Buffer 將二進制字符串轉爲 Buffer 對象,再做爲 Blob 的第一個參數,指定好第二個參數的類型 type 便可
博客地址:https://ainyi.com/65