H5图片压缩与上传实例
接到需求,问前端是否可以压缩图片?因为有的图片太大,传到服务器上再压缩太慢了。意识里没有这么玩过,早上老大丢来一个知乎链接,一看,原来前辈们已经用canvas实现了(为自己的见识羞愧3秒钟,再马上开干)!。
canvas压缩
使用了github上的一个现成库:,不得不膜拜下stomita这位大神。大体的思路是将图片抽样显示在canvas上,然后用通过canvas.todataurl方法得到base64字符串来实现压缩。比如在input元素触发change事件之后,读取里面的文件进行操作:
var fileinput = document.getelementbyid('fileinput'); fileinput.onchange = function() { var file = fileinput.files[0]; // 创建一个压缩对象,该构造函数接收file或者blob。 var mpimg = new megapiximage(file); // render方法的maxwith,maxheight,以及quality都决定了压缩图片的质量 var resimg = document.getelementbyid('resultimage'); mpimg.render(resimg, { maxwidth: 300, maxheight: 300, quality: 0.5 }); }; 压缩完成会得到
类似这样的图片:
data:image/jpeg 这样的格式已经应用的很多了,很多样式里面的背景图片直接就是这样。
需要说明的是有两点,这里的resimg是一个预览图片,是已经存在于文档中的,如果你不需要预览,而只是创建一个img用来压缩(document.createelement("img")),这会少一个tagname属性。你可以修改源码或者自己加上这个属性。源码中会根据tagname进行判断,不存在的话会报错:
megapiximage.prototype.render = function (target, options, callback) { //.... target.tagname = target.tagname || "img"; //加上这一句 var tagname = target.tagname.tolowercase(); if (tagname === 'img') { target.src = renderimagetodataurl(this.srcimage, opt, dosquash); } else if (tagname === 'canvas') { renderimagetocanvas(this.srcimage, target, opt, dosquash); } if (typeof this.onrender === 'function') { this.onrender(target); } if (callback) { callback(); } if (this.blob) { this.blob = null; url.revokeobjecturl(this.srcimage.src); } };
另外这是一个耗时的操作,如果是多张图片进行压缩,不能直接调用,需要稍微变换一下,不然会导致前面的图片没有压缩完成就进入到了后面的图片。
fileselected: function () { var files = $("#fileimage")[0].files; var count = files.length; console.log("共有" + count + "个文件"); for (var i = 0; i < count; i++) {var item = files[i]; console.log("原图片大小", item.size); if (item.size > 1024 * 1024 * 2) { console.log("图片大于2m,开始进行压缩..."); (function(img) { var mpimg = new megapiximage(img); var resimg = document.createelement("img"); resimg.file = img; mpimg.render(resimg, { maxwidth: 500, maxheight: 500, quality: 1 }, function() { //do some thing }); })(item); } core.previewimage(item); } },
上传处理
1.直接post base64字符串
uploadbase64str: function (base64str) { var formdata = new formdata(); formdata.append("base64str", base64str); var xhr = new xmlhttprequest(); xhr.upload.addeventlistener("progress", function (e) { var percentcomplete = math.round(e.loaded * 100 / e.total); para.onprogress(percentcomplete.tostring() + '%'); }); xhr.addeventlistener("load", function (e) { para.uploadcomplete(xhr.responsetext); }); xhr.addeventlistener("error", function (e) { para.uploaderror(e); }); xhr.open("post", para.base64strurl, true); xhr.send(formdata); },
比如这里base64strurl是/home/muploadimgbase64str,mvc控制器方法如下:
[httppost] public actionresult muploadimgbase64str(string base64str) { try { var imgdata = base64str.split(',')[1]; //过滤特殊字符即可 string dummydata = imgdata.trim().replace("%", "").replace(",", "").replace(" ", "+"); if (dummydata.length % 4 > 0) { dummydata = dummydata.padright(dummydata.length + 4 - dummydata.length % 4, '='); } byte[] bytearray = convert.frombase64string(dummydata); using (system.io.memorystream ms = new system.io.memorystream(bytearray)) { var img = system.drawing.image.fromstream(ms); var path = "~/content/uploadfiles/mobile/"; var uploadpath = server.mappath(path); if (!directory.exists(uploadpath)) { directory.createdirectory(uploadpath); } var savename = uploadpath + “stoneniqiu” + ".jpg"; img.save(savename); return json(savename); } } catch (exception e) { return json(e.message); } }
几m的图片能压缩到几十k或者几百k,当然,如果宽度、高度和质量设置的太小,图片就会很失真了。这个字符串怎么获取呢?有两个方法,一个是直接读取src:
var base641 = resimg.src;
一个是用canvas转换:
function getbase64image(img) { var canvas = document.createelement("canvas"); canvas.width = img.width; canvas.height = img.height; var ctx = canvas.getcontext("2d"); ctx.drawimage(img, 0, 0, img.width, img.height); var dataurl = canvas.todataurl("image/jpeg"); return dataurl; // return dataurl.replace("data:image/png;base64,", ""); } var base64 = getbase64image(resimg);
同一张图片,这两者获取到的字符串大小不一样,但图片质量我是分辨不出什么差别。
比如一个2m的图片,通过getbase64image方法读到的字符串大小才64k,而src直接读取到的却是270k,各自生成的图片更小。一下分别是原图(2.2m),base64(48k),src(202k)对应的图片。
getbase64image通过canvas的todataurl 获取到更小的base64字符串。
2.也可以在前端转换blob对象,再post到后端
function datauritoblob(dataurl) { var bytestring = atob(dataurl.split(',')[1]); var ab = new arraybuffer(bytestring.length); var ia = new uint8array(ab); for (var i = 0; i < bytestring.length; i++) { ia[i] = bytestring.charcodeat(i); } return new blob([ab], { type: 'image/jpeg' }); }
3.不压缩的就直接装到formdata中,send到后台。
uploadfile: function (file) { console.log("开始上传"); var formdata = new formdata(); formdata.append(para.filebase, file);//这个名字要和mvc后台配合 var xhr = new xmlhttprequest(); xhr.upload.addeventlistener("progress", function (e) { var percentcomplete = math.round(e.loaded * 100 / e.total); para.onprogress(percentcomplete.tostring() + '%'); }); xhr.addeventlistener("load", function (e) { para.uploadcomplete(xhr.responsetext); }); xhr.addeventlistener("error", function (e) { para.uploaderror(e); }); xhr.open("post", para.url, true); xhr.send(formdata); },
全部的代码:(包含压缩和上传以及demo):
github:
小结:基本上就是这样了,前端能够压缩图片的话,确实省了流量和时间。 是在上一篇的基础上进行改进的。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 看人真准
下一篇: php中检查文件或目录是否存在的代码小结