Home >Web Front-end >JS Tutorial >How to use the mobile front-end image compression and upload function?

How to use the mobile front-end image compression and upload function?

亚连
亚连Original
2018-06-22 15:51:591230browse

The editor below will share with you an example of mobile front-end image compression and uploading. It has a very good reference value and I hope it will be helpful to everyone. Let the editor come and take a look.

Summary: I was working on a small game platform project before. There was a "User Center" module, which involved avatar uploading. Function. When uploading pictures on the mobile terminal, all the pictures uploaded are local pictures of the mobile phone, and local pictures are generally relatively large. Taking current smartphones as an example, many pictures usually taken are two to three megabytes in size. If you upload them directly like this, The image will be too big. If the user is using mobile data, uploading the image completely is obviously not a good idea. Therefore, it is necessary to perform compression before uploading. After searching a lot of information on the Internet, I tried many methods and encountered many pitfalls. For example, Android can successfully compress and upload images, but it cannot be uploaded on iOS. It took a long time to find out about iOS. pit. This has been proven to be feasible in practice. Images of several megabytes can be compressed within the 200k required by our backend! Such a feasible method must be shown to everyone [ps: they are all made by piecing together other people’s methods, hehe~].

Currently, various new APIs of HTML5 have been better implemented on the mobile webkit. According to caniuse, the FileReader, Blob, and Formdata objects used in this demo have been implemented in most mobile device browsers (safari6.0, android 3.0), so compressing images directly on the front end has become a common problem for many mobile devices. It is a necessary function for uploading pictures on the terminal.

Compressing and uploading images on the mobile terminal mainly uses the three h5 APIs of filereader, canvas and formdata. The logic is not difficult. The whole process is:

(1) When the user uses input file to upload an image, use filereader to read the image data uploaded by the user (base64 format)

(2) Pass the image data into img object, then draw the img to the canvas, and then call canvas.toDataURL to compress the image

(3) Obtain the compressed base64 format image data, convert it into binary and insert it into the formdata, and then submit the formdata through XmlHttpRequest .

In these three steps, the image compression and uploading are completed.

It sounds simple, but in fact there are still some pitfalls. Next, use the code to analyze it directly:

[1] Get the image data

First get the image data, which is to monitor the input file change event, then obtain the uploaded file object files, convert the array-like files into an array, and then perform forEach traversal.

Then determine the file type. If it is not a picture, it will not be processed. If it is a picture, instantiate a filereader, read the uploaded file data in base64 format, and determine the data length. If the picture is larger than 200KB, call the compress method to compress it, otherwise call the upload method to upload.

filechooser.onchange = function() {
 if (!this.files.length) return;
 var files = Array.prototype.slice.call(this.files);
 if (files.length > 9) {
  alert("最多同时只可上传9张图片");
  return;
 }
 files.forEach(function(file, i) {
  if (!/\/(?:jpeg|png|gif)/i.test(file.type)) return;
  var reader = new FileReader();
  var li = document.createElement("li");
  li.innerHTML = &#39;<p class="progress"><span></span></p>&#39;;
  $(".img-list").append($(li));
  reader.onload = function() {
   var result = this.result;
   var img = new Image();
   img.src = result;
   //如果图片大小小于200kb,则直接上传
   if (result.length <= maxsize) {
    $(li).css("background-image", "url(" + result + ")");
    img = null;
    upload(result, file.type, $(li));
    return;
   }
   //图片加载完毕之后进行压缩,然后上传
   if (img.complete) {
    callback();
   } else {
    img.onload = callback;
   }
   function callback() {
    var data = compress(img);
    $(li).css("background-image", "url(" + data + ")");
    upload(data, file.type, $(li));
    img = null;
   }
  };
  reader.readAsDataURL(file);
 })
};

【2】Compress images

After completing the acquisition of image data above, you can use the method of compressing images. Compressing images does not involve directly drawing the image to canvas and then calling toDataURL.

In IOS, there are two limitations for drawing pictures on canvas:

The first is the size of the picture. If the size of the picture exceeds two million pixels, the picture cannot be drawn on the canvas. , no error will be reported when calling drawImage, but when you use toDataURL to obtain image data, you will get empty image data.

Furthermore, the size of the canvas is limited. If the size of the canvas is larger than about five million pixels (i.e., the product of width and height), not only the picture cannot be drawn, but also other things cannot be drawn. .

To deal with the first limitation, the solution is to draw tiles. Tile drawing is to divide the picture into multiple pieces and draw them on the canvas. The method in my code is to divide the picture into 1 million pixels and then draw it on the canvas.

To deal with the second restriction, my solution is to appropriately compress the width and height of the image. For the sake of safety, the upper limit set in my code is four million pixels. If the image is larger than four million pixels, Just compress it to less than 4 million pixels. A 4-megapixel image should be enough, with a width and height of 2000X2000.

This solves the two limitations on IOS.

In addition to the limitations mentioned above, there are two pitfalls. One is that the toDataURL of canvas can only compress jpg. When the image uploaded by the user is png, it needs to be converted to jpg, that is, it can be used uniformly. canvas.toDataURL('image/jpeg', 0.1), the type is uniformly set to jpeg, and the compression ratio is controlled by itself.

