前端從web伺服器或者CDN下載資源

前段時間聽到前端同學說前端拿到資源的CDN鏈接後可以直接從CDN下載資源,不需要經過後端,感覺很神奇,但是一致不明白是怎麼實現的,前兩天整理了下關於CDN和對象存儲的知識,今天搜了下前端直接下載資源的方式,特此記錄。
目前前端直接下載web伺服器或者CDN靜態資源的方式有兩種,一個是利用<a>標籤,另一個是通過window.open()函數。

一、利用<a>標籤

<a>標籤就是html中的超鏈接標籤,但是用過這個標籤的同學應該都有這種影響,當超鏈接鏈接的內容是圖片、影片或者pdf時,點擊超鏈接往往會在瀏覽器的新標籤頁打開對應的圖片、影片或者pdf,而不會開始下載。但是其他像壓縮包這樣的資源,也就是瀏覽器沒辦法直接打開的資源則會直接開始下載。比如點擊test_download.zip這個超鏈接,這個是一個zip包,所以會開始下載。那怎麼樣讓所有的文件都默認下載而不是打開呢?
html5給<a>標籤增加了一個download屬性,當<a>標籤帶上了download屬性時,點擊超鏈接則會被瀏覽器解析為下載而不是打開。
下面我們來實操一下:

<a>標籤未添加download屬性

比如這個是不帶download屬性的<a>標籤案例。點擊test_download.zip這個超鏈接可以下載對應的圖片和文件夾,只需要把部落格里的程式碼拷貝到文件夾里的index.html中就能看到效果。
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        <img src = "img/books/book1.jpg" alt = "日俄海戰"/>
        <a href = "img/books/book1.jpg">點擊下載圖片</a>
    </body>
</html>
用瀏覽器打開html文件後,點擊超鏈接
在新標籤頁中打開了圖片,並沒有下載。

<a>標籤添加download屬性後

給<a>標籤添加download屬性後,再試一次:
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        <img src = "img/books/book1.jpg" alt = "日俄海戰"/>
        <a href = "img/books/book1.jpg" download="日俄海戰.jpg">點擊下載圖片</a>
    </body>
</html>
用瀏覽器打開html文件後,點擊超鏈接,彈出了路徑選擇窗口,點擊確定,圖片完成下載。

換成網路圖片再試一次

讓我們把<img>標籤和<a>標籤的路徑換成一張網路圖片,而非本地圖片,
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <!--解決img標籤不能展示網路圖片的問題-->
        <meta name="referrer" content="no-referrer">
        <title></title>
    </head>
    <body>
        <img src = "//img2020.cnblogs.com/blog/1456655/202110/1456655-20211004112059587-1817640282.png" alt = "cdn和對象存儲"/>
        <a href = "//img2020.cnblogs.com/blog/1456655/202110/1456655-20211004112059587-1817640282.png" download="cdn和對象存儲.png">點擊下載圖片</a>
    </body>
</html>
點擊下載的超鏈接,會發現並沒有開始下載,而是在新標籤頁打開了圖片。
注意:如果你的網路圖片渲染不出來,嘗試在<head>標籤內添加 <meta name=”referrer” content=”no-referrer”>

沒有開始下載原因

如果加上download屬性,文件還是直接打開,無法正常下載,這有可能是download屬性失效造成的。

download屬性也受同源策略的影響,即非同一埠下不能直接下載第三方文件,所以這裡download失效之後做的僅僅是跳轉功能:


解決方案

換個推流的方式,也就是用js將資源按照二進位流的方式讀取,對二進位流生成一個url,把url綁定到<a>標籤的href屬性中,因為瀏覽器無法打開二進位流文件,所以對於這樣的資源,瀏覽器將開始下載而不是在新標籤頁打開資源。
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <!--解決img標籤不能展示網路圖片的問題-->
        <meta name="referrer" content="no-referrer">
        <!--代替import axios from 'axios'語句-->
        <script src="//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")    // 創建<a>標籤
                link.style.display = "none"        // 隱藏<a>標籤
                link.href = URL.createObjectURL(blob)        // 根據二進位流對象生成一個url
                link.download = fileName // 這裡填保存成的文件名
                link.click()    //強制觸發a標籤事件
                URL.revokeObjectURL(link.href)
                link.remove();
              });
        }
    </script>
    <body>
        <img src = "//img2020.cnblogs.com/blog/1456655/202110/1456655-20211004112059587-1817640282.png" alt = "cdn和對象存儲"/>
        <a href = "#" onclick="downloadByURL('//img2020.cnblogs.com/blog/1456655/202110/1456655-20211004112059587-1817640282.png','cdn.png')">點擊下載圖片</a>
    </body>
</html>

保存修改後,刷新瀏覽器,點擊下載超鏈接,可以看到再次彈出了路徑選擇的彈窗

打開本地文件,證實確實下載成功。
 

提示:

1、<head>標籤中的        <script src=”//unpkg.com/axios/dist/axios.min.js”></script>用於替換import axios from ‘axios’,因為我們在js中用到了axios,但是import語句貌似只能用在獨立的js文件中,不能像上面這樣嵌入在html裡面。如果不像這樣替換,瀏覽器console會報錯import axios from ‘axios’
2、在瀏覽器渲染html時,一直會提示下面這個跨域問題,這個問題暫時沒有徹底解決,但是可以通過這個方法暫時屏蔽掉這個問題。
Access to XMLHttpRequest at '//img2020.cnblogs.com/blog/1456655/202110/1456655-20211004112059587-1817640282.png' from origin '//127.0.0.1:8020' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. 

使用window.open()下載

樣例程式碼如下,我試了下,確實可以下載,但是打開下載下來的圖片卻提示文件格式錯誤,不知道為什麼,如果下載一個本地的txt文件,則會把當前網頁的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 = "日俄海戰"/>
        <a href = "#" onclick="downloadByURL('img/books/book1.jpg')" download="日俄海戰.jpg">點擊下載圖片</a>
    </body>
</html>
參考:
解決vue:import axios 報錯 Uncaught SyntaxError: Cannot use import statement outside a module
html里a標籤中href調用js的幾種方法
a標籤/js下載文件(2020)
純前端保存下載文件到本地