接到需求,问前端是否能够压缩图片?由于有的图片太大,传到服务器上再压缩太慢了。意识里没有这么玩过,早上老大丢来一个知乎连接,一看,原来前辈们已经用canvas实现了(为本身的见识羞愧3秒钟,再立刻开干)!。html
使用了github上的一个现成库:https://github.com/stomita/ios-imagefile-megapixel,不得不膜拜下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 }); };
压缩完成会获得相似这样的图片:ios
data:image/jpeg 这样的格式已经应用的不少了,不少样式里面的背景图片直接就是这样。git
须要说明的是有两点,这里的resImg是一个预览图片,是已经存在于文档中的,若是你不须要预览,而只是建立一个img用来压缩(document.createElement("img")),这会少一个tagName属性。你能够修改源码或者本身加上这个属性。源码中会根据tagName进行判断,不存在的话会报错:github
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); } };
另外这是一个耗时的操做,若是是多张图片进行压缩,不能直接调用,须要稍微变换一下,否则会致使前面的图片没有压缩完成就进入到了后面的图片。canvas
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控制器方法以下:api
[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转换:mvc
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:https://github.com/stoneniqiu/h5upload/
小结:基本上就是这样了,前端可以压缩图片的话,确实省了流量和时间。 插件是在上一篇的基础上进行改进的。