移动前端—图片压缩上传实践

   此前有同事跟我聊过关于移动端用canvas压缩图片后再上传的功能,最近有了点空闲时间,所以就实践了一下。demo效果链接在文章底部贴出。

  在做移动端图片上传的时候,用户传的都是手机本地图片,而本地图片一般都相对比较大,拿iphone6来说,平时拍很多图片都是一两M的,如果直接这样上传,那图片就太大了,如果用户用的是移动流量,完全把图片上传显然不是一个好办法。

  目前来说,HTML5的各种新API都在移动端的webkit上得到了较好的实现。根据查看caniuse,本demo里使用到的FileReader、Blob、Formdata对象均已在大部分移动设备浏览器中得到了实现(safari6.0+、android 3.0+),所以直接在前端压缩图片,已经成了很多移动端图片上传的必备功能了。

  在移动端压缩图片并且上传主要用到filereader、canvas 以及 formdata 这三个h5的api。逻辑并不难。整个过程就是:

  (1)用户使用input file上传图片的时候,用filereader读取用户上传的图片数据(base64格式)

  (2)把图片数据传入img对象,然后将img绘制到canvas上,再调用canvas.toDataURL对图片进行压缩

  (3)获取到压缩后的base64格式图片数据,转成二进制塞入formdata,再通过XmlHttpRequest提交formdata。

  如此三步,就完成了图片的压缩和上传。

  说起来好像挺简单,其实还是有些坑的。接下来就直接用代码进行分析:

  【一】获取图片数据

  先是获取图片数据,也就是监听input file的change事件,然后获取到上传的文件对象files,将类数组的files转成数组,然后进行forEach遍历。

  接着判断文件类型,如果不是图片则不作处理。如果是图片就实例化一个filereader,以base64格式读取上传的文件数据,判断数据长度,如果大于200KB的图片就调用compress方法进行压缩,否则调用upload方法进行上传。

filechooser.onchange = function () {if (!this.files.length) return;var files = Array.prototype.slice.call(this.files);if (files.length > 9) {alert("最多同时只可上传9张图片");return;}files.forEach(function (file, i) {if (!/\/(?:jpeg|png|gif)/i.test(file.type)) return;var reader = new FileReader();var li = document.createElement("li");li.innerHTML = '<div class="progress"><span></span></div>';$(".img-list").append($(li));reader.onload = function () {var result = this.result;var img = new Image();img.src = result;//如果图片大小小于200kb,则直接上传if (result.length <= maxsize) {$(li).css("background-image", "url(" + result + ")");img = null;upload(result, file.type, $(li));return;}//                图片加载完毕之后进行压缩,然后上传if (img.complete) {callback();} else {img.onload = callback;}function callback() {var data = compress(img);$(li).css("background-image", "url(" + data + ")");upload(data, file.type, $(li));img = null;}};reader.readAsDataURL(file);})};

  【2】压缩图片

  上面做完图片数据的获取后,就可以做compress压缩图片的方法了。而压缩图片也并不是直接把图片绘制到canvas再调用一下toDataURL就行的。

  在IOS中,canvas绘制图片是有两个限制的:

  首先是图片的大小,如果图片的大小超过两百万像素,图片也是无法绘制到canvas上的,调用drawImage的时候不会报错,但是你用toDataURL获取图片数据的时候获取到的是空的图片数据。

  再者就是canvas的大小有限制,如果canvas的大小大于大概五百万像素(即宽高乘积)的时候,不仅图片画不出来,其他什么东西也都是画不出来的。

  应对第一种限制,处理办法就是瓦片绘制了。瓦片绘制,也就是将图片分割成多块绘制到canvas上,我代码里的做法是把图片分割成100万像素一块的大小,再绘制到canvas上。

  而应对第二种限制,我的处理办法是对图片的宽高进行适当压缩,我代码里为了保险起见,设的上限是四百万像素,如果图片大于四百万像素就压缩到小于四百万像素。四百万像素的图片应该够了,算起来宽高都有2000X2000了。

  如此一来就解决了IOS上的两种限制了。

  除了上面所述的限制,还有两个坑,一个就是canvas的toDataURL是只能压缩jpg的,当用户上传的图片是png的话,就需要转成jpg,也就是统一用canvas.toDataURL('image/jpeg', 0.1) , 类型统一设成jpeg,而压缩比就自己控制了。

  另一个就是如果是png转jpg,绘制到canvas上的时候,canvas存在透明区域的话,当转成jpg的时候透明区域会变成黑色,因为canvas的透明像素默认为rgba(0,0,0,0),所以转成jpg就变成rgba(0,0,0,1)了,也就是透明背景会变成了黑色。解决办法就是绘制之前在canvas上铺一层白色的底色。

function compress(img) {var initSize = img.src.length;var width = img.width;var height = img.height;//如果图片大于四百万像素,计算压缩比并将大小压至400万以下var ratio;if ((ratio = width * height / 4000000)>1) {ratio = Math.sqrt(ratio);width /= ratio;height /= ratio;}else {ratio = 1;}canvas.width = width;canvas.height = height;//        铺底色ctx.fillStyle = "#fff";ctx.fillRect(0, 0, canvas.width, canvas.height);//如果图片像素大于100万则使用瓦片绘制var count;if ((count = width * height / 1000000) > 1) {count = ~~(Math.sqrt(count)+1); //计算要分成多少块瓦片//            计算每块瓦片的宽和高var nw = ~~(width / count);var nh = ~~(height / count);tCanvas.width = nw;tCanvas.height = nh;for (var i = 0; i < count; i++) {for (var j = 0; j < count; j++) {tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh);ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh);}}} else {ctx.drawImage(img, 0, 0, width, height);}//进行最小压缩var ndata = canvas.toDataURL('image/jpeg', 0.1);console.log('压缩前:' + initSize);console.log('压缩后:' + ndata.length);console.log('压缩率:' + ~~(100 * (initSize - ndata.length) / initSize) + "%");tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0;return ndata;}

  【三】图片上传

  完成图片压缩后,就可以塞进formdata里进行上传了,先将base64数据转成字符串,再实例化一个ArrayBuffer,然后将字符串以8位整型的格式传入ArrayBuffer,再通过BlobBuilder或者Blob对象,将8位整型的ArrayBuffer转成二进制对象blob,然后把blob对象append到formdata里,再通过ajax发送给后台即可。

  XmlHttpRequest2中不仅可以发送大数据,还多出了比如获取发送进度的API,我代码里也进行了简单的实现。

//    图片上传,将base64的图片转成二进制对象,塞进formdata上传function upload(basestr, type, $li) {var text = window.atob(basestr.split(",")[1]);var buffer = new ArrayBuffer(text.length);var ubuffer = new Uint8Array(buffer);var pecent = 0 , loop = null;for (var i = 0; i < text.length; i++) {ubuffer[i] = text.charCodeAt(i);}var Builder = window.WebKitBlobBuilder || window.MozBlobBuilder;var blob;if (Builder) {var builder = new Builder();builder.append(buffer);blob = builder.getBlob(type);} else {blob = new window.Blob([buffer], {type: type});}var xhr = new XMLHttpRequest();var formdata = new FormData();formdata.append('imagefile', blob);xhr.open('post', '/cupload');xhr.onreadystatechange = function () {if (xhr.readyState == 4 && xhr.status == 200) {console.log('上传成功:' + xhr.responseText);clearInterval(loop);//当收到该消息时上传完毕$li.find(".progress span").animate({'width': "100%"}, pecent < 95 ? 200 : 0, function () {$(this).html("上传成功");});$(".pic-list").append('<a href="' + xhr.responseText + '">' + xhr.responseText + '<img src="' + xhr.responseText + '" /></a>')}};//数据发送进度,前50%展示该进度xhr.upload.addEventListener('progress', function (e) {if (loop) return;pecent = ~~(100 * e.loaded / e.total) / 2;$li.find(".progress span").css('width', pecent + "%");if (pecent == 50) {mockProgress();}}, false);//数据后50%用模拟进度function mockProgress() {if (loop) return;loop = setInterval(function () {pecent++;$li.find(".progress span").css('width', pecent + "%");if (pecent == 99) {clearInterval(loop);}}, 100)}xhr.send(formdata);}

  

  至此,整个上传的前端图片压缩就完成了,因为是用了formdata提交,所以后台接数据的时候就跟普通form表单提交数据一样处理即可。

  如果对该demo有兴趣的可以看这个demo的github地址:

  前端代码:https://github.com/whxaxes/node-test/blob/master/server/upload/index_2.html  

web 前端图片优化之--图片压缩上传相关推荐

  1. vue实现PC端调用摄像头拍照人脸录入、移动端调用手机前置摄像头人脸录入、及图片旋转矫正、压缩上传base64格式/文件格式

    PC端调用摄像头拍照上传base64格式到后台,这个没什么花里胡哨的骚操作,直接看代码 (canvas + video) <template><div><!--开启摄像头 ...

  2. vue移动端页面调用手机拍照_vue实现PC端调用摄像头拍照、移动端调用手机前置摄像头人脸录入、及图片旋转矫正、压缩上传base64格式/文件格式...

    export default { () { return {} }, methods: { # // 压缩图片 and 旋转角度纠正 下方代码 # 需要自行去掉 个人只作为着色效果加上 compres ...

  3. 解决上传图片自动旋转的问题以及对图片进行压缩上传

    1. 关于图片自动旋转原因 在使用PS或者其他软件旋转图片时,图片旋转了,但Orientation不会改变,由于我们使用的图片预览器能够预处理图片,使其看起来与旋转后一致,但上传图片时,浏览器并不会预 ...

  4. JS前端图片压缩上传

    JS前端图片压缩上传重点知识 最近在做一个手机端的图片上传,写了一个比较符合自己要求的方法,可供参考 在做这个功能模块时,我遇到了以下问题,都花费了大量时间: 1. 不知道怎么压缩图片,(代码和方法) ...

  5. 前端图片压缩上传(压缩篇)

    为什么说这是一篇比较适合小白的前端图片压缩文章呢?因为我也是一个刚工作半年的前端小白,最近接到了一个前端图片压缩上传的任务,通过各种百度博客完成了这项任务,但是任务完成后对各种技术细节却还不是特别理解 ...

  6. 前端图片压缩上传(纯js的质量压缩,非长宽压缩)

    前端图片压缩上传(纯js的质量压缩,非长宽压缩) 此demo为大于1M对图片进行压缩上传 若小于1M则原图上传,可以根据自己实际需求更改. demo源码如下: <!DOCTYPE html> ...

  7. 前端实现图片压缩上传功能

    以移动端为例,在H5上传图片时,由于机型 / 网速 / 流量等限制,在低版本机型上经常会出现上传的图片太大导致上传很慢甚至崩溃的情况,所以需要对在某些情况下需要对上传的图片进行压缩上传. 以下以移动端 ...

  8. uniapp小程序图片前端压缩上传

    目录 1,前言 2,实现代码 1,前言 这次项目中做了一个图片上传,要求是大于2MB的就压缩成2MB一下的再上传,我这边利用了uniapp的文件接口,使用canvas做了一个压缩上传的功能,目前已上线 ...

  9. 前端性能优化(图片优化)

    从输入URL到页面加载完成的过程:首先通过DNS(域名解析)把URL解析为对应的IP地址,然后与该IP地址确定的服务器建立起TCP网络连接.随后向服务器发送HTTP请求,服务器处理完HTTP请求后把目 ...

最新文章

  1. 在循环中删除list中的元素
  2. Apache 配置的性能调优
  3. 强!chrome彻底关闭自动升级新方法实例演示,终于解决了chrome自动升级的烦恼
  4. Spring boot统一日志记录
  5. iar 堆栈设置_IAR MSP430设置合理堆栈大小(the stack pointer for stack is outside the stack range)...
  6. 应用容器公共免费部署平台
  7. php生成格式 word文档,php使用phpword生成word文档
  8. 浏览器了解(四)javascript解析
  9. CSS—内联样式(行内样式)、内部样式、外部样式、选择器
  10. soap错误码和获取处理错误码的详细信息
  11. 航天飞机电子计算机象征是什么,航天飞机的特点是什么
  12. Python实现常用的假设检验
  13. 据说这是一堂价值300万元的课。有空看下,不要错过
  14. VB中LCASE与UCASE
  15. 解决 Hyper-V R2 虚拟网卡影响网速变慢问题
  16. 介绍四种绘制词云图的方法
  17. 浴火重生,燃起奋斗的欲望
  18. veins车载通信仿真框架(3)--OMNET++基础
  19. 雅虎财经数据python 网络爬虫stock股票 用 Python 通过雅虎财经获取股票数据
  20. 基于Java-SQL sever实现人事管理系统-数据库课程设计(附源码)

热门文章

  1. 机器视觉工程师应该知道的23个工业镜头专业术语
  2. 电脑显示nt服务器发生错误,Windows NT常见问题(一)
  3. Ubuntu16.04下安装MATLAB 2016b!完美运行
  4. 鲲鹏devkit开发套件——编译调试工具介绍
  5. 最新历史版本 :LINUX KERNEL 配置编译中文指南
  6. Origin作向下柱状图
  7. linux下实现root用户和其他用户之间转换
  8. 打印机服务器ip修改,打印机服务器ip设置
  9. android 高德地图设置不能旋转_高德地图行车记录仪AR导航怎么设置使用教程
  10. 浏览器打印功能---js