接到需求,问前端是否可以压缩图片?因为有的图片太大,传到服务器上再压缩太慢了。意识里没有这么玩过,早上老大丢来一个知乎链接,一看,原来前辈们已经用canvas实现了(为自己的见识羞愧3秒钟,再马上开干)!。

canvas压缩

使用了github上的一个现成库:https://github.com/stomita/ios-imagefile-megapixel,不得不膜拜下stomita这位大神。大体的思路是将图片抽样显示在canvas上,然后用通过canvas.toDataURL方法得到base64字符串来实现压缩。比如在input元素触发change事件之后,读取里面的文件进行操作:

  1. var fileInput = document.getElementById('fileInput');
  2. fileInput.onchange = function() {
  3. var file = fileInput.files[0];
  4. // 创建一个压缩对象,该构造函数接收file或者blob。
  5. var mpImg = new MegaPixImage(file);
  6. // render方法的maxWith,maxHeight,以及quality都决定了压缩图片的质量
  7. var resImg = document.getElementById('resultImage');
  8. mpImg.render(resImg, { maxWidth: 300, maxHeight: 300, quality: 0.5 });
  9. };

压缩完成会得到类似这样的图片:

data:image/jpeg 这样的格式已经应用的很多了,很多样式里面的背景图片直接就是这样。

需要说明的是有两点,这里的resImg是一个预览图片,是已经存在于文档中的,如果你不需要预览,而只是创建一个img用来压缩(document.createElement("img")),这会少一个tagName属性。你可以修改源码或者自己加上这个属性。源码中会根据tagName进行判断,不存在的话会报错:

  1. MegaPixImage.prototype.render = function (target, options, callback) {
  2. //....
  3. target.tagName = target.tagName || "IMG"; //加上这一句
  4. var tagName = target.tagName.toLowerCase();
  5. if (tagName === 'img') {
  6. target.src = renderImageToDataURL(this.srcImage, opt, doSquash);
  7. } else if (tagName === 'canvas') {
  8. renderImageToCanvas(this.srcImage, target, opt, doSquash);
  9. }
  10. if (typeof this.onrender === 'function') {
  11. this.onrender(target);
  12. }
  13. if (callback) {
  14. callback();
  15. }
  16. if (this.blob) {
  17. this.blob = null;
  18. URL.revokeObjectURL(this.srcImage.src);
  19. }
  20. };

另外这是一个耗时的操作,如果是多张图片进行压缩,不能直接调用,需要稍微变换一下,不然会导致前面的图片没有压缩完成就进入到了后面的图片。

  1. fileSelected: function () {
  2. var files = $("#fileImage")[0].files;
  3. var count = files.length;
  4. console.log("共有" + count + "个文件");
  5. for (var i = 0; i < count; i++) {var item = files[i];
  6. console.log("原图片大小", item.size);
  7. if (item.size > 1024 * 1024 * 2) {
  8. console.log("图片大于2M,开始进行压缩...");
  9.  
  10. (function(img) {
  11. var mpImg = new MegaPixImage(img);
  12. var resImg = document.createElement("img");
  13. resImg.file = img;
  14. mpImg.render(resImg, { maxWidth: 500, maxHeight: 500, quality: 1 }, function() {
  15. //do some thing
  16. });
  17. })(item);
  18.  
  19. }
  20. core.previewImage(item);
  21. }
  22. },

上传处理

1.直接post base64字符串

  1. uploadBase64str: function (base64Str) {
  2. var formdata = new FormData();
  3. formdata.append("base64str", base64Str);
  4. var xhr = new XMLHttpRequest();
  5. xhr.upload.addEventListener("progress", function (e) {
  6. var percentComplete = Math.round(e.loaded * 100 / e.total);
  7. para.onProgress(percentComplete.toString() + '%');
  8. });
  9. xhr.addEventListener("load", function (e) {
  10. para.uploadComplete(xhr.responseText);
  11. });
  12. xhr.addEventListener("error", function (e) {
  13. para.uploadError(e);
  14. });
  15.  
  16. xhr.open("post", para.base64strUrl, true);
  17. xhr.send(formdata);
  18. },