The other is that if you convert png to jpg, when you draw it on canvas, if the canvas has a transparent area, the transparent area will turn black when you convert it to jpg, because the transparent pixels of canvas default to rgba( 0,0,0,0), so when converted to jpg, it becomes rgba(0,0,0,1), that is, the transparent background will become black. The solution is to lay a white background on the canvas before drawing.

function compress(img) {
 var initSize = img.src.length;
 var width = img.width;
 var height = img.height;
 //如果图片大于四百万像素,计算压缩比并将大小压至400万以下
 var ratio;
 if ((ratio = width * height / 4000000) > 1) {
  ratio = Math.sqrt(ratio);
  width /= ratio;
  height /= ratio;
 } else {
  ratio = 1;
 }
 canvas.width = width;
 canvas.height = height;
 //铺底色
 ctx.fillStyle = "#fff";
 ctx.fillRect(0, 0, canvas.width, canvas.height);
 //如果图片像素大于100万则使用瓦片绘制
 var count;
 if ((count = width * height / 1000000) > 1) {
  count = ~~(Math.sqrt(count) + 1); //计算要分成多少块瓦片
  //计算每块瓦片的宽和高
  var nw = ~~(width / count);
  var nh = ~~(height / count);
  tCanvas.width = nw;
  tCanvas.height = nh;
  for (var i = 0; i < count; i++) {
   for (var j = 0; j < count; j++) {
    tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh);
    ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh);
   }
  }
 } else {
  ctx.drawImage(img, 0, 0, width, height);
 }
 //进行最小压缩
 var ndata = canvas.toDataURL(&#39;image/jpeg&#39;, 0.1);
 console.log(&#39;压缩前:&#39; + initSize);
 console.log(&#39;压缩后:&#39; + ndata.length);
 console.log(&#39;压缩率:&#39; + ~~(100 * (initSize - ndata.length) / initSize) + "%");
 tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0;
 return ndata;
}

【三】Picture upload

完成图片压缩后,就可以塞进formdata里进行上传了,先将base64数据转成字符串,再实例化一个ArrayBuffer,然后将字符串以8位整型的格式传入ArrayBuffer,再通过BlobBuilder或者Blob对象,将8位整型的ArrayBuffer转成二进制对象blob,然后把blob对象append到formdata里,再通过ajax发送给后台即可。

XmlHttpRequest2中不仅可以发送大数据,还多出了比如获取发送进度的API,我代码里也进行了简单的实现。

//图片上传,将base64的图片转成二进制对象,塞进formdata上传
function upload(basestr, type, $li) {
 var text = window.atob(basestr.split(",")[1]);
 var buffer = new ArrayBuffer(text.length);
 var ubuffer = new Uint8Array(buffer);
 var pecent = 0,
  loop = null;
 for (var i = 0; i < text.length; i++) {
  ubuffer[i] = text.charCodeAt(i);
 }
 var Builder = window.WebKitBlobBuilder || window.MozBlobBuilder;
 var blob;
 if (Builder) {
  var builder = new Builder();
  builder.append(buffer);
  blob = builder.getBlob(type);
 } else {
  blob = new window.Blob([buffer], {
   type: type
  });
 }
 var xhr = new XMLHttpRequest();
 var formdata = new FormData();
 formdata.append(&#39;imagefile&#39;, blob);
 xhr.open(&#39;post&#39;, &#39;/cupload&#39;);
 xhr.onreadystatechange = function() {
  if (xhr.readyState == 4 && xhr.status == 200) {
   console.log(&#39;上传成功:&#39; + xhr.responseText);
   clearInterval(loop);
   //当收到该消息时上传完毕
   $li.find(".progress span").animate({
    &#39;width&#39;: "100%"
   }, pecent < 95 ? 200 : 0, function() {
    $(this).html("上传成功");
   });
   $(".pic-list").append(&#39;<a href="&#39; + xhr.responseText + &#39;">&#39; + xhr.responseText + &#39;<img src="&#39; + xhr.responseText + &#39;" /></a>&#39;)
  }
 };
 //数据发送进度,前50%展示该进度
 xhr.upload.addEventListener(&#39;progress&#39;, function(e) {
  if (loop) return;
  pecent = ~~(100 * e.loaded / e.total) / 2;
  $li.find(".progress span").css(&#39;width&#39;, pecent + "%");
  if (pecent == 50) {
   mockProgress();
  }
 }, false);
 //数据后50%用模拟进度
 function mockProgress() {
  if (loop) return;
  loop = setInterval(function() {
   pecent++;
   $li.find(".progress span").css(&#39;width&#39;, pecent + "%");
   if (pecent == 99) {
    clearInterval(loop);
   }
  }, 100)
 }
 xhr.send(formdata);
}

至此,整个上传的前端图片压缩就完成了,因为是用了formdata提交,所以后台接数据的时候就跟普通form表单提交数据一样处理即可。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

在Webpack中如何加载SVG

在vue-cli中如何实现移动端自适应

在Vue 组件Toast中如何实现显示框效果

有关webpack中rules参数处理

The above is the detailed content of How to use the mobile front-end image compression and upload function?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn