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

最近在做一个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/*" multiple> <input type="hidden" /> <!--设置这个隐藏域是为了便于存放上传至服务器后返回的图片地址-->

接下来就到了js上场了:

//图片上传 var file = { upload: function (e) { var self = this; var files = e.target.files; var type = files[0].type.split('https://www.jb51.net/')[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();

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wyxypz.html