比如这里base64strUrl是/home/MUploadImgBase64Str,MVC控制器方法如下:

  1. [HttpPost]
  2. public ActionResult MUploadImgBase64Str(string base64str)
  3. {
  4. try
  5. {
  6. var imgData = base64str.Split(',')[];
  7. //过滤特殊字符即可
  8. string dummyData = imgData.Trim().Replace("%", "").Replace(",", "").Replace(" ", "+");
  9. if (dummyData.Length % > )
  10. {
  11. dummyData = dummyData.PadRight(dummyData.Length + - dummyData.Length % , '=');
  12. }
  13. byte[] byteArray = Convert.FromBase64String(dummyData);
  14. using (System.IO.MemoryStream ms = new System.IO.MemoryStream(byteArray))
  15. {
  16. var img = System.Drawing.Image.FromStream(ms);
  17.  
  18. var path = "~/Content/UploadFiles/mobile/";
  19. var uploadpath = Server.MapPath(path);
  20. if (!Directory.Exists(uploadpath))
  21. {
  22. Directory.CreateDirectory(uploadpath);
  23. }
  24. var saveName = uploadpath + stoneniqiu + ".jpg";
  25. img.Save(saveName);
  26. return Json(saveName);
  27. }
  28. }
  29. catch (Exception e)
  30. {
  31. return Json(e.Message);
  32.  
  33. }
  34. }

几M的图片能压缩到几十k或者几百k,当然,如果宽度、高度和质量设置的太小,图片就会很失真了。这个字符串怎么获取呢?有两个方法,一个是直接读取src:

  1. var base641 = resImg.src;

一个是用canvas转换:

  1. function getBase64Image(img) {
  2. var canvas = document.createElement("canvas");
  3. canvas.width = img.width;
  4. canvas.height = img.height;
  5.  
  6. var ctx = canvas.getContext("2d");
  7. ctx.drawImage(img, 0, 0, img.width, img.height);
  8.  
  9. var dataURL = canvas.toDataURL("image/jpeg");
  10. return dataURL;
  11.  
  12. // return dataURL.replace("data:image/png;base64,", "");
  13. }
  14. var base64 = getBase64Image(resImg);

同一张图片,这两者获取到的字符串大小不一样,但图片质量我是分辨不出什么差别。

比如一个2M的图片,通过getBase64Image方法读到的字符串大小才64k,而src直接读取到的却是270k,各自生成的图片更小。一下分别是原图(2.2M),base64(48k),src(202k)对应的图片。

   

getBase64Image通过canvas的toDataURL 获取到更小的base64字符串。

2.也可以在前端转换blob对象,再post到后端

  1. function dataURItoBlob(dataUrl) {
  2.  
  3. var byteString = atob(dataUrl.split(',')[1]);
  4. var ab = new ArrayBuffer(byteString.length);
  5. var ia = new Uint8Array(ab);
  6. for (var i = 0; i < byteString.length; i++) {
  7. ia[i] = byteString.charCodeAt(i);
  8. }
  9. return new Blob([ab], { type: 'image/jpeg' });
  10. }

3.不压缩的就直接装到formdata中,send到后台。

  1. uploadFile: function (file) {
  2. console.log("开始上传");
  3. var formdata = new FormData();
  4.  
  5. formdata.append(para.filebase, file);//这个名字要和mvc后台配合
  6.  
  7. var xhr = new XMLHttpRequest();
  8. xhr.upload.addEventListener("progress", function (e) {
  9.  
  10. var percentComplete = Math.round(e.loaded * 100 / e.total);
  11. para.onProgress(percentComplete.toString() + '%');
  12. });
  13. xhr.addEventListener("load", function (e) {
  14. para.uploadComplete(xhr.responseText);
  15. });
  16. xhr.addEventListener("error", function (e) {
  17. para.uploadError(e);
  18. });
  19.  
  20. xhr.open("post", para.url, true);
  21.  
  22. xhr.send(formdata);
  23. },

全部的代码:(包含压缩和上传以及demo):

github:https://github.com/stoneniqiu/h5upload/

小结:基本上就是这样了,前端能够压缩图片的话,确实省了流量和时间。 插件是在上一篇的基础上进行改进的。

H5图片压缩与上传的更多相关文章

  1. 项目分享五:H5图片压缩与上传

    一.简介 图片的压缩与上传,是APP里一个很常用的功能.我们来年看 ChiTuStore 是怎样做的.相关文件 App/Module/User/UserInfo.html,App/Module/Use ...

  2. 使用ajax上传图片,支持图片即时浏览,支持js图片压缩后上传给服务器

    使用ajax上传图片,支持图片即时浏览,支持js图片压缩后上传给服务器 ajax上传主要使用了 var reader = new FileReader() 此方法 js图片压缩主要是利用canvas进 ...

  3. (转)Android学习-使用Async-Http实现图片压缩并上传功能

    (转)Android学习-使用Async-Http实现图片压缩并上传功能 文章转载自:作者:RyaneLee链接:http://www.jianshu.com/p/940fc7ba39e1 让我头疼一 ...

  4. 移动端h5拍照压缩即时上传后台并预览

    项目经理让迭代一个功能,实时预览并上传到后台的功能,听到这立马想起了几个第三方插件去实现,mui  和api cloude万万没想到的是这个app前面使用ios 和安卓原生写的,然后mui和api c ...

  5. Android的图片压缩并上传

    Android开发中上传图片很常见,一般为了节省流量会进行压缩的操作,本篇记录一下压缩和上传的方法. 图片压缩的方法 : import java.io.ByteArrayOutputStream; i ...

  6. vue开发中vue-resource + canvas 图片压缩、上传、预览

    1.使用vue-resource上传,也可以自定义ajax上传: 2.使用<input type="file" @change="submit()" na ...

  7. js图片压缩+ajax上传

    图片压缩用到了localresizeimg 地址: https://github.com/think2011/localResizeIMG 用起来比较简单 <input type="f ...

  8. H5图片预览、压缩、上传

    目标实现: 1.选择图片, 前端预览效果 2.图片大于1.2M的时候, 对图片进行压缩 3.以表单的形式上传图片 4.图片删除 预览效果图: 代码说明: 1.input:file选择图片 <!- ...

  9. 基于h5的图片无刷新上传(uploadifive)

    基于h5的图片无刷新上传(uploadifive) uploadifive简介 了解uploadify之前,首先了解来一下什么是uploadify,uploadfy官网,uploadify和uploa ...

随机推荐

  1. 阿里巴巴直播内容风险防控中的AI力量

    直播作为近来新兴的互动形态和今年阿里巴巴双十一的一大亮点,其内容风险监控是一个全新的课题,技术的挑战非常大,管控难点主要包括业界缺乏成熟方案和标准.主播行为.直播内容不可控.峰值期间数千路高并发处理. ...

  2. 通过一个demo了解Redux

    TodoList小demo 效果展示 项目地址 (单向)数据流 数据流是我们的行为与响应的抽象:使用数据流能帮我们明确了行为对应的响应,这和react的状态可预测的思想是不谋而合的. 常见的数据流框架 ...

  3. DDD 领域驱动设计-商品建模之路

    最近在做电商业务中,有关商品业务改版的一些东西,后端的架构设计采用现在很流行的微服务,有关微服务的简单概念: 微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成.系统中的各个微服务可被独 ...

  4. [原] KVM 虚拟化原理探究(2)— QEMU启动过程

    KVM 虚拟化原理探究- QEMU启动过程 标签(空格分隔): KVM [TOC] 虚拟机启动过程 第一步,获取到kvm句柄 kvmfd = open("/dev/kvm", O_ ...

  5. 查看mac中磁盘空间占用情况

    今天发现磁盘空间不够了,首先要找到那些文件夹占用了磁盘空间. du命令很好使 du -c -d 1 -m | sort -n -c 显示当前文件夹总计占用空间 -d 1 层级为1,即只显示当前目录下一 ...

  6. MySQL:常见使用问题

    内容 1.Linux 上安装 MySQL 2.单机上安装多实例 3.不知root密码情况下,修改root密码 1.Linux 上安装MySQL 安装步骤: 1)解压 tar.gz文件 -linux-g ...

  7. org.apache.ibatis.binding.BindingException: Invalid bound statement (not found):

    org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): da.huying.usermanag ...

  8. C++常考面试题汇总

    c++面试题 一 用简洁的语言描述 c++ 在 c 语言的基础上开发的一种面向对象编程的语言: 应用广泛: 支持多种编程范式,面向对象编程,泛型编程,和过程化编程:广泛应用于系统开发,引擎开发:支持类 ...

  9. Xamarin.Android活动的生命周期

    一.前言 用过Android手机的人一定会发现一种现象,当你把一个应用置于后台后,一段时间之后在打开就会发现应用重新打开了,但是之前的相关的数据却没有丢失.可以看出app的“生命”是掌握在系统手上的, ...

  10. JAVA通信系列三:Netty入门总结

    一.Netty学习资料 书籍<Netty In Action中文版> 对于Netty的十一个疑问http://news.cnblogs.com/n/205413/ 深入浅出Nettyhtt ...