自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

這個文件下載問題難住了我至少三位同事

開發(fā) 前端
今天給大家分享兩個比較有用的瀏覽器行為與預期不一致的現(xiàn)象,這兩個問題其實并不是什么難題,但在工作中發(fā)現(xiàn)不少人被難住了,,在我的印象中至少有三位同事在群里問這樣的問題,上周又有同事被此現(xiàn)象困住了。

[[409885]]

 今天給大家分享兩個比較有用的瀏覽器行為與預期不一致的現(xiàn)象,這兩個問題其實并不是什么難題,但在工作中發(fā)現(xiàn)不少人被難住了,在我的印象中至少有三位同事在群里問這樣的問題,上周又有同事被此現(xiàn)象困住了,所以我覺得這應該是個共性問題,在這里分享給大家,希望對大家有幫助

現(xiàn)象一、點擊按鈕無法實現(xiàn)文件下載

前端同事反饋在瀏覽器里點擊實現(xiàn)好的「下載商品圖片」按鈕卻無法下載(預期應該下載 zip 文件)

img

但如果你在瀏覽器的地址欄里輸入此下載地址卻又能直接從瀏覽器里下載,這是為何?

我們可以打開調試工具「網絡部分」,然后點擊一下上面的「下載商品圖片」,首先看一下網絡請求是否正常。

1、 首先看請求頭,可以看出狀態(tài)碼是 200,另外還有 content-disposition 與 Content-Type 這兩個 response header

img

畫外音:Content-Type: application/octet-stream 告訴客戶端這是一個二進制文件,content-disposition 告訴客戶端這是一個需要下載的附件并告訴瀏覽器該附件默認的文件名。

2、再看此請求的 response body,是否和步驟一的 application/octet-stream 相符:

img

可以看到 response 就是一堆亂碼,即文件的二進制流表現(xiàn)形式,所以從請求來看其實是沒有問題的,文件是正常的返回的,但為啥文件卻沒有下載下來,下載下來的文件去哪里了呢,注意看上圖的另一個紅框 XHR ,它的全稱是 XMLHttpRequest,是 ajax 請求的一種表現(xiàn)形式。

ajax 本身無法觸發(fā)瀏覽器的下載功能, 它的 response 會交由 JavaScript 處理,使用 ajax 下載完成后,response 以字符串的形式存儲在內存中,那使用 ajax 就沒法下載了嗎?不是的,我們看下瀏覽器為啥能下載

我們發(fā)現(xiàn)使用瀏覽器的  GET 請求(主要以 frame 加載, a 標簽點擊觸發(fā))或 POST請求(以 form 的形式存在)是可以下載文件的,因為這是瀏覽器的內置事件,下載的 response 會交由瀏覽器自己處理,瀏覽器如果識別到是二進制流數(shù)據則下載,如果識別到是可以打開的文件,如 xml, image 等則不會下載,會以預覽的樣式存在。

那么為啥 ajax 不能默認實現(xiàn)文件下載呢,這是瀏覽器的安全策略限制的,試想如果 ajax 可以下載文件,那就意味著 ajax 可以直接與磁盤交互,這會存在嚴重的安全隱患。

根據以上分析,要使用  ajax 下載文件我們也就有思路了,既然使用 a 標簽(或  frame)的點擊事件可以觸發(fā)瀏覽器的內置下載行為,那我們在用 ajax 下載拿到 response 后,可以用 js 新建一個隱藏的 a 標簽(標簽的 href 指向文件的鏈接),執(zhí)行它的 click 事件,這樣就觸發(fā)了瀏覽器的內置下載事件,就可以下載文件了,不過需要注意的事,創(chuàng)建的 a 標簽中要添加一個 download 屬性。 。

這個 download 屬性有啥用呢,對于瀏覽器能打開的文件,例如 html,xml 等,如果你不加 download,點擊 a 標簽就不是下載了,而是打開。(注意 download 屬性目前只被火狐和谷歌兼容)

