Mobile front-image compression upload practice

Source: Internet
Author: User

before a colleague and I talked about the mobile end with canvas compressed pictures and then upload the function, recently have a little free time, so practice a bit. The Demo effect link is posted at the bottom of the article.

In the mobile image upload, the user is the mobile phone local pictures, and local pictures are relatively large, take iphone6, usually shoot a lot of pictures are one or two m, if directly upload, the picture is too big, if the user is mobile traffic, the picture upload is obviously not a good way.

At present, the HTML5 of the new API are on the mobile side of the WebKit has been better implemented. So directly in the front-end compressed pictures, has become a lot of mobile image upload necessary features.

Compress the images on the mobile side and upload the three H5 APIs that are used primarily for filereader, canvas, and Formdata. Logic is not difficult. The whole process is:

(1) When the user uploads a picture using input file, the user uploads the image data (base64 format) with FileReader

(2) Pass the image data to the IMG object, then draw the IMG onto the canvas, then call Canvas.todataurl to compress the picture.

(3) Obtain the compressed base64 format picture data, turn into binary plug into the formdata, and then submit formdata through XMLHttpRequest.

In such three steps, the image compression and upload are completed.

It seems very simple, in fact, there are some pits. The next step is to analyze it directly in code:

  "One" Get picture data

  First get the image data, that is, listen to the Change event of input file, then get to the uploaded file object files, the class array of files to a group, and then a foreach traversal.

Then determine the file type, if not the picture is not processed. If the picture is to instantiate a filereader, read the uploaded file data in base64 format, determine the length of the data, if the picture is greater than 200KB call compress method to compress, otherwise call upload method to upload.

Filechooser.onchange =function () {        if(! This. files.length)return; varFiles = Array.prototype.slice.call ( This. files); if(Files.length > 9) {alert ("Up to 9 images can be uploaded at the same time"); return; } Files.foreach (function(file, i) {if(!/\/(?: jpeg|png|gif)/i.test (File.type))return; varReader =NewFileReader (); varLi = document.createelement ("li"); Li.innerhtml= ' <div class= ' progress ' ><span></span></div> '; $(". Img-list"). Append ($ (LI)); Reader.onload=function () {                varresult = This. Result; varIMG =NewImage (); IMG.SRC=result; //If the image size is less than 200kb, upload it directly                if(Result.length <=maxsize) {$ (LI). css ("Background-image", "url (" + result + ")"); IMG=NULL;                    Upload (result, File.type, $ (LI)); return; }//compress the picture after it is loaded, then upload                if(Img.complete) {callback (); } Else{img.onload=callback; }                functioncallback () {vardata =Compress (IMG); $ (LI). css ("Background-image", "url (" + Data + ")");                    Upload (data, File.type, $ (LI)); IMG=NULL;            }            };        Reader.readasdataurl (file); })    };

  "2" Compressed picture

after getting the picture data, we can do the compress compress the image method. and compressing the picture is not directly drawing the picture to the canvas and then calling the Todataurl on the line.

In iOS, canvas draws a picture with two restrictions:

The first is the size of the picture, if the size of the picture is more than 2 million pixels, the picture can not be drawn to the canvas, the call DrawImage will not error, but you use Todataurl to obtain the image data when the image is empty data.

In addition, the size of the canvas is limited, if the size of the canvas is larger than about 5 million pixels (that is, the width and height product), not only the picture can not be drawn, and nothing else is not drawn.

The first limitation should be handled by tiles. Tile painting, that is, the picture is divided into multiple pieces to the canvas, my code is to split the image into a 1 million-pixel piece of the size, and then drawn to the canvas.

In response to the second limitation, my approach is to properly compress the width of the image, and in my code, for the sake of insurance, the upper limit is 4 million pixels, and if the image is larger than 4 million pixels, it is compressed to less than 4 million pixels. The 4 million-megapixel picture should be enough, and the width and height are 2000x2000.

This solves both limitations on iOS.

In addition to the limitations described above, there are two pits, one is the canvas of the todataurl is only compressed JPG, when the user uploads the image is a PNG, you need to turn to JPG, that is, unified with 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 a PNG-to-JPG is drawn to the canvas and the canvas has a transparent area, the transparent area turns black when it is turned into a JPG, because the canvas's transparency, like Somer, considers Rgba (0,0,0,0), so turning into JPG becomes rgba ( 0,0,0,1), that is, the transparent background will become black. The solution is to paint a layer of white background on the canvas.

functionCompress (img) {varInitsize =img.src.length; varwidth =Img.width; varHeight =Img.height; //if the picture is larger than 4 million pixels, calculate the compression ratio and press the size below 4 million        varratio; if((ratio = width * height/4000000) >1) {ratio=math.sqrt (ratio); Width/= ratio;        Height/= ratio; }Else{ratio= 1; } canvas.width=width; Canvas.height=height;//Spread the backgroundCtx.fillstyle = "#fff"; Ctx.fillrect (0, 0, Canvas.width, canvas.height); //If the picture pixel is greater than 1 million, use tiles to draw        varcount; if((count = width * height/1000000) > 1) {Count= ~ ~ (math.sqrt (count) +1);//calculate how many tiles to divide into//Calculate the width and height of each tile            varNW = ~ ~ (Width/count); varNH = ~ ~ (Height/count); Tcanvas.width=NW; Tcanvas.height=NH;  for(vari = 0; I < count; i++) {                 for(varj = 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); }        //for minimum compression        varNdata = Canvas.todataurl (' Image/jpeg ', 0.1); Console.log (' Before compression: ' +initsize); Console.log (' After compression: ' +ndata.length); Console.log (' Compression ratio: ' + ~ ~ (Initsize-ndata.length/initsize) + "%"); Tcanvas.width= Tcanvas.height = Canvas.width = Canvas.height = 0; returnNdata; }

  "Three" image upload

After the picture is compressed, it can be plugged into the formdata to upload, first convert the Base64 data into a string, then instantiate a arraybuffer, and then pass the string in a 8-bit integer format into the Arraybuffer, Then through the Blobbuilder or Blob object, the 8-bit integer arraybuffer is turned into a binary object blob, then the Blob object append to Formdata, and then through Ajax to the backend.

XmlHttpRequest2 can not only send big data, but also out of the API, such as to get the progress of sending, my code is also a simple implementation.

//image upload, convert base64 image into binary object, plug in formdata upload    functionUpload (basestr, type, $li) {varText = Window.atob (Basestr.split (",") [1]); varBuffer =NewArrayBuffer (text.length); varUbuffer =Newuint8array (buffer); varpecent = 0, loop =NULL;  for(vari = 0; i < text.length; i++) {Ubuffer[i]=text.charcodeat (i); }        varBuilder = window. Webkitblobbuilder | |window.        Mozblobbuilder; varblob; if(Builder) {varBuilder =NewBuilder ();            Builder.append (buffer); Blob=Builder.getblob (type); } Else{blob=Newwindow.        Blob ([buffer], {type:type}); }        varXHR =NewXMLHttpRequest (); varFormdata =NewFormData (); Formdata.append (' ImageFile ', BLOB); Xhr.open (' Post ', '/cupload '); Xhr.onreadystatechange=function () {            if(Xhr.readystate = = 4 && xhr.status = 200) {Console.log (' Upload succeeded: ' +xhr.responsetext);                Clearinterval (loop); //upload Complete When you receive the message$li. Find (". Progress span"). Animate ({' width ': ' 100% '}, pecent < 95? 200:0,function () {                    $( This). HTML ("Upload succeeded");                }); $(". Pic-list"). Append (' <a href= ' + xhr.responsetext + ' "> ' + xhr.responsetext + ' </a> ')            }        }; //Data delivery progress, top 50% show the progressXhr.upload.addEventListener (' Progress ',function(e) {if(Loop)return; Pecent= ~ ~ (e.loaded/e.total)/2; $li. Find (". Progress span"). CSS (' width ', pecent + "%")); if(Pecent = = 50) {mockprogress (); }        }, false); //data after 50% with simulation progress        functionmockprogress () {if(Loop)return; Loop= SetInterval (function() {pecent++; $li. Find (". Progress span"). CSS (' width ', pecent + "%")); if(Pecent = = 99) {clearinterval (loop); }            }, 100)} xhr.send (Formdata); }

  

  At this point, the entire upload of the front-end picture compression is completed, because it is used formdata submission, so the background data is the same as the normal form form submission data processing.

If you are interested in this demo, you can see this demo's GitHub address:

Front-end Code: https://github.com/whxaxes/node-test/blob/master/server/upload/index_2.html

By the way also post the implementation of the background (NODEJS): Https://github.com/whxaxes/node-test/blob/master/server/upload/upload_2.js

Then post the preview of the demo (if you want to see the compression effect, please use the PC browser to access):

Link: http://wanghx.cn:9030/uindex_2 or directly scan the following two-dimensional code to see the effect (the server's bandwidth is relatively small, the speed may be relatively slow).

  

  

  Because the demo is only practice to play, the test only with their own iphone6 test, so do not know whether other models have problems, if there are inappropriate please forgive me.

Mobile front-image compression upload practice

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.