前端從web服務(wù)器或者CDN下載資源總結(jié)
點擊上方?
程序員成長指北
,關(guān)注公眾號
回復(fù) 1 ,加入高級Node交流群
前段時間聽到前端同學(xué)說前端拿到資源的 CDN 鏈接后可以直接從 CDN 下載資源,不需要經(jīng)過后端,感覺很神奇,但是一直不明白是怎么實現(xiàn)的,前兩天整理了下關(guān)于" CDN 和對象存儲的知識 "(原文見此文參考接最后一條),今天搜索學(xué)習(xí)了下前端直接下載資源的方式,特此記錄。
目前前端直接下載 web 服務(wù)器或者 CDN 靜態(tài)資源的方式有兩種,一個是利用 <a> 標(biāo)簽,另一個是通過 window.open() 函數(shù)。
一、利用
<a>標(biāo)簽
<a> 標(biāo)簽就是 html 中的超鏈接標(biāo)簽,但是用過這個標(biāo)簽的同學(xué)應(yīng)該都有這種印象,當(dāng)超鏈接鏈接的內(nèi)容是圖片、視頻或者 pdf 時,點擊超鏈接往往會在瀏覽器的新標(biāo)簽頁打開對應(yīng)的圖片、視頻或者 pdf,而不會開始下載。但是其他像壓縮包這樣的資源,也就是瀏覽器沒辦法直接打開的資源則會直接開始下載。那怎么樣讓所有的文件都默認下載而不是打開呢?
html5 給 <a> 標(biāo)簽增加了一個 download 屬性,當(dāng) <a> 標(biāo)簽帶上了 download 屬性時,點擊超鏈接則會被瀏覽器解析為下載而不是打開。
下面我們來實操一下:
<a> 標(biāo)簽未添加 download 屬性
比如這個是不帶 download 屬性的 <a> 標(biāo)簽案例。
<!DOCTYPE?html>
<html>
????<head>
????????<meta?charset="utf-8"?/>
????????<title></title>
????</head>
????<body>
????????<img?src?=?"img/books/book1.jpg"?alt?=?"日俄海戰(zhàn)"/>
????????<a?href?=?"img/books/book1.jpg">點擊下載圖片</a>
????</body>
</html>
用瀏覽器打開 html 文件后,點擊超鏈接

在新標(biāo)簽頁中打開了圖片,并沒有下載。

<a> 標(biāo)簽添加 download 屬性后
給 <a> 標(biāo)簽添加 download 屬性后,再試一次:
<!DOCTYPE?html>
<html>
????<head>
????????<meta?charset="utf-8"?/>
????????<title></title>
????</head>
????<body>
????????<img?src?=?"img/books/book1.jpg"?alt?=?"日俄海戰(zhàn)"/>
????????<a?href?=?"img/books/book1.jpg"?download="日俄海戰(zhàn).jpg">點擊下載圖片</a>
????</body>
</html>
用瀏覽器打開 html 文件后,點擊超鏈接,彈出了路徑選擇窗口,點擊保存,圖片完成下載。
換成網(wǎng)絡(luò)圖片再試一次
讓我們把 <img> 標(biāo)簽和 <a> 標(biāo)簽的路徑換成一張網(wǎng)絡(luò)圖片,而非本地圖片,
<!DOCTYPE?html>
<html>
????<head>
????????<meta?charset="utf-8"?/>
????????<!--解決img標(biāo)簽不能展示網(wǎng)絡(luò)圖片的問題-->
????????<meta?name="referrer"?content="no-referrer">
????????<title></title>
????</head>
????<body>
????????<img?src?=?"https://img2020.cnblogs.com/blog/1456655/202110/1456655-20211004112059587-1817640282.png"?alt?=?"cdn和對象存儲"/>
????????<a?href?=?"https://img2020.cnblogs.com/blog/1456655/202110/1456655-20211004112059587-1817640282.png"?download="cdn和對象存儲.png">點擊下載圖片</a>
????</body>
</html>
點擊下載的超鏈接,會發(fā)現(xiàn)并沒有開始下載,而是在新標(biāo)簽頁打開了圖片。

