In-depth research on HTML5 canvas for Image Compression and uploading,
As mentioned in the previous article, we know that the traffic is still very expensive, and the pixels of mobile phones are getting higher and higher. Taking a photo is just several MB, which is hard to hurt. Although the client can easily compress and upload images, our applications may be opened in the browser. What should we do? compress images. Due to the previous development thinking on the PC, Nima js has the permission to operate files and has the right to compress images. you can't solve it. Go to the client to complete it. I can only say that I am still a little confused. Under the influence of HTML5, there are more and more front-end capabilities and the development capability is getting higher and higher. Long live H5! The charm of the front-end is also here. What was impossible in the past does not mean that it is not possible now or in the future. Please work hard, cool!
How does js compress images ??? In the subconscious, I did not think it was possible at the beginning. Later I read the materials and studied them. I found it feasible! Start!
First, let's talk about how we uploaded files before H5. Generally, we use plug-ins, flash, or simply a file form, with less effort.
Since H5, the boss has never worried about my development.
In the previous article, we mentioned that FileReader and FormData are used for image uploading. In fact, we can use these two methods to preview and upload images. To achieve image compression, we need to use canvas. Yes, it is canvas!
The general idea is:
1. Create an image and a canvas
var image = new Image(),canvas = document.createElement("canvas"),ctx = canvas.getContext('2d');
2. The image address selected in input is obtained through FileReader and assigned to the newly created image object. Then, the image object is dropped to the canvas.
Var file = obj. files [0]; var reader = new FileReader (); // read the file reader on the client. onload = function () {var url = reader. result; // the content of the file to be read. this attribute is valid only after the read operation is complete, and the data format depends on the method in which the read operation is initiated. therefore, you must use reader. onload, image. src = url; // the content of the file read by reader is base64. This url can be used to preview the image before upload ...}; image. onload = function () {var w = image. naturalWidth, h = image. naturalHeight; canvas. width = w; canvas. height = h; ctx. drawImage (image, 0, 0, w, h, 0, 0, w, h); fileUpload () ;}; reader. readAsDataURL (file );
It should be noted that the size of the canvas should be determined when the canvas draws an image to the canvas, and the drawImage parameters should be set as follows:
void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
dx
The position of the X axis in the upper left corner of the source image on the target canvas.
dy
The position of the Y axis in the upper left corner of the source image on the target canvas.
dWidth
Draw the image width on the target canvas. Allows scaling of the drawn image. If not specified, the image width is not scaled during painting.
dHeight
Draw the Image Height on the target canvas. Allows scaling of the drawn image. If not specified, the image height is not scaled during painting.
sx
To be drawn to the target context, select the X coordinate in the upper left corner of the rectangular selection box of the source image.
sy
The Y coordinate in the upper left corner of the rectangular selection box of the source image to be drawn to the target context.
sWidth
The width of the rectangle selection box of the source image to be drawn to the target context. If not, the entire rectangle starts from the sx and sy coordinates and ends at the bottom right corner of the image.
sHeight
The height of the rectangular selection box of the source image to be drawn to the target context.
In order to upload the complete image, dx, dy must be set to 0, dWidth and dHeight must be set to the width and height of the original image. This is why we need to obtain the original size after the image object is downloaded. This is critical!
3. upload images
Function fileUpload () {var data = canvas. toDataURL ("image/jpeg", quality); // The format of dataURL is "data: image/png; base64, ***", which is descriptive text before commas, we only need the data = data. split (',') [1]; data = window. atob (data); var ia = new Uint8Array (data. length); for (var I = 0; I <data. length; I ++) {ia [I] = data. charCodeAt (I) ;}; // canvas. the default format returned by toDataURL is image/png var blob = new Blob ([ia], {type: "image/jpeg"}); var fd = new FormData (); fd. append ('myfile', blob); var xhr = new XMLHttpRequest (); xhr. addEventListener ("load", opts. success, false); xhr. addEventListener ("error", opts. error, false); xhr. open ("POST", opts. url); xhr. send (fd );}
The key method used here is canvas. toDataURL.
canvas.toDataURL(type, encoderOptions);
The official description isHTMLCanvasElement.toDataURL()
Method returns a data URI containing a representation of the image in the format specified bytype
Parameter (defaults to PNG). The returned image is in a resolution of 96 dpi. in fact, it is used to read The data of The canvas. The default format is png. If the first parameter type is image/jpeg, the second parameter encoderOptions can be used to set the image compression quality. After testing, if it is png format, this method may increase the image size by 100% ~~~~ This is counterproductive, so we can use the canvas. set sWidth and sHeight when using drawImage, for example, to reduce the size by 1.5 times. The image quality does not affect the view, especially for images with relatively large sizes.
There are also unfamiliar methods aboveAtobIt is used for decoding because the base64.
var encodedData = window.btoa("Hello, world"); // encode a stringvar decodedData = window.atob(encodedData); // decode the string
This method may be a bunch of garbled characters,Uint8ArrayThe returned result is an octal integer array.
Blob is the container for storing binary files.A typical Blob Object is an image or sound file. The default format is PNG.
var blob = new Blob([ia], { type: "image/jpeg" });
Finally, send the Blob Object to the server through ajax.
The entire process is roughly as above, ~~~ After the implementation of the test run: "didn't you say that the image is compressed? Why is the upload of the image so slow !", Brother picked up his cell phone and demonstrated it to her sister paper. It was very fast, so he said, "Your mobile phone is not good or the network is not good. You can see that the picture is obviously smaller, it must be faster than before, you can see my second pass ". In other words, we should secretly check the code and log the time in the browser to compare Nima !!! It was just several hundred milliseconds faster !! After a long time of hard work, the previous Code has also been restructured. Let's play with me.
After reading the code above, you can guess where the problem is. That's right,An error occurred while obtaining the length and width of the local image.
I am going to get the local image size of 4 MB. It took 3174 ms !!, The longer the image is, the longer the image is ~
image.onload = function() { var w = image.naturalWidth, h = image.naturalHeight; canvas.width = w / 1.5; canvas.height = h / 1.5; ctx.drawImage(image, 0, 0, w, h, 0, 0, w / 1.5, h / 1.5); Upload.fileUpload(type);};
When retrieving images locally, the browser cannot directly look like file. if the size is the same as the length and width, you can only get the content through FileReader and assign it to the newly created image object. It takes time to download the newly created image object! How can this problem be solved? Isn't it just getting the size of the local image?
So thought of the previous study of the Rapid Acquisition of the image width of the blog, http://www.cnblogs.com/hutuzhu/p/4092907.html, demo address: http://jsbin.com/jivugadure/edit? Html, js, and output query the height or width of an image during loading at regular intervals. You do not need to wait until the entire image is loaded.
After the test, it still does not work, because the regular query method has an effect on the images returned by the regular server. The image address here is base64, and it seems that the time is longer ~ Cry.
Summary:
1. It is feasible to use HTML5 to compress images and upload images. On the mobile end, we do not need to rely on clients or plug-ins. Currently, mainstream browsers have a high degree of support.
2. Image Compression: on the one hand, it is intended to reduce the waiting time for user uploads and the traffic sacrificed by users. In terms of the overall time, it takes time to download images more than once due to image size acquisition, in fact, the difference in compression time is not very large. Unless you find a proper method to directly obtain the image size, please let me know. Thank you very much;
3. Since the time cost is almost the same, we compress the image, reduce the image size, reduce the traffic consumption, storage space, and the next time we get the image, so it is worth it.
Supplemental source code:
(Function ($) {$. extend ($. fn, {fileUpload: function (opts) {this. each (function () {var $ self = $ (this); var quality = opts. quality? Opts. quality/100: 0.2; var dom = {"fileToUpload": $ self. find (". fileToUpload ")," thumb ": $ self. find (". thumb ")," progress ": $ self. find (". upload-progress ")}; var image = new Image (), canvas = document. createElement ("canvas"), ctx = canvas. getContext ('2d '); var funs = {setImageUrl: function (url) {image. src = url;}, bindEvent: function () {console. log (dom. fileToUpload) dom. fileToUpload. on ("change", function () {funs. fileSelect (this) ;}) ;}, fileSelect: function (obj) {var file = obj. files [0]; var reader = new FileReader (); reader. onload = function () {var url = reader. result; funs. setImageUrl (url); dom.thumb.html (image) ;}; image. onload = function () {var w = image. naturalWidth, h = image. naturalHeight; canvas. width = w; canvas. height = h; ctx. drawImage (image, 0, 0, w, h, 0, 0, w, h); funs. fileUpload () ;}; reader. readAsDataURL (file) ;}, fileUpload: function () {var data = canvas. toDataURL ("image/jpeg", quality); // The format of dataURL is "data: image/png; base64, ***", which is descriptive text before commas, we only need the data = data. split (',') [1]; data = window. atob (data); var ia = new Uint8Array (data. length); for (var I = 0; I <data. length; I ++) {ia [I] = data. charCodeAt (I) ;}; // canvas. the default format returned by toDataURL is image/png var blob = new Blob ([ia], {type: "image/jpeg"}); var fd = new FormData (); fd. append ('myfile', blob); var xhr = new XMLHttpRequest (); xhr. addEventListener ("load", opts. success, false); xhr. addEventListener ("error", opts. error, false); xhr. open ("POST", opts. url); xhr. send (fd) ;}}; funs. bindEvent () ;}}}) ;}( Zepto );
Call method:
$(".fileUpload").fileUpload({ "url": "savetofile.php", "file": "myFile", "success":function(evt){ console.log(evt.target.responseText) }});
I hope you can find a better way to communicate with each other! Thank you!