vue图片上传组件

阅读目录

前言:很多项目中都需要用到图片上传功能,而其多处使用的要求,为了避免重复造轮子,让我决定花费一些时间去深入了解,最终封装了一个 vue 的图片上传组件。现将总结再次,希望有帮助。

Layout

  1. <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

  1. 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

回到顶部

大小判断

  1. (oFile.size / 1024) > 1024

1M = 1024KB

回到顶部

Smaller

  1. let form = new FormData();
  2. form.append('file',oFile);
  3. let xhr = new XMLHttpRequest();
  4. xhr.open('post',url,true);
  5. xhr.timeout = 30 * 1000;
  6. xhr.upload.onprogress = this.progress;
  7. xhr.onload = this.uploadComplete;
  8. xhr.onerror = this.uploadFailed;
  9. xhr.upload.onloadstart = () => {
  10. let date = new Date().getTime();
  11. let initSize = 0;
  12. }
  13. 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 时接口更换为处理较大文件的接口即可。

如何压缩?

  1. 通过FileReader对象将文件对象读取成 base64 格式。

  2. 通过Image对象结合 canvas 画布将 base 格式的图片将一定的比例缩小后重新保存成为 base64 格式。

  3. 将 base64 格式转换成 Blob 流后,图片就可以说是压缩完成了。

  4. 剩下的步骤重复formData,XMLHttpRequest即可完成较大图片的上传。这里需要注意的是,在 formData 中防止文件时,因为此时是 Blob 流,所以防止文件时,需要自定义文件格式,这里的处理是 Blob 文件 + 时间戳与.jpg 组成。

组件源码

  1. <template>
  2. <!-- 图片上传组件 -->
  3. <div class="upload-wraper">
  4. <input type="file" id="upload-ele" multiple="false" accept="image/*" @change="uploadFile(url,quality,hasApi,BigUrl)">
  5. <toast v-model="total.isShow" type="text">{{total.text}}</toast>
  6. </div>
  7. </template>
  8. <script>
  9. import { Indicator } from 'mint-ui';
  10. import { Toast } from 'vux';
  11. export default {
  12. name: 'uploadImage',
  13. components: {
  14. Indicator,
  15. Toast,
  16. },
  17. props: {
  18. 'url': String, //小与 1M 的 api
  19. 'quality': Number, //图片质量
  20. 'BigUrl': {
  21. type: String,
  22. default: '',
  23. }, //大于 1M 图片的 api
  24. 'hasApi': {
  25. type: Boolean,
  26. default: false
  27. } //是否对大于 1M 的图片单独分配接口
  28. },
  29. data() {
  30. return {
  31. total: {isShow:false,text:""}
  32. }
  33. },
  34. methods: {
  35. uploadFile(url,quality,hasApi,BigUrl) {
  36. Indicator.open(` 上传中 `);
  37. // files 是 input 设置为 file 后的一个内置对象。files 对象是一个只读属性,不可被修改。
  38. var oFile = document.getElementById('upload-ele').files[0];
  39. console.log('File Object',oFile);
  40. var form = new FormData();
  41. // 大小判断 如果大于 1M 就新型压缩处理
  42. // console.log('File Size Unit:KB',(oFile.size / 1024))
  43. if((oFile.size / 1024) > 1024) {
  44. if(hasApi) {
  45. form.append('file',oFile);
  46. let xhr = new XMLHttpRequest(); //XMLHttpRequest Object
  47. xhr.open('post',BigUrl,true); // Method: post,url: server receive address,true/false isAsync
  48. xhr.timeout = 30 * 1000; //Timeout one minute;
  49. xhr.ontimeout = this.uploadTimeout; // Upload Timeout Function
  50. xhr.upload.onprogress = this.progress; //Progress Function
  51. xhr.onload = this.uploadComplete; //Upload Success Function
  52. xhr.onerror = this.uploadFailed; //Upload Failed Funciton
  53. xhr.upload.onloadstart = () => {
  54. let date = new Date().getTime(); // TimeStamp Prevents Caching
  55. let initSize = 0; // Init File Size Zero
  56. } // Upload Start
  57. xhr.send(form);
  58. } else {
  59. this.imgCompress(oFile,{quality: quality},
  60. (base64Codes) => {
  61. var bl = this.convertBase64UrlToBlob(base64Codes);
  62. form.append("file", bl, "file_" + Date.parse(new Date()) + ".jpg"); // 文件对象
  63. console.log(form);
  64. let xhr = new XMLHttpRequest(); // XMLHttpRequest 对象
  65. xhr.open("post", url, true); //post 方式,url 为服务器请求地址,true 该参数规定请求是否异步处理。
  66. xhr.upload.onprogress = this.progress; //Progress Function
  67. xhr.ontimeout = this.uploadTimeout; // Upload Timeout Function
  68. xhr.onload = this.uploadComplete; //Upload Success Function
  69. xhr.onerror = this.uploadFailed; //Upload Failed Funciton
  70. xhr.upload.onloadstart = function() {
  71. let ot = new Date().getTime(); // TimeStamp Prevents Caching
  72. let oloaded = 0; // Init File Size Zero
  73. };// Upload Start
  74. xhr.send(form);
  75. })
  76. }
  77. } else {
  78. // 小与 1M
  79. form.append('file',oFile);
  80. let xhr = new XMLHttpRequest(); //XMLHttpRequest Object
  81. xhr.open('post',url,true); // Method: post,url: server receive address,true/false isAsync
  82. xhr.timeout = 30 * 1000; //Timeout one minute;
  83. xhr.ontimeout = this.uploadTimeout; // Upload Timeout Function
  84. xhr.upload.onprogress = this.progress; //Progress Function
  85. xhr.onload = this.uploadComplete; //Upload Success Function
  86. xhr.onerror = this.uploadFailed; //Upload Failed Funciton
  87. xhr.upload.onloadstart = () => {
  88. let date = new Date().getTime(); // TimeStamp Prevents Caching
  89. let initSize = 0; // Init File Size Zero
  90. } // Upload Start
  91. xhr.send(form);
  92. }
  93. },
  94. /**
  95. * @description Request Success
  96. */
  97. uploadComplete(evt) {
  98. let res = JSON.parse(evt.target.responseText);
  99. if(evt.target.readyState == 4 && evt.target.status == 200) {
  100. this.$emit('upload',res.result.url);
  101. } else {
  102. this.uploadFailed();
  103. }
  104. },
  105. /**
  106. * @description Request Failed
  107. */
  108. uploadFailed(evt) {
  109. Indicator.close();
  110. this.total = {
  111. isShow:true,
  112. text:"上传失败"
  113. }
  114. },
  115. /**
  116. * @description Timeout Function
  117. */
  118. uploadTimeout(evt) {
  119. this.cancleUploadFile(evt)
  120. Indicator.close();
  121. this.total = {
  122. isShow:true,
  123. text:"请求超时"
  124. }
  125. },
  126. /**e
  127. * @description Upload Cancel
  128. */
  129. cancleUploadFile(evt) {
  130. evt.abort();
  131. },
  132. /**
  133. * @description Requst Loading....
  134. */
  135. progress(progressEvent) {
  136. if(!progressEvent.lengthComputable) {
  137. this.total = {
  138. isShow:true,
  139. text:"进度读取失败"
  140. }
  141. return false;
  142. }
  143. let precent = Math.floor(100 * progressEvent.loaded / progressEvent.total); //Upload Progress
  144. if(precent < 100) {
  145. Indicator.open(` 上传中${precent}%`);
  146. } else {
  147. Indicator.close();
  148. this.total = {
  149. isShow:true,
  150. text:"上传成功"
  151. }
  152. }
  153. },
  154. /**
  155. * @description 图片压缩
  156. * @param {Object} file 压缩的文件
  157. * @param {Number} width 压缩后端宽度,宽度越小,字节越小
  158. */
  159. imgCompress(file,width,callBack) {
  160. var ready = new FileReader();
  161. ready.readAsDataURL(file);
  162. ready.onload = () => {
  163. this.canvasDataURL(ready.result,width,callBack);
  164. }
  165. },
  166. /**
  167. * 将以 base64 的图片 url 数据转换为 Blob
  168. * @param urlData
  169. * 用 url 方式表示的 base64 图片数据
  170. */
  171. convertBase64UrlToBlob(urlData) {
  172. var arr = urlData.split(","),
  173. mime = arr[0].match(/:(.*?);/)[1],
  174. bstr = atob(arr[1]),
  175. n = bstr.length,
  176. u8arr = new Uint8Array(n);
  177. while (n--) {
  178. u8arr[n] = bstr.charCodeAt(n);
  179. }
  180. return new Blob([u8arr], { type: mime });
  181. },
  182. /**
  183. * @description 大于 1M 的图片进行重新绘制压缩
  184. */
  185. canvasDataURL(path, obj, callback) {
  186. var img = new Image();
  187. img.src = path;
  188. img.onload = () => {
  189. // var that = this;
  190. // 默认按比例压缩
  191. var w = this.width,
  192. h = this.height,
  193. scale = w / h;
  194. w = obj.width || w;
  195. h = obj.height || w / scale;
  196. var quality = 0.7; // 默认图片质量为 0.7
  197. //生成 canvas
  198. var canvas = document.createElement("canvas");
  199. var ctx = canvas.getContext("2d");
  200. // 创建属性节点
  201. var anw = document.createAttribute("width");
  202. anw.nodeValue = w;
  203. var anh = document.createAttribute("height");
  204. anh.nodeValue = h;
  205. canvas.setAttributeNode(anw);
  206. canvas.setAttributeNode(anh);
  207. ctx.drawImage(img, 0, 0, w, h);
  208. // 图像质量
  209. if (obj.quality && obj.quality <= 1 && obj.quality > 0) {
  210. quality = obj.quality;
  211. }
  212. // quality 值越小,所绘制出的图像越模糊
  213. var base64 = canvas.toDataURL("image/jpeg", quality);
  214. // 回调函数返回 base64 的值
  215. callback(base64);
  216. };
  217. },
  218. }
  219. }
  220. </script>
  221. <style lang="less" scoped>
  222. .upload-wraper {
  223. width: 100%;
  224. height: 100%;
  225. }
  226. </style>

 

  1. 这里是公众号项目,所以,这里引入的第三方插件为mint-uivux。具体情况视情况而定。

该组件例外封装了,后端是够对较大图片的处理,如果后端已经进行处理,则直接调用。如果没有处理,则进行压缩处理,

组件的封装,可灵活修改,还有很多地方仍待修改

插槽,图片上传后,回显可在组件内部实现。借由 slot 更加完美。

该组件限制了图片文件的上传,其他文件则不行。

引入如下:

  1. <upload-image class="upload" :quality='.7' :url="$base.uploadUrl" :hasApi="false" @upload='uploadImage'></upload-image>

参考文献

  1. js 内置 file(文件)对象。https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file

  2. FormData 对象的。https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData

  3. XMLHttpRequest 对象。https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest

  4. Image 对象。https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image

  5. Canvas 画布。内容较多,建议通过学习视频了解。在本文中主要用于大型图片的压缩处理

  6. base64 和 Blob 的转换,百度很多已经封装好的。可直接使用。eg:https://www.cnblogs.com/jiujiaoyangkang/p/9396043.html


Data: 2018-12-13

author: Csun