成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

文件下載,竟難住了我們CTO...

開發(fā) 前端 開發(fā)工具
今天給大家分享兩個比較有用的瀏覽器行為與預期不一致的現(xiàn)象,這兩個問題其實并不是什么難題,但在工作中發(fā)現(xiàn)不少人被難住了。

今天給大家分享兩個比較有用的瀏覽器行為與預期不一致的現(xiàn)象,這兩個問題其實并不是什么難題,但在工作中發(fā)現(xiàn)不少人被難住了。

[[405136]]

圖片來自包圖網(wǎng)

在我的印象中至少有三位同事在群里問這樣的問題,上周又有同事被此現(xiàn)象困住了,所以我覺得這應該是個共性問題,在這里分享給大家,希望對大家有幫助。

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

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

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

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

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

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

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

可以看到 response 就是一堆亂碼,即文件的二進制流表現(xiàn)形式,所以從請求來看其實是沒有問題的,文件是正常的返回的,但為啥文件卻沒有下載下來。

下載下來的文件去哪里了呢,注意看上圖的另一個紅框 XHR ,它的全稱是 XMLHttpRequest,是 ajax 請求的一種表現(xiàn)形式。

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

我們發(fā)現(xiàn)使用瀏覽器的 GET 請求(主要以 frame 加載,a 標簽點擊觸發(fā))或 POST 請求(以 form 的形式存在)是可以下載文件的。

因為這是瀏覽器的內(nèi)置事件,下載的 response 會交由瀏覽器自己處理,瀏覽器如果識別到是二進制流數(shù)據(jù)則下載,如果識別到是可以打開的文件,如 xml,image 等則不會下載,會以預覽的樣式存在。

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

根據(jù)以上分析,要使用 ajax 下載文件我們也就有思路了,既然使用 a 標簽(或 frame)的點擊事件可以觸發(fā)瀏覽器的內(nèi)置下載行為。

那我們在用 ajax 下載拿到 response 后,可以用 js 新建一個隱藏的 a 標簽(標簽的 href 指向文件的鏈接),執(zhí)行它的 click 事件。

這樣就觸發(fā)了瀏覽器的內(nèi)置下載事件,就可以下載文件了,不過需要注意的事,創(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ù)據(jù)的類文件對象) 
  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,指向本地文件的內(nèi)存地址 
  13.     elink.download = filename; 
  14.     elink.click(); 
  15.     URL.revokeObjectURL(elink.href); // 釋放URL 對象 
  16.     document.body.removeChild(elink); // 移除<a>標簽 

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

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

可以看到返回的 Content-Type 為 octet-stream,上文我們提到,它指任意類型的二進制流數(shù)據(jù),一般下載文件返回的是這種類型,瀏覽器由于無法識別打開流數(shù)據(jù),所以會下載。

那為啥大多數(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 明明存在啊:

那為啥在 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 的值啦。

參考鏈接:

  1. Access-Control-Expose-Headers: http://1il58.cn/AptUz 

作者:坤哥,前獨角獸技術專家,現(xiàn)創(chuàng)業(yè)者

編輯:陶家龍

出處:碼海(ID:seaofcode)

責任編輯:武曉燕 來源: 碼海
相關推薦

2021-07-07 11:15:05

文件前端瀏覽器

2021-01-07 08:23:02

日志

2021-06-27 21:06:47

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

2023-02-17 07:27:28

2020-12-07 08:04:39

CTO中年公司

2024-12-19 15:41:17

2020-08-11 10:20:26

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

2015-02-26 09:41:50

2012-11-15 10:01:49

梁勝CloudStackCitrix

2011-09-08 13:50:51

51cto 51CTO

2022-08-18 08:41:32

RPC微服務事件驅(qū)動

2023-11-02 18:16:21

模型CTO俱樂部51CTO

2021-02-02 18:03:00

字符串面試官子序列

2017-04-05 17:59:29

思科CTO下午茶

2025-05-19 14:35:30

RustCTO項目

2018-12-26 09:44:02

分布式緩存本地緩存

2018-05-31 16:07:40

區(qū)塊鏈安全應用程序

2017-05-10 08:39:34

裝機線纜機箱

2009-03-22 10:57:48

CCNA視頻教程下載
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 午夜精品一区二区三区免费视频 | 亚洲自拍偷拍免费视频 | 欧美成人一区二区三区 | 欧美啪啪| 一区二区三区欧美在线 | 国产精品乱码一区二区三区 | 国产视频久久久 | 精品一区二区三区电影 | 激情亚洲 | 在线播放国产一区二区三区 | 国产一伦一伦一伦 | 中文字幕亚洲欧美日韩在线不卡 | 色av一区二区 | 日本超碰 | 黄色片av| 色视频网站 | аⅴ资源新版在线天堂 | 午夜久久久 | 天天操夜夜操 | 日韩一级免费观看 | 精品国产一区二区国模嫣然 | 日韩精品一区中文字幕 | 男女精品久久 | 国产精品日韩一区二区 | 亚洲美女网站 | 久久里面有精品 | 亚洲国产欧美一区二区三区久久 | 中文字幕亚洲欧美日韩在线不卡 | 91深夜福利视频 | 午夜不卡一区二区 | 91在线播 | 国产91精品网站 | 日韩精品在线看 | 亚洲午夜精品一区二区三区他趣 | 逼逼视频 | 97伦理电影网 | 午夜爱爱毛片xxxx视频免费看 | 欧美午夜精品 | 久久国产精品视频观看 | 国产一区二区在线播放视频 | 午夜久久久久久久久久一区二区 |