JS移动端/H5同时选择多张图片上传并使用canvas压缩图片


Posted in Javascript onJune 20, 2017

最近在做一个H5的项目,里边涉及到拍照上传图片的功能以及识别图片的功能,这里对识别图片的功能不做赘述,不属本文范畴。我在做完并上线项目后,同事跟我提了一个要求是可不可以同时选择多张图片上传,我做的时候的想法是如果给file表单加了 multiple 属性就没有办法调用手机的摄像头拍照了,如果不加,就无法同时选择多张图片,于是我就照实跟同事说了这个情况。但回头一想,单张图片可以上传,那多张图片呢?于是就有了本文的内容。

HTML5定义了 FileReader 作为文件 API 的重要成员用于读取文件,根据 W3C 的定义,FileReader接口提供了读取文件的方法和包含读取结果的事件模型。

FileReader的实例拥有 4 个方法,其中 3 个用以读取文件,另一个用来中断读取。下面的表格列出了这些方法以及他们的参数和功能,需要注意的是 ,无论读取成功或失败,方法并不会返回读取结果,这一结果存储在 result 属性中。

方法名 参数 描述
abort none 中断读取
readAsBinaryString file 将文件读取为二进制码
readAsDataURL file 将文件读取为 DataURL
readAsText file, [encoding] 将文件读取为文本

readAsText:该方法有两个参数,其中第二个参数是文本的编码方式,默认值为 UTF-8。这个方法非常容易理解,将文件以文本方式读取,读取的结果即是这个文本文件中的内容。

readAsBinaryString:该方法将文件读取为二进制字符串,通常我们将它传送到后端,后端可以通过这段字符串存储文件。

readAsDataURL:这是例子程序中用到的方法,该方法将文件读取为一段以 data: 开头的字符串,这段字符串的实质就是 Data URL,Data URL是一种将小文件直接嵌入文档的方案。这里的小文件通常是指图像与 html 等格式的文件。

FileReader还包含了一套完整的事件模型,用于捕获读取文件时的状态,下面这个表格归纳了这些事件。

事件 描述
onabort 中断时触发
onerror 出错时触发
onload 文件读取成功完成时触发
onloadend 读取完成触发,无论成功或失败
onloadstart 读取开始时触发
onprogress 读取中

文件一旦开始读取,无论成功或失败,实例的 result 属性都会被填充。如果读取失败,则 result 的值为 null ,否则即是读取的结果,绝大多数的程序都会在成功读取文件的时候,抓取这个值。

了解了H5提供的 FileReader 后,我们就使用 FileReader 来实现同事选择多张图片并上传。

首先,在 HTML 加入一个file表单,并设置其为 multiple(浏览器在对multiple、disabled、checked、selected等这类属性进行解析时,只要这些属性存在,默认的就会被解析成true,甭管你设置的是disabled=true或者disabled=false亦或是disabled="",如果不想这些属性起作用,唯有用js来remove掉这些属性,除非你不设置这些属性。),并设置accept="image/*"用以只能选择图片类型的文件,代码如下:

<input type="file" accept="image/*" name="upload" id="upload" multiple>
<input type="hidden" id="hiddenImgUrl" />  <!--设置这个隐藏域是为了便于存放上传至服务器后返回的图片地址-->

接下来就到了js上场了:

//图片上传
var file = {
  upload: function (e) {
    var self = this;
    var files = e.target.files;
    var type = files[0].type.split('/')[0];
    if (type != 'image') {
      alertMsg('请上传图片');
      return;
    }
    //var size = Math.floor(file.size / 1024 / 1024);
    //if (size > 3) {
    //  alert('图片大小不得超过3M');
    //  return;
    //};
    for (var i = 0; i < files.length; i++) {
      var reader = new FileReader();
      reader.readAsDataURL(files[i]);
      reader.onloadstart = function () {
        //用以在上传前加入一些事件或效果,如载入中...的动画效果
      };
      reader.onloadend = function (e) {
        var dataURL = this.result;
        var imaged = new Image();
        imaged.src = dataURL;
        imaged.onload = function () {  //利用canvas对图片进行压缩
          var canvas = document.createElement('canvas');
          var ctx = canvas.getContext('2d');
          var w = 0;
          var h = 0;
          if (this.width > this.height) {
            h = 1000;
            var scale = this.width / this.height;
            h = h > this.height ? this.height : h;
            w = h * scale;
          }
          else {
            w = 1000;
            var scale = this.width / this.height;
            w = w > this.width ? this.width : w
            h = w / scale;
          }
          var anw = document.createAttribute("width");
          var anh = document.createAttribute("height");
          if (this.width > this.height) {
            anw.value = h;
            anh.value = w;
          }
          else {
            anw.value = w;
            anh.value = h;
          }
          canvas.setAttributeNode(anw);
          canvas.setAttributeNode(anh);
          if (this.width > this.height) {
            ctx.translate(h, 0);
            ctx.rotate(90 * Math.PI / 180)
            ctx.drawImage(this, 0, 0, w, h);
            ctx.restore();
          }
          else {
            ctx.drawImage(this, 0, 0, w, h);
          }
          dataURL = canvas.toDataURL('image/jpeg');
          //回调函数用以向数据库提交数据
          self.callback(dataURL);
        };
      };
    }
  },
  event: function () {
    $("#upload").change(function (e) {      
      file.upload(e);
    });
  },
  callback: function (dataURL) {
    $.ajaxSettings.async = false;  //这里必须将ajax的异步改为同步才可以把返回并保存在隐藏域中的图片地址取出同时加在地址栏中作为参数一并传入下一个页面,这样做的目的是因为返回的图片地址不是一个json数组,而是单个的json字符串,所以只能将返回的图片地址json字符串拼接在一起作为参数传到下一个页面
    $.post(url, dataURL, function (res) {  //将base64图片流的图片通过后台转换成普通的图片路径并上传至服务器
      var imgUrl = $("#hiddenImgUrl").val();
      if (res.success) {
        $(".loading").hide();
        if (imgUrl != "") {
          $("#hiddenImgUrl").val(imgUrl + "|" + res.imgUrl);  //中间加一个 | 是为了到下一个页面便于将传过去的图片地址参数转换为数组
        } else {
          $("#hiddenImgUrl").val(res.imgUrl);
        }
        var imgUrl = $("#hiddenImgUrl").val();
        window.location.href = "apply.html?imgUrl=" + imgUrl;
      } else {
        alert(res.message);
      }
    }, "json");
  },
  init: function () {
    this.event();
  }
}
file.init();

由于在通过post向服务器上传时采用了同步的方式,所以我在写项目的过程中,老是无法实现加载中的动画效果,并且把加载中的动画效果放在 reader.onloadstart方法中依旧不起作用,最后只能放在了$("#upload").change(function (e) {})方法中,代码如下:

event: function () {
    $("#upload").change(function (e) {
      $(".loading").show();     
      file.upload(e);
    });
  }

以上是同时上传多张图片并将图片传入下一个页面继续进行后续操作,那么如何在同时上传完多张图片后在本页再预览这些图片呢?其实方法也是很简单的,上边callback函数里边的$.post的返回值里就包含了上传至服务器后的图片路径,将这些路径赋给img标签的src,然后再插入到页面中就OK了,代码如下:

callback: function (dataURL) {    
    $.post(url, dataURL, function (res) {  //将base64图片流的图片通过后台转换成普通的图片路径并上传至服务器
      if (res.success) {
        $(".loading").hide();
        var result = '<div class="result"><img src="'+res.imgUrl+'" alt=""/></div>';
        var div = document.createElement('div');
        div.innerHTML = result;
        document.body.appendChild(div);        
      } else {
        alert(res.message);
      }
    }, "json");
  }

以上在预览图片时由于不需要跳转,不需要传入返回的所有图片的路径作为参数,所以也就不需要将ajax的异步设置为同步了。

以上所述是小编给大家介绍的JS移动端/H5同时选择多张图片上传并使用canvas压缩图片,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
解决 FireFox 下[使用event很麻烦] 的问题.
Aug 22 Javascript
JS 字符串连接[性能比较]
May 10 Javascript
jQuery News Ticker 基于jQuery的即时新闻行情展示插件
Nov 05 Javascript
拉动滚动条加载数据的jquery代码
May 03 Javascript
JS禁用浏览器退格键实现思路及代码
Oct 29 Javascript
js调用浏览器打印模块实现点击按钮触发自定义函数
Mar 21 Javascript
JavaScript基于ajax编辑信息用法实例
Jul 15 Javascript
Vue动态加载异步组件的方法
Nov 21 Javascript
详解webpack-dev-middleware 源码解读
Mar 23 Javascript
js实现带有动画的返回顶部
Aug 09 Javascript
vue动态设置路由权限的主要思路
Jan 13 Vue.js
使用Cargo工具高效创建Rust项目
Aug 14 Javascript
BootStrap Select清除选中的状态恢复默认状态
Jun 20 #Javascript
Vue实现路由跳转和嵌套
Jun 20 #Javascript
Vue.2.0.5实现Class 与 Style 绑定的实例
Jun 20 #Javascript
JS中的三个循环小结
Jun 20 #Javascript
详解Vue 方法与事件处理器
Jun 20 #Javascript
Vue Ajax跨域请求实例详解
Jun 20 #Javascript
详解webpack 入门总结和实践(按需异步加载,css单独打包,生成多个入口文件)
Jun 20 #Javascript
You might like
2020年4月放送!《Princess Connect Re:Dive》制作组 & 角色声优公开!
2020/03/06 日漫
PHP 加密/解密函数 dencrypt(动态密文,带压缩功能,支持中文)
2009/01/30 PHP
php 文件夹删除、php清除缓存程序
2009/08/25 PHP
php中静态类与静态变量用法的区别分析
2015/01/15 PHP
PHP代码优化技巧小结
2015/09/29 PHP
Laravel框架使用Seeder实现自动填充数据功能
2018/06/13 PHP
ASP Json Parser修正版
2009/12/06 Javascript
JS Map 和 List 的简单实现代码
2013/07/08 Javascript
一个简单的JS时间控件示例代码(JS时分秒时间控件)
2013/11/22 Javascript
js 时间格式与时间戳的相互转换示例代码
2013/12/25 Javascript
JavaScript事件类型中焦点、鼠标和滚轮事件详解
2016/01/25 Javascript
jQuery EasyUI 为Combo,Combobox添加清除值功能的实例
2017/04/13 jQuery
JavaScript中引用vs复制示例详析
2018/12/06 Javascript
VUE v-model表单数据双向绑定完整示例
2019/01/21 Javascript
node.js中ws模块创建服务端和客户端,网页WebSocket客户端
2019/03/06 Javascript
js计算两个时间差 天 时 分 秒 毫秒的代码
2019/05/21 Javascript
vue使用高德地图根据坐标定位点的实现代码
2019/08/22 Javascript
sqlalchemy对象转dict的示例
2014/04/22 Python
Python中除法使用的注意事项
2014/08/21 Python
python去除文件中空格、Tab及回车的方法
2016/04/12 Python
Python序列化基础知识(json/pickle)
2017/10/19 Python
解决python selenium3启动不了firefox的问题
2018/10/13 Python
python自动化生成IOS的图标
2018/11/13 Python
Python数据可视化:箱线图多种库画法
2019/11/06 Python
深入浅析python的第三方库pandas
2020/02/13 Python
django-csrf使用和禁用方式
2020/03/13 Python
python实现将range()函数生成的数字存储在一个列表中
2020/04/02 Python
numpy的Fancy Indexing和array比较详解
2020/06/11 Python
python BeautifulSoup库的安装与使用
2020/12/17 Python
CSS3实现可关闭的下拉手风琴菜单效果
2015/08/31 HTML / CSS
机电专业大学生职业规划书范文
2014/02/25 职场文书
白莲教口号
2014/06/18 职场文书
试用期员工工作自我评价
2014/09/10 职场文书
政府领导干部个人对照检查材料思想汇报
2014/09/24 职场文书
职代会闭幕词
2015/01/28 职场文书
MySQL 逻辑备份 into outfile
2022/05/15 MySQL