注意:如果你的網(wǎng)絡(luò)圖片渲染不出來,嘗試在標(biāo)簽內(nèi)添加 <meta name="referrer" content="no-referrer">
沒有開始下載原因
很可能是瀏覽器的同源策略導(dǎo)致 download 屬性失效造成的。同源策略是瀏覽器的一種安全策略,所謂的同源指的是 URL 地址里面的協(xié)議、域名和端口號均相同,如果一個站點開啟了同源策略,也稱為禁止跨域訪問,這時,這個站點的資源只允許在該站點內(nèi)部跳轉(zhuǎn)和訪問,不允許被第三方站點訪問和跳轉(zhuǎn)。我們這個案例中由于源 cnblogs 的服務(wù)器設(shè)置了禁止跨域,在我們這個場景下的表現(xiàn)就是不能在非 cnblogs 站點下直接下載 cnblogs 文件,所以這里 download 屬性 會失效,而失效之后做的僅僅是跳轉(zhuǎn)功能:
圖片來源:https://blog.csdn.net/PGguoqi/article/details/106817181
解決方案
換個推流的方式,也就是用 js 將資源按照二進制流的方式讀取,對二進制流生成一個 url,這個 url 是我們自己站點可訪問的 url ,沒有禁止跨域的限制。把 url 綁定到 <a> 標(biāo)簽的 href 屬性中,因為瀏覽器無法打開二進制流文件,所以對于這樣的資源,瀏覽器將開始下載而不是在新標(biāo)簽頁打開資源。
<!DOCTYPE?html>
<html>
????<head>
????????<meta?charset="utf-8"?/>
????????<!--解決img標(biāo)簽不能展示網(wǎng)絡(luò)圖片的問題-->
????????<meta?name="referrer"?content="no-referrer">
????????<!--代替import?axios?from?'axios'語句-->
????????<script?src="https://unpkg.com/axios/dist/axios.min.js"></script>
????????<title></title>
????</head>
????<script>
//????????import?axios?from?'axios'
????????/**
?????????*?下載文件
?????????*?@param?url?文件url
?????????*?@param?fileName
?????????*/
????????function?downloadByURL(url,fileName)?{
??????????????axios
????????????????.get(url,?{
????????????????????????responseType:?'blob'
????????????????????})
??????????????.then(response?=>?{
??????????????????data?=?response.data
????????????????if?(!data)?return
????????????????const?blob?=?new?Blob([data],?{type:?"image/png"})
????????????????const?link?=?document.createElement("a")????//?創(chuàng)建<a>標(biāo)簽
????????????????link.style.display?=?"none"????????//?隱藏<a>標(biāo)簽
????????????????link.href?=?URL.createObjectURL(blob)????????//?根據(jù)二進制流對象生成一個url,這個url是我們自己站點可訪問的url,沒有禁止跨域的限制
????????????????link.download?=?fileName?//?這里填保存成的文件名
????????????????link.click()????//強制觸發(fā)a標(biāo)簽事件
????????????????URL.revokeObjectURL(link.href)
????????????????link.remove();
??????????????});
????????}
????</script>
????<body>
????????<img?src?=?"https://img2020.cnblogs.com/blog/1456655/202110/1456655-20211004112059587-1817640282.png"?alt?=?"cdn和對象存儲"/>
????????<a?href?=?"#"?onclick="downloadByURL('https://img2020.cnblogs.com/blog/1456655/202110/1456655-20211004112059587-1817640282.png','cdn.png')">點擊下載圖片</a>
????</body>
</html>
保存修改后,刷新瀏覽器,點擊下載超鏈接,可以看到再次彈出了路徑選擇的彈窗
打開本地文件,證實確實下載成功。
提示:
1、 標(biāo)簽中的 <script src="https://unpkg.com/axios/dist/axios.min.js"></script> 用于替換 import axios from 'axios',因為我們在 js 中用到了 axios,但是 import 語句嵌入在 html 里面使用是 ECMAScript 6 的語法,直接這么寫瀏覽器不識別,需要用 webpack 編譯成符合 ECMAScript 5 語法的 function 才能正常使用。作為初學(xué)者不太懂怎么操作,所以用另一個方式來解決,那就是使用引用 script 的方式來替代 import,如果不像這樣替換,瀏覽器 console 運行 import axios from 'axios' 時會報錯。

2、在瀏覽器渲染 html 時,一直會提示下面這個跨域問題,這個問題暫時沒有徹底解決,但是可以通過https://jingyan.baidu.com/article/148a1921c9dbf24d71c3b11f.html 暫時屏蔽掉這個問題。
Access to XMLHttpRequest at 'https://img2020.cnblogs.com/blog/1456655/202110/1456655-20211004112059587-1817640282.png' from origin 'http://127.0.0.1:8020' has been blocked by <span data-word-id="828" class="abbreviate-word">CORS</span> policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
下面是一篇關(guān)于谷歌瀏覽器跨域問題的博客,以后有時間回來看https://segmentfault.com/a/1190000022506474
二、使用 window.open() 下載
樣例代碼如下,我試了下,確實可以下載,但是打開下載下來的圖片卻提示文件格式錯誤,不知道為什么,如果下載一個本地的 txt 文件,則會把當(dāng)前網(wǎng)頁的 html 源碼下載下來,很奇怪。搞不懂,以后有時間再來想。
<!DOCTYPE?html>
<html>
????<head>
????????<meta?charset="utf-8"?/>
????????<title></title>
????</head>
????<script>
????????function?downloadByURL(url){
????????????window.open(url,'_self')
????????}
????</script>
????<body>
????????<img?src?=?"img/books/book1.jpg"?alt?=?"日俄海戰(zhàn)"/>
????????<a?href?=?"#"?onclick="downloadByURL('img/books/book1.jpg')"?download="日俄海戰(zhàn).jpg">點擊下載圖片</a>
????</body>
</html>
Node 社群
我組建了一個氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學(xué)習(xí)感興趣的話(后續(xù)有計劃也可以),我們可以一起進行Node.js相關(guān)的交流、學(xué)習(xí)、共建。下方加 考拉 好友回復(fù)「Node」即可。
如果你覺得這篇內(nèi)容對你有幫助,我想請你幫我2個小忙:
點贊和在看就是最大的支持 ??
參考資料 [1]HTML Img 標(biāo)簽 src 為網(wǎng)絡(luò)地址無法顯示圖片問題解決(https): https://www.cnblogs.com/guozhaoxin/p/11934483.html
[2]解決 vue:import axios 報錯 Uncaught SyntaxError: Cannot use import statement outside a module: https://blog.csdn.net/weixin_43424730/article/details/115400200
[3]Chrome 瀏覽器如何開啟 Ajax 跨域訪問調(diào)試?: https://jingyan.baidu.com/article/148a1921c9dbf24d71c3b11f.html
[4]html 里 a 標(biāo)簽中 href 調(diào)用 js 的幾種方法: https://blog.csdn.net/itlixw/article/details/110220088
[5]a 標(biāo)簽/js 下載文件(2020): https://blog.csdn.net/itlixw/article/details/110220088
[6]純前端保存下載文件到本地: https://www.cnblogs.com/wuhairui/p/15166748.html
[7]http://es6-features.org/#Constants: http://es6-features.org/#Constants
[8]對象存儲、HDFS、CDN 之間的關(guān)系: https://www.cnblogs.com/hi3254014978/p/15364428.html
