圖片下載的幾種實現

下載圖片的幾種方式

  • 經過瀏覽器自動下載。瀏覽器接收到二進制文件,自動轉碼下載。好比:window.open()
  • 經過xhr下載。ajax請求獲得的是二進制文件,只能手動轉碼下載。

第一種:瀏覽器自動下載型

這種須要後端配置響應參數javascript

window.open

例子:
本地起了一個node服務,端口3000html

  • 設置了content-disposition的狀況:java

    頁面設置:node

    <button onclick="downImg(1)">點擊下載圖片</button>
    <button onclick="downImg(2)">點擊下載excel</button>
    <script>
        function downImg(type) {
            if(type ===1) {
                window.open('http://127.0.0.1:3000/img');
            } else {
                window.open('http://127.0.0.1:3000/api');
            }
        }
    </script>

    後端:es6

    app.get('/img', (req, res) => {
        res.setHeader('Content-Type', 'image/jpeg');
        // res.download自動設置Content-disposition
        res.download('./public/1.jpg'); 
    })

    實驗結果:ajax

    • ie >=8 ,成功下載圖片;
    • chrome,成功下載圖片;
  • 未設置content-disposition的狀況chrome

    app.get('/img', (req, res) => {
        res.setHeader('Content-Type', 'image/jpeg');
        let pathname = path.join(__dirname, './public/1.jpg')
        res.sendFile(pathname);
    })
    app.get('/api', (req, res) => {
        res.setHeader('Content-Type', 'application/vnd.ms-excel');
        let pathname = path.join(__dirname, './public/api.xlsx')
        res.sendFile(pathname);
    })

    實驗結果:後端

    • 圖片在新標籤頁打開
    • excel表自動下載了
經過a標籤請求
function alabel(src, downloadName) {
    // 注意,ie11還不支持es6的語法
    var a = document.createElement('a');
    a.target = "_blank";
    a.href = src;
    a.download = downloadName;
    a.click();
}
function downImg() {
    var url = 'http://127.0.0.1:3000/img';
    alabel(url, '1.jpg');
}

實驗結果:api

瀏覽器 chrome ie
同域 能夠下載 不能下載
跨域 打開新標籤頁 不能下載
設置content-disposition 能夠下載 不能下載
沒設置content-disposition 能夠下載 不能下載

因此a標籤下載跟瀏覽器的兼容性和是否跨域有關跨域

第一種狀況結論
一、對於圖片下載,須要設置響應頭:`content-disposition`,該字段的做用是告訴瀏覽器,這是一個附件;對於excel表格,無論是否設置了響應頭,都會下載。
二、window.open()沒有跨域問題,沒有瀏覽器兼容性問題
三、相對於a標籤,window.open會好一些,a標籤有瀏覽器的兼容性問題。

第二種:經過xhr轉換
經過xhr轉換的有多種
blob下載

blob下載必須設置responseType:blob,不然返回的是一堆亂碼的字符串。設置responseType:blob以後,瀏覽器將返回數據轉成blob對象。

首先,建立xhr對象,請求接口
接下來是常見的瀏覽器網絡請求接口4步驟:

createXHR(url, data, method, successCallBack, errCallBack) {
    // 建立xhr對象
    let xhr = new XMLHttpRequest();
    // 鏈接
    xhr.open(method, url);
    // 發送
    xhr.send(data);
    // 必須設置responseType,不然返回的是字符串
    xhr.responseType = "blob";
    xhr.onreadystatechange = function() {
        // xhr的請求狀態有0,1,2,3,4
        if(xhr.readyState === 4) {
            // 返回的狀態碼
            if(xhr.status === 200) {
                // Content-Disposition獲取文件名
                successCallBack(xhr.response, xhr.getResponseHeader("Content-Disposition"));
            } else {
                errCallBack(xhr.response);
            }
        }
    }
}

調用createXHR

var url = 'http://127.0.0.1:3000/img';
createXHR(url, {},'GET', function(res, header) {
    let downloadName = header.split('filename="')[1].slice(0, -1);
    blob([res], 'image/jpeg', downloadName)
}, function(e) {
    console.log(e);  
})

blob方法

blob(data, content_type, downloadName) {
    let blob = new Blob(data, { type: content_type});
    // URL.createObjectURL生成一個DOMString, 包含了一個url對象,這個url對象與file對象或者blob對象有一個映射關係。 URL 的生命週期和建立它的窗口中的 document 綁定
    let url = URL.createObjectURL(blob);
    alabel(url, downloadName);
    // 銷燬url, 回收內存
    URL.revokeObjectURL(url);
}

能夠使用blob構造函數,那也能夠使用h5的fileReader, 由於fileReader繼承blob。fileReader的寫法以下:

// FileReader轉換對象不須要數組形式,blob是數組格式
fileReader(res, '1.jpg');

fileReader(data, downloadName) {
    let reader = new FileReader();
    reader.readAsDataURL(data);
    reader.addEventListener('load', function() {
        alabel(this.result, downloadName);
    })
}

實驗結果:

  • blob下載,使用的是Blob構造函數。最後仍是要建立a標籤下載。咱們也知道a標籤下載的一些缺點,一樣blob下載也存在這樣的缺點。
  • getResponseHeader,若是是跨域,只能獲取到Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma幾個響應頭,若是須要獲取其餘的響應頭,須要設置Access-Control-Expose-Headers
兼容ie下載文件

a標籤下載有瀏覽器兼容問題。要兼容ie就要找其餘的方法。前面有提到的window.open,還有一種是iframe

  • iframe標籤

    var url = 'http://127.0.0.1:3000/img';
    var iframe = document.createElement("iframe");  
    iframe.src = url;  
    iframe.style.display = "none";  
    document.body.appendChild(iframe);
    iframe.click();

    實驗結果:

    • 兼容性:ie,chrome
    • 可是要設置content-disposition
  • 還有一種ie自有的下載文件,ie10以上
window.navigator.msSaveBlob(blob, '1.jpg');
跨域,沒設置響應頭,又要兼容ie

這種狀況只能說是做死,可是沒辦法,實際狀況也有可能有這麼樣。這種狀況呢,只能是使用服務端作代理。這裏使用的是node作代理。

頁面:

// url是目標圖片地址, serverApi是本地服務器接口
var url = 'http://http://127.0.0.1:3001/img';
var serverApi = 'http://127.0.0.1:3000/download'
createXHR(serverApi, {url},'POST', function(res, header) {
        blob(res, 'image/jpeg','1.jpg');
    }, function(e) {/*  */
        console.log(e);    
    })

服務端:

app.post('/download', (req, res) => {
    res.setHeader('Content-Type', 'image/png')
    request.get(req.body.url).pipe(res)
})

最後

一、若是響應頭設置有content-disposition,能夠使用window.open

二、不知足1時,能夠使用a標籤下載

三、若是要兼容ie,能夠使用navigator.msSaveBlob

四、若是沒設置響應頭,又跨域,那隻能使用服務器代理

相關文章
相關標籤/搜索