使用 ajax 來執(zhí)行下載文件的代碼示例如下: 

  1. const filename = response.headers['content-disposition'].match(  
  2.       /filename=(.*)/  
  3. )[1]  
  4. // 首先要創(chuàng)建一個 Blob 對象(表示不可變、原始數(shù)據的類文件對象)  
  5. const blob = new Blob([response.data], {type: 'application/zip'});  
  6. if (typeof window.navigator.msSaveBlob !== 'undefined') {  
  7.     // 兼容IE,window.navigator.msSaveBlob:以本地方式保存文件  
  8.     window.navigator.msSaveBlob(blob, decodeURI(filename))  
  9. } else {  
  10.     let elink = document.createElement("a"); // 創(chuàng)建一個<a>標簽  
  11.     elink.style.display = "none"; // 隱藏標簽  
  12.     elink.href = window.URL.createObjectURL(blob); // 配置href,指向本地文件的內存地址  
  13.     elink.download = filename 
  14.     elink.click();  
  15.     URL.revokeObjectURL(elink.href); // 釋放URL 對象  
  16.     document.body.removeChild(elink); // 移除<a>標簽  

現(xiàn)象二、在瀏覽器輸入圖片鏈接想預覽,結果卻變成了下載圖片

這個問題其實經由上文分析,相信你不難猜出是咋回事,我們先抓包看一下:

img

可以看到返回的 Content-Type 為 octet-stream,上文我們提到,它指任意類型的二進制流數(shù)據,一般下載文件返回的是這種類型,瀏覽器由于無法識別打開流數(shù)據,所以會下載,那為啥大多數(shù)圖片在瀏覽器上是可以預覽的呢,因為它返回的 Content-Type 是 image/png 或 image/jpeg 等瀏覽器可以直接識別打開的文件,這樣就不會執(zhí)行下載事件

總結

以上兩個問題需要我們對瀏覽器的工作機制與 HTTP 協(xié)議有一定的了解,所以基礎真的很重要啊,不然很可能你排查半天也無從下手,但如果你知道了這些原理,抓個包分析一下它們的 Content-Type,瞬間就豁然開朗了!另外對一些疑難雜癥,了解 HTTP 協(xié)議與瀏覽器的工作機制也有助于幫助你快速定位解決問題。

比如上圖的解決方案中我們通過 content-disposition 來獲取文件的名稱 

  1. const filename = response.headers['content-disposition'].match(  
  2.       /filename=(.*)/  
  3. )[1] 

但在最開始發(fā)現(xiàn)這段代碼有問題,打印日志發(fā)現(xiàn) response.headers['content-disposition'] 居然為空,可是打開瀏覽器的 network 會發(fā)現(xiàn), content-disposition 明明存在啊

img

那為啥在 reponse 的 header 里拿不到 content-disposition 呢?

一查發(fā)現(xiàn)原來還是 HTTP 協(xié)議的問題

默認情況下,header 只有七種 simple response headers (簡單響應首部)可以暴露給外部: 

  1. Cache-Control  
  2. Content-Language  
  3. Content-Length  
  4. Content-Type  
  5. Expires  
  6. Last-Modified  
  7. Pragma 

這里的暴露給外部,意思是讓客戶端(比如 Chrome)可以訪問得到,既可以在 Network 里看到,也可以在代碼里獲取到他們的值。

而 content-disposition  不在其中,所以即使服務器在協(xié)議回包里加了該字段,如下 

  1. response.setHeader("content-disposition", "attachment; filename=" + filename); 

但因沒“暴露”給外部,客戶端就「看得到,吃不到」。

而響應首部 Access-Control-Expose-Headers 就是控制“暴露”的開關,它列出了哪些首部可以作為響應的一部分暴露給外部。

所以如果想要讓客戶端可以訪問到其他的首部信息,服務器不僅要在 header 里加入該首部,還要將它們在 Access-Control-Expose-Headers 里面列出來,如下: 

  1. response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");  
  2. response.setHeader("content-disposition", "attachment; filename=" + filename); 

這樣的話 JS 的 response header 里就有 content-disposition 的值啦。 

 

責任編輯:龐桂玉 來源: 前端大全
相關推薦

2021-06-11 06:38:25

CTO瀏覽器文件

2021-06-27 21:06:47

開發(fā)循環(huán)依賴

2023-02-17 07:27:28

2011-06-16 09:28:42

惠普

2021-01-07 08:23:02

日志

2011-09-07 16:38:04

微軟ZuneWindows 7

2024-12-19 15:41:17

2020-02-13 14:32:36

解決Maven沖突問題

2018-02-01 11:20:17

技術門診

2017-02-20 17:22:55

戴爾

2011-07-09 18:04:04

激光一體機用戶體驗

2023-12-26 00:48:28

云原生公有云私有云

2023-08-29 23:28:19

2018-02-23 17:16:03

態(tài)牛

2020-08-11 10:20:26

http數(shù)據庫狀態(tài)

2020-07-03 16:30:04

區(qū)塊鏈區(qū)塊鏈技術

2022-05-27 15:13:05

AIOT

2022-03-23 08:01:04

Python語言代碼

2015-11-16 16:28:37

點贊
收藏

51CTO技術棧公眾號