Layout
<div class="upload-wraper">
<input type="file" id="upload_ele" multiple="false" accept="image/*" @change="uploadFile()" />
</div>
type=file将类型设置为选择文件
multiple是否允许文件的多选
accept="image/*" 将文件的类型限制为image类型,*包括所有格式的图片
change事件当type设置为file后,出发的事件为change,也可通过submit实现
这里布局的话,因为是vue组件所以简单点,不需要多个input构成form表单,然后通过submit提交,一个input通过change事件来实现上传
Js
Basic information for uploading files
let oFIle = document.getElementById('upload-ele').files[0];
files是input设置为file后的一个js内置对象。files对象死一个read-only属性,不可被修改!
打印出oFile后可以看到该文件对象的basic information.如下:
isClosed:false 是否已经结束,可以理解为标签是否闭合
lastModified:1539602132000最后更改的时间timeStamp
lastModifiedDate:Mon Oct 15 2018 19:15:32 GMT+0800 (CST) {}最后更改时间
name:"D9791645A5DF19D17FD7392A080E7A28.jpg"图片的名称
path:"/Users/mac/Documents/D9791645A5DF19D17FD7392A080E7A28.jpg"图片所在的路径为本地路径
Size:38938图片的大小信息 单位为kb
type:'image/jpeg'图片的类型
webkitRelativePath:""文件相关的路径
File Size Processing
大小判断
(oFile.size / 1024) > 1024
1M = 1024KB
Smaller
let form = new FormData(); form.append('file',oFile); let xhr = new XMLHttpRequest(); xhr.open('post',url,true); xhr.timeout = 30 * 1000; xhr.upload.onprogress = this.progress; xhr.onload = this.uploadComplete; xhr.onerror = this.uploadFailed; xhr.upload.onloadstart = () => { let date = new Date().getTime(); let initSize = 0; } xhr.send(form);
XMLHttpRequest()是js内置对象,可以使用该属性实现请求头的处理操作。
xhr.open(); 请求方法: post,url: 服务器接受的地址,true/false 是否异步
xhr.timeout; 设置超时时间,据情况而定
xhr.ontimeout; 超时处理,一般为取消请求
xhr.upload.onprogress; 进程处理 ,上传文件的进度处理
xhr.onload; 请求成功处理
xhr.onerror; 请求失败处理
Xhr.upload.onloadstart; 请求开始处理的操作,一般创建时间戳,初始化大小。
xhr.send(); 请求配置完毕,发送请求
这里小于1M的文件是可以直接通过放到formData中,通过配置xhr将图片对象上传到oss,在请求成功时拿到图片的网络路径供后,提供给后端。
Larger
why:为什么要对大于1M的图片进行处理呢?因为文件在大于1M的一般是上传失败的。常见的ftp文件上传时,默认是2M,大于时,会上传失败,所以这里的图片上传进行了大于1M压缩的处理。
how:流程与小于1M时十分相似,不过,这里添加了图片的压缩处理。
more:一般在较大文件处理时,后端也是可以进行处理的,单独提供较大图片的接口。这时,我们就不需要进行压缩了,只需要在判断大于1024KB时接口更换为处理较大文件的接口即可。
如何压缩?
-
通过FileReader对象将文件对象读取成base64格式。
-
通过Image对象结合canvas画布将base格式的图片将一定的比例缩小后重新保存成为base64格式。
-
将base64格式转换成Blob流后,图片就可以说是压缩完成了。
-
剩下的步骤重复formData,XMLHttpRequest即可完成较大图片的上传。这里需要注意的是,在formData中防止文件时,因为此时是Blob流,所以防止文件时,需要自定义文件格式,这里的处理是 Blob文件 + 时间戳与.jpg组成。
组件源码
<template> <!-- 图片上传组件 --> <div class="upload-wraper"> <input type="file" id="upload-ele" multiple="false" accept="image/*" @change="uploadFile(url,quality,hasApi,BigUrl)"> <toast v-model="total.isShow" type="text">{{total.text}}</toast> </div> </template> <script> import { Indicator } from 'mint-ui'; import { Toast } from 'vux'; export default { name: 'uploadImage', components: { Indicator, Toast, }, props: { 'url': String, //小与1M的api 'quality': Number, //图片质量 'BigUrl': { type: String, default: '', }, //大于1M图片的api 'hasApi': { type: Boolean, default: false } //是否对大于1M的图片单独分配接口 }, data() { return { total: {isShow:false,text:""} } }, methods: { uploadFile(url,quality,hasApi,BigUrl) { Indicator.open(`上传中`); // files是input设置为file后的一个内置对象。files对象是一个只读属性,不可被修改。 var oFile = document.getElementById('upload-ele').files[0]; console.log('File Object',oFile); var form = new FormData(); // 大小判断 如果大于1M就新型压缩处理 // console.log('File Size Unit:KB',(oFile.size / 1024)) if((oFile.size / 1024) > 1024) { if(hasApi) { form.append('file',oFile); let xhr = new XMLHttpRequest(); //XMLHttpRequest Object xhr.open('post',BigUrl,true); // Method: post,url: server receive address,true/false isAsync xhr.timeout = 30 * 1000; //Timeout one minute; xhr.ontimeout = this.uploadTimeout; // Upload Timeout Function xhr.upload.onprogress = this.progress; //Progress Function xhr.onload = this.uploadComplete; //Upload Success Function xhr.onerror = this.uploadFailed; //Upload Failed Funciton xhr.upload.onloadstart = () => { let date = new Date().getTime(); // TimeStamp Prevents Caching let initSize = 0; // Init File Size Zero } // Upload Start xhr.send(form); } else { this.imgCompress(oFile,{quality: quality}, (base64Codes) => { var bl = this.convertBase64UrlToBlob(base64Codes); form.append("file", bl, "file_" + Date.parse(new Date()) + ".jpg"); // 文件对象 console.log(form); let xhr = new XMLHttpRequest(); // XMLHttpRequest 对象 xhr.open("post", url, true); //post方式,url为服务器请求地址,true 该参数规定请求是否异步处理。 xhr.upload.onprogress = this.progress; //Progress Function xhr.ontimeout = this.uploadTimeout; // Upload Timeout Function xhr.onload = this.uploadComplete; //Upload Success Function xhr.onerror = this.uploadFailed; //Upload Failed Funciton xhr.upload.onloadstart = function() { let ot = new Date().getTime(); // TimeStamp Prevents Caching let oloaded = 0; // Init File Size Zero };// Upload Start xhr.send(form); }) } } else { // 小与1M form.append('file',oFile); let xhr = new XMLHttpRequest(); //XMLHttpRequest Object xhr.open('post',url,true); // Method: post,url: server receive address,true/false isAsync xhr.timeout = 30 * 1000; //Timeout one minute; xhr.ontimeout = this.uploadTimeout; // Upload Timeout Function xhr.upload.onprogress = this.progress; //Progress Function xhr.onload = this.uploadComplete; //Upload Success Function xhr.onerror = this.uploadFailed; //Upload Failed Funciton xhr.upload.onloadstart = () => { let date = new Date().getTime(); // TimeStamp Prevents Caching let initSize = 0; // Init File Size Zero } // Upload Start xhr.send(form); } }, /** * @description Request Success */ uploadComplete(evt) { let res = JSON.parse(evt.target.responseText); if(evt.target.readyState == 4 && evt.target.status == 200) { this.$emit('upload',res.result.url); } else { this.uploadFailed(); } }, /** * @description Request Failed */ uploadFailed(evt) { Indicator.close(); this.total = { isShow:true, text:"上传失败" } }, /** * @description Timeout Function */ uploadTimeout(evt) { this.cancleUploadFile(evt) Indicator.close(); this.total = { isShow:true, text:"请求超时" } }, /**e * @description Upload Cancel */ cancleUploadFile(evt) { evt.abort(); }, /** * @description Requst Loading.... */ progress(progressEvent) { if(!progressEvent.lengthComputable) { this.total = { isShow:true, text:"进度读取失败" } return false; } let precent = Math.floor(100 * progressEvent.loaded / progressEvent.total); //Upload Progress if(precent < 100) { Indicator.open(`上传中${precent}%`); } else { Indicator.close(); this.total = { isShow:true, text:"上传成功" } } }, /** * @description 图片压缩 * @param {Object} file 压缩的文件 * @param {Number} width 压缩后端宽度,宽度越小,字节越小 */ imgCompress(file,width,callBack) { var ready = new FileReader(); ready.readAsDataURL(file); ready.onload = () => { this.canvasDataURL(ready.result,width,callBack); } }, /** * 将以base64的图片url数据转换为Blob * @param urlData * 用url方式表示的base64图片数据 */ convertBase64UrlToBlob(urlData) { var arr = urlData.split(","), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n); } return new Blob([u8arr], { type: mime }); }, /** * @description 大于1M的图片进行重新绘制压缩 */ canvasDataURL(path, obj, callback) { var img = new Image(); img.src = path; img.onload = () => { // var that = this; // 默认按比例压缩 var w = this.width, h = this.height, scale = w / h; w = obj.width || w; h = obj.height || w / scale; var quality = 0.7; // 默认图片质量为0.7 //生成canvas var canvas = document.createElement("canvas"); var ctx = canvas.getContext("2d"); // 创建属性节点 var anw = document.createAttribute("width"); anw.nodeValue = w; var anh = document.createAttribute("height"); anh.nodeValue = h; canvas.setAttributeNode(anw); canvas.setAttributeNode(anh); ctx.drawImage(img, 0, 0, w, h); // 图像质量 if (obj.quality && obj.quality <= 1 && obj.quality > 0) { quality = obj.quality; } // quality值越小,所绘制出的图像越模糊 var base64 = canvas.toDataURL("image/jpeg", quality); // 回调函数返回base64的值 callback(base64); }; }, } } </script> <style lang="less" scoped> .upload-wraper { width: 100%; height: 100%; } </style>
这里是公众号项目,所以,这里引入的第三方插件为mint-ui和vux。具体情况视情况而定。
该组件例外封装了,后端是够对较大图片的处理,如果后端已经进行处理,则直接调用。如果没有处理,则进行压缩处理,
组件的封装,可灵活修改,还有很多地方仍待修改
插槽,图片上传后,回显可在组件内部实现。借由slot更加完美。
该组件限制了图片文件的上传,其他文件则不行。
引入如下:
<upload-image class="upload" :quality='.7' :url="$base.uploadUrl" :hasApi="false" @upload='uploadImage'></upload-image>
参考文献
-
js内置file(文件)对象。https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file
-
FormData对象的。https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData
-
XMLHttpRequest对象。https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
-
Image对象。https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image
-
Canvas画布。内容较多,建议通过学习视频了解。在本文中主要用于大型图片的压缩处理。
-
base64和Blob的转换,百度很多已经封装好的。可直接使用。eg:https://www.cnblogs.com/jiujiaoyangkang/p/9396043.html
Data: 2018-12-13
author: Csun