H5压缩图片上传(FileReader +canvas)

时间:2021-05-13 19:42:42

因为最近项目做一个webApp的页面,需要上传图片,总结了一下,思路如下:

一、监听一个 input (type='file') 的 change 事件,然后拿到文件的 file;

<input id="img-input" class="upload-input" type="file" accept="image/*" id="imgbox" multiple/>

二、把 file 转成 dataURL;

/**
 * file 转成 dataURL
 * @param file 文件
 * @param callback 回调函数
 */
function fileToDataURL (file, callback) {
  const reader = new window.FileReader();
  reader.onload = function (e) {
    callback(e.target.result);
  };
  reader.readAsDataURL(file);
}

三、然后用 canvas 绘制图片,绘制的时候经过算法按比例裁剪,然后再把 canvas 转成 dataURL;

/**
 * 使用 canvas 压缩 dataURL
 * @param dataURL
 * @param ratio
 * @param callback
 */
function compressDataURL (dataURL, ratio, callback) {
  const img = new window.Image();
  img.src = dataURL;
  // onload
  img.onload = function () {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    canvas.width = 100 * ratio.w;
    canvas.height = 100 * ratio.h;
    const RATIO = canvas.width / canvas.height;
    let cutx = 0;
    let cuty = 0;
    let cutw = img.width;
    let cuth = img.height;
    if (cutw / cuth > RATIO) {
      // 宽超过比例了]]
      let realw = cuth * RATIO;
      cutx = (cutw - realw) / 2;
      cutw = realw;
    } else if (cutw / cuth < RATIO) {
      // 长超过比例了]]
      let realh = cutw / RATIO;
      cuty = (cuth - realh) / 2;
      cuth = realh;
    }
    ctx.drawImage(img, cutx, cuty, cutw, cuth, 0, 0, canvas.width, canvas.height);
    const ndata = canvas.toDataURL('image/jpeg', 1);
    callback(ndata);
  };
}

四、 dataURL 转成 blob;

/**
 * dataURL 转成 blob
 * @param dataURL
 * @return blob
 */
function dataURLtoBlob (dataURL) {
  let binaryString = atob(dataURL.split(',')[1]);
  let arrayBuffer = new ArrayBuffer(binaryString.length);
  let intArray = new Uint8Array(arrayBuffer);
  let mime = dataURL.split(',')[0].match(/:(.*?);/)[1]
  for (let i = 0, j = binaryString.length; i < j; i++) {
    intArray[i] = binaryString.charCodeAt(i);
  }
  let data = [intArray];
  let result;
  try {
    result = new Blob(data, { type: mime });
  } catch (error) {
    window.BlobBuilder = window.BlobBuilder ||
      window.WebKitBlobBuilder ||
      window.MozBlobBuilder ||
      window.MSBlobBuilder;
    if (error.name === 'TypeError' && window.BlobBuilder){
      var builder = new BlobBuilder();
      builder.append(arrayBuffer);
      result = builder.getBlob(type);
    } else {
      throw new Error('暂不支持');
    }
  }
  return result;
}

五、把 blob append 到 FormData 的实例对象,然后上传。

var imgBox =document.getElementById("imgbox");
var blobArr = []; //参数可以传给后台 imgBox.change
= function(e){ var file=this.files; for(var i=0;i<event.target.files.length;i++){ fileToDataURL(file[i], function(dataURL){ compressDataURL(dataURL,function (newDataURL) {             const newBlob = this.dataURLtoBlob(newDataURL,file[i]); //const fileOfBlob = new File([newBlob], file[i].name); //可将blob转换成file格式 blobArr.push(newBlob); }); }); } }

//上传
var
formData = new FormData();
for (var i = 0, len = blobArr.file.length; i < len; i++) {
  formData.append("file" + i, options.file[i]);
}

let xhr = new XMLHttpRequest();
 
// 进度监听
// xhr.upload.addEventListener('progress', progFoo, false);
// 加载监听
// xhr.addEventListener('load', loadFoo, false);
// 错误监听
// xhr.addEventListener('error', errorFoo, false);
 
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4) {
    if (xhr.status === 200) {
    // 上传成功,获取到结果 results
    let results = JSON.parse(xhr.responseText);
    // ......
    }
    } else {
    // 上传失败
    }
  }
};
  xhr.open( 'POST', '/api/upload', true);
  xhr.send(formData);
});