H5图片压缩与上传实例


Posted in Javascript onApril 21, 2017

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

canvas压缩

使用了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 }); 
 };
压缩完成会得到

类似这样的图片:

H5图片压缩与上传实例

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);

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

H5图片压缩与上传实例

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

H5图片压缩与上传实例H5图片压缩与上传实例H5图片压缩与上传实例

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/

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript中的prototype使用说明
Apr 13 Javascript
扩展jquery实现客户端表格的分页、排序功能代码
Mar 16 Javascript
找出字符串中出现次数最多的字母和出现次数精简版
Nov 07 Javascript
用js+iframe形成页面的一种遮罩效果的具体实现
Dec 31 Javascript
Javascript学习笔记之函数篇(四):arguments 对象
Nov 23 Javascript
Shell脚本实现Linux系统和进程资源监控
Mar 05 Javascript
学习javascript面向对象 理解javascript对象
Jan 04 Javascript
Vue 让元素抖动/摆动起来的实现代码
May 31 Javascript
JQuery Ajax跨域调用和非跨域调用问题实例分析
Apr 16 jQuery
JSONP解决JS跨域问题的实现
May 25 Javascript
微信小程序文章列表功能完整实例
Jun 03 Javascript
你不知道的 TypeScript 高级类型(小结)
Aug 28 Javascript
H5手机端多文件上传预览插件
Apr 21 #Javascript
ES6新特性八:async函数用法实例详解
Apr 21 #Javascript
.net MVC+Bootstrap下使用localResizeIMG上传图片
Apr 21 #Javascript
jquery中$.fn和图片滚动效果实现的必备知识总结
Apr 21 #jQuery
ES6新特性七:数组的扩充详解
Apr 21 #Javascript
React中ES5与ES6写法的区别总结
Apr 21 #Javascript
ES6新特性六:promise对象实例详解
Apr 21 #Javascript
You might like
php5.3 废弃函数小结
2010/05/16 PHP
php数字转汉字代码(算法)
2011/10/08 PHP
基于Zend的Captcha机制的应用
2013/05/02 PHP
设置php页面编码的两种方法示例介绍
2014/03/03 PHP
PHP 导出Excel示例分享
2014/08/18 PHP
PHP生成器简单实例
2015/05/13 PHP
PHP单元测试配置与使用方法详解
2019/12/27 PHP
基于javascipt-dom编程 table对象的使用
2013/04/22 Javascript
javascript实现yield的方法
2013/11/06 Javascript
jQuery实现异步获取json数据的2种方式
2014/08/29 Javascript
用Node.js通过sitemap.xml批量抓取美女图片
2015/05/28 Javascript
关于原生js中bind函数的简单实现
2016/08/10 Javascript
JS解决iframe之间通信和自适应高度的问题
2016/08/24 Javascript
详解Angular之constructor和ngOnInit差异及适用场景
2017/06/22 Javascript
js学习总结之DOM2兼容处理重复问题的解决方法
2017/07/27 Javascript
JS继承与闭包及JS实现继承的三种方式
2017/10/15 Javascript
Next.js项目实战踩坑指南(笔记)
2018/11/29 Javascript
深入理解react 组件类型及使用场景
2019/03/07 Javascript
node.js ws模块搭建websocket服务端的方法示例
2019/04/25 Javascript
Vue实现input宽度随文字长度自适应操作
2020/07/29 Javascript
[00:20]DOTA2荣耀之路7:-ah fu-抢盾
2018/05/31 DOTA
基于Python实现的百度贴吧网络爬虫实例
2015/04/17 Python
批处理与python代码混合编程的方法
2016/05/19 Python
基于Python实现大文件分割和命名脚本过程解析
2019/09/29 Python
Python TKinter如何自动关闭主窗口
2020/02/26 Python
tensorflow指定CPU与GPU运算的方法实现
2020/04/21 Python
对CSS3选择器的研究(详解)
2016/09/16 HTML / CSS
解决CSS3的opacity属性带来的层叠顺序问题
2016/05/09 HTML / CSS
小程序canvas中文字设置居中锚点
2019/04/16 HTML / CSS
GEOX鞋美国官方网站:意大利会呼吸的鞋
2017/07/12 全球购物
2019年Java面试必问之经典试题
2012/09/12 面试题
幼儿园课题实施方案
2014/05/14 职场文书
没有孩子的离婚协议书怎么写
2014/09/17 职场文书
出国签证在职证明范本
2014/11/24 职场文书
会计试用期工作总结2015
2015/05/28 职场文书
五一放假通知怎么写
2015/08/18 职场文书