項目開發中經常會有導出數據到Excel類似的需求,或者是下載文檔的需求。最簡單的下載方式是直接請求服務端文件地址,通過瀏覽器http實現文件下載。但是開發中,由于項目需求,你要下載的文件地址不會暴露給用戶,而且需要鑒權才允許下載文件,這個時候我們該怎么處理呢?
應用場景
文件地址沒有暴露在公網,無法通過文件url直接下載文件。
要下載的文件內容可能是根據用戶請求動態生成的,如導出Excel數據表。
后端需要鑒權驗證用戶提交的下載請求。
實現流程
前端發送get/post請求,攜帶header信息(如token用于鑒權),后端接收請求,完成鑒權后,讀取對應的文件,將文件以文件流的形式發送給瀏覽器,瀏覽器完成下載。
我們把這種方式叫做Blob方式下載。
Blob 對象表示一個不可變、原始數據的類文件對象。Blob 表示的不一定是JavaScript原生格式的數據。
前端代碼
我們以下載圖片為例,使用axios來做前端異步下載請求。
get方式請求中,我們要在header中攜帶token信息,這個token就是你在系統中的通行證,一般是你在登錄的時候后端給你的token,相當于一張游樂場的門票,有了這張門票,你可以到游樂場里游玩任意項目,只是在游玩時給工作人員出示這個token門票就可以了。
并且還要告訴后端,需要后端返回blob類型的數據,使用responseType: 'blob'。
axios.get('http://localhost:9998/download.php', {
headers: {
'token': '1234512345'
},
responseType: 'blob'
}).then((res) => {
if (res.data.type !== 'application/octet-stream') {
alert('下載失敗');
return false;
}
const blob = new Blob([res.data], {
type: 'image/jpeg'
})
let a = document.createElement("a");
let objUrl = URL.createObjectURL(blob);
a.href = objUrl;
a.download = 'abc.jpg' //文件名
a.click();
URL.revokeObjectURL(objUrl); // 釋放內存
document.body.removeChild(a);
alert('下載成功');
}).catch((err) => {
console.log(err);
alert('下載失敗');
});
我們拿到后端返回的blob對象數據后,在頁面上創建一個a標簽,然后模擬點擊事件,將blob數據保存成文件。注意URL.createObjectURL(blob)將blob保存在內存中,下載完后記得釋放內存哦。
后端代碼
在后端download.php中,先要在header中允許接收token,如果是跨域請求那就應該還要設置header("Access-Control-Allow-Origin: *");允許跨域請求。
header("Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With, token");
$token = isset($_SERVER['HTTP_TOKEN']) ? $_SERVER['HTTP_TOKEN'] : '';
if ($token != '1234512345') {
echo 'error.';
exit;
}
$file = '../file/cc.jpg';
if (!file_exists($file)) {
header('HTTP/1.1 404 Not Found');
exit;
}
$fileSize = filesize($file);
//下載文件需要用到的頭
header("Content-type: application/octet-stream");
header("Accept-Ranges: bytes");
header("Accept-Length:".$fileSize);
$fp = fopen($file, "rb");
$buffer = 1024;
$fileCount = 0;
//向瀏覽器發送數據
while(!feof($fp) && $fileCount < $fileSize){
$cont = fread($fp, $buffer);
$fileCount += $buffer;
echo $cont;
}
fclose($fp);
接著就是驗證token是否正確,上述代碼中的驗證過程是偽代碼,實際開發中應該根據業務需求,按照一定的算法驗證token。token里面可能含有用戶信息和過期時間等數據。
然后判斷要下載的文件是否存在,這個文件可能不在web目錄下,用戶無法直接通過url訪問。
最后就是讀取文件流,發送給瀏覽器。