Mobile front-image compression upload practice

Source: Internet
Author: User

At present, the HTML5 of the new API are on the mobile side of the WebKit has been better implemented. According to view Caniuse, the FileReader, Blob, Formdata objects used in this demo have been implemented in most mobile device browsers (safari6.0+, Android 3.0+), so the image is compressed directly in the front 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;

var files = 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 (!/\/(?: jpegpnggif)/i.test (File.type)) return;

var reader = new FileReader ();

var li = document.createelement ("Li");
li.innerhtml = "<div class=" Progress "><span></span></div>";
$ (". Img-list"). Append ($ (LI));

Reader.onload = function () {
var result = This.result;
var img = new Image ();
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;
}

function callback () {
var data = 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.

Function Compress (img) {        var initsize = img.src.length;         var width = img.width;        var height = img.height;        //If the picture is larger than 4 million pixels, calculate the compression ratio and press the size below 4 million          Var ratio;        if ((ratio = width * height/4000000) >1) {&NBSP;&NBSP;&NB sp;         ratio = math.sqrt (ratio);             width/= ratio;            Height/= ratio;       }else {             ratio = 1;       }         Canvas.width = width;        Canvas.height = Height;//        floor         ctx.fillstyle = "# FFF ";        ctx.fillrect (0, 0, canvas.width, canvas.height);        //If the picture pixel is greater than 1 million use tile drawing         var count;         if ((count = width * height/1000000) > 1) {             count = ~ ~ (math.sqrt (count) +1); Calculate how many tiles to divide//            calculate the width and height of each tile              var NW = ~ ~ (width/count);             var NH = ~ ~ (height/count);             tcanvas.width = nw;            TCanvas.height = Nh;            for (var i = 0; i < count; i++) {        & nbsp;       for (var j = 0; J < Count; J + +) {                     tctx.drawimage (img, I * NW * ratio, J * NH * rat Io, 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);       }        //Minimum compression         var ndata = Canvas.todataurl ("Image/jpeg", 0.1);        Console.log ("Pre-compression:" + initsize);        Console.log ("after compression:" + ndata.length);         Console.log ("Compression ratio:" + ~ ~ (initsize-ndata.length)/initsize) + "%");  &nb sp;      tcanvas.width = Tcanvas.height = Canvas.width = Canvas.height = 0;   & nbsp;    return ndata;   }

  "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     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 ();  & nbsp;      formdata.append ("ImageFile", blob);         Xhr.open ("Post", "/cupload");        Xhr.onreadystatechange = function () {             if (xhr.readystate = = 4 && Xhr.status = = {                Console.log (" Upload succeeded: "+ xhr.responsetext);                ClearInterval (Loop) ;               //upload complete when the message is received                  $li. Find (". Progress span"). Animate ({"width": "100%"}, pecent < 200:0, function () {         & nbsp;          $ (this). html ("Upload succeeded");                });                 $ (". Pic-list"). Append ("<a href=" "+ Xhr.responsetext +" ">" + Xhr.responsetext + "</a>")             }       };       //Data sending progress, top 50% showing the progress         Xhr.upload.addEventListener (" Progress ", function (e) {            if (loop) return;             pecent = ~ ~ (e.loaded/e.total)/2;             $li. Find (". Progress span"). CSS ("width", pecent + "%");             if (pecent = =) {                 mockprogress ();            }       }, False);        //Data 50% with simulation progress         function mockprogress () {             if (loop) return;            loop = setinterval (function () {                 pecent++;                 $li. Find (". Progress span"). CSS ("width", pecent + "%");                 if (pecent = =) {                     Clearinterval ( Loop);               }            )        }         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).

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.