1. 程式人生 > >利用FileReader和FormData實現圖片預覽和上傳(base64轉二進位制檔案)

利用FileReader和FormData實現圖片預覽和上傳(base64轉二進位制檔案)

業務有個需求,要做圖片預覽上傳,過去都是客戶端上傳給後端,後端返回 url 前端進行預覽,現在其實可以不依賴後端做預覽,最後在上傳,這主要依賴 FileReader 和 FormData 這兩個物件和 JavaScript 處理二進位制的能力。

OK,Show code~,以下程式碼已註釋掉具體業務邏輯和實現,如果需要了解 API 細節,可以請參考:

監聽表單檔案變化

檔案表單的樣式主要通過讓它後面,通過別的DOM來美化它。

<input type="file">
input.on.('change', preview);

預覽

預覽使用 FileReader 物件來讀:

function preview(e) {
    var file = e.target.files[0];
    var reader = new FileReader();

    reader.onloadend = function () {
        // 圖片的 base64 格式, 可以直接當成 img 的 src 屬性值
        var dataURL = reader.result;
        var img = new Image();
        img.src = dataURL;
        // 插入到 DOM 中預覽
        // ...
}; reader.readAsDataURL(file); // 讀出 base64 }

提交圖片檔案(二進位制檔案 非 base64)

base64 轉 二進位制檔案

/**
 * dataURL to blob, ref to https://gist.github.com/fupslot/5015897
 * @param dataURI
 * @returns {Blob}
 */
function dataURItoBlob(dataURI) {
    var byteString = atob(dataURI.split(',')[1]);
    var mimeString = dataURI.split('
,')[0].split(':')[1].split(';')[0]; var ab = new ArrayBuffer(byteString.length); var ia = new Uint8Array(ab); for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); } return new Blob([ab], {type: mimeString}); }

構造 FormData 填充二進位制檔案資料,通過 ajax 的方式進行提交:

var fd = new FormData();
var blob = dataURItoBlob(dataURL);
fd.append('file', blob);


$.ajax({
    type: 'POST',
    url: '/upload',
    data: fd,
    processData: false, // 不會將 data 引數序列化字串
    contentType: false, // 根據表單 input 提交的資料使用其預設的 contentType
    xhr: function() {
        var xhr = new window.XMLHttpRequest();
        xhr.upload.addEventListener("progress", function(evt) {
            if (evt.lengthComputable) {
                var percentComplete = evt.loaded / evt.total;
                console.log('進度', percentComplete);
            }
        }, false);

        return xhr;
    }
}).success(function (res) {
    // 拿到提交的結果
}).error(function (err) {
    console.error(err);
});

注意:不要漏了指定 processData 和 contentType 為 false 。

壓縮

業務中不需要前端不需要壓縮,因為後端有更靠譜的壓縮方案,但是前端其實也可以壓縮,那就是用 canvas 把圖畫出適合的大小,然後上傳。

主要流程:

  • 在 new 出來的 Image 物件,我們監聽它的 onload 事件
  • 按照壓縮比例,算出壓縮後的圖片尺寸
  • 建立 canvas ,尺寸設定成上一步驟算出來的壓縮後的圖片尺寸
  • 呼叫 drawImage 方法,把圖片繪製到 canvas 中
  • 呼叫 canvas 的 toDataURL ,取出 base64 格式的資料
  • 後續的傳圖步驟和上面的原圖上傳一樣
var img = new Image();

img.onload = function () {
    // 當圖片寬度超過 400px 時, 就壓縮成 400px, 高度按比例計算
    // 壓縮質量可以根據實際情況調整
    var w = Math.min(400, img.width);
    var h = img.height * (w / img.width);
    var canvas = document.createElement('canvas');
    var ctx = canvas.getContext('2d');

    // 設定 canvas 的寬度和高度
    canvas.width = w;
    canvas.height = h;

    // 把圖片繪製到 canvas 中
    ctx.drawImage(img, 0, 0, w, h);

    // 取出 base64 格式資料
    var dataURL = canvas.toDataURL('image/png');

    // ...
};

img.src = reader.result;

這樣一看,好像除去業務邏輯的話,好像也沒多少程式碼