Home > Article > Web Front-end > The mobile terminal uses H5 to implement the function of compressing image uploads
This article mainly introduces in detail the use of H5 on the mobile terminal to realize the compressed image upload function. It has a certain reference value. Interested friends can refer to it.
A colleague talked to me before Regarding the function of using canvas to compress images on the mobile terminal and then upload them, I recently had some free time, so I put it into practice. The demo effect link is posted at the bottom of the article.
When uploading pictures on the mobile terminal, users upload local pictures on the mobile phone, and local pictures are generally relatively large. Take iPhone6 as an example. Many pictures usually taken are one or two megabytes in size. If If you upload it directly like this, the image will be too big. If the user uses mobile data, uploading the image completely is obviously not a good idea.
Currently, various new APIs of HTML5 have been better implemented on 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 pictures, use filereader to read the picture data uploaded by the user (base64 format)
(2) Pass the picture data into img Object, then draw img to 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 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] Obtain image data
First, obtain the image data, that is, listen to the change event of the input file, and then obtain For the uploaded file object files, convert the array-like files into an array, and then perform forEach traversal.
Next, 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 (!/\/(?:jpegpnggif)/i.test(file.type)) return; var reader = new FileReader(); var li = document.createElement("li"); li.innerHTML = "<p class="progress"><span></span></p>"; $(".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 perform compress compression Picture method. 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 limitation, 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.
In this way, the two limitations on IOS are solved.
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 and draw it on the canvas, if there is a transparent area in the canvas, the transparent area will turn black when you convert it to jpg, because the transparent pixels of the 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("image/jpeg", 0.1); console.log("压缩前:" + initSize); console.log("压缩后:" + ndata.length); console.log("压缩率:" + ~~(100 * (initSize - ndata.length) / initSize) + "%"); tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0; return ndata; }
[3] Image upload
After the image compression is completed, it can be inserted into formdata and uploaded. , first convert the base64 data into a string, then instantiate an ArrayBuffer, then pass the string into the ArrayBuffer in the format of an 8-bit integer, and then use the BlobBuilder or Blob object to convert the 8-bit integer ArrayBuffer into a binary object blob , then append the blob object to formdata, and then send it to the background through ajax.
XmlHttpRequest2 not only can send big data, but also has additional APIs such as obtaining the sending progress, which I have also implemented in a simple way in my code.
// 图片上传,将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("imagefile", blob); xhr.open("post", "/cupload"); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { console.log("上传成功:" + xhr.responseText); clearInterval(loop); //当收到该消息时上传完毕 $li.find(".progress span").animate({"width": "100%"}, pecent < 95 ? 200 : 0, function () { $(this).html("上传成功"); }); $(".pic-list").append("<a href="" + xhr.responseText + " rel="external nofollow" ">" + xhr.responseText + "<img src="" + xhr.responseText + "" /></a>") } }; //数据发送进度,前50%展示该进度 xhr.upload.addEventListener("progress", function (e) { if (loop) return; pecent = ~~(100 * e.loaded / e.total) / 2; $li.find(".progress span").css("width", pecent + "%"); if (pecent == 50) { mockProgress(); } }, false); //数据后50%用模拟进度 function mockProgress() { if (loop) return; loop = setInterval(function () { pecent++; $li.find(".progress span").css("width", pecent + "%"); if (pecent == 99) { clearInterval(loop); } }, 100) } xhr.send(formdata); }
至此,整个上传的前端图片压缩就完成了,因为是用了formdata提交,所以后台接数据的时候就跟普通form表单提交数据一样处理即可。
以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!
相关推荐:
The above is the detailed content of The mobile terminal uses H5 to implement the function of compressing image uploads. For more information, please follow other related articles on the PHP Chinese website!