• Vue2 | Vant uploader实现上传文件和图片


    需求:

    实现图片和文件的上传,单个图片超过1M则压缩,全部文件加起来不得超过10M。

    效果:

    1. html

    1. <van-form ref="form">
    2. <van-field name="uploader" label="佐证材料" required>
    3. <template #input>
    4. <van-uploader
    5. v-model="files"
    6. multiple
    7. accept=""
    8. :before-read="beforeRead"
    9. :after-read="afterRead"
    10. @delete="deleteImg"
    11. />
    12. template>
    13. van-field>
    14. van-form>

    2. js:

    1. data() {
    2. return {
    3. files: [],
    4. fileList: [], // 已上传的图片
    5. fileIds: [], // 佐证材料文件id,上传成功之后返回的数据
    6. uploadfile: {
    7. type: '',
    8. name: ''
    9. }, // 上传单图的情况
    10. uploadfiles: [], // 上传多图的情况
    11. }
    12. },
    13. methods: {
    14. // 文件读取前触发
    15. beforeRead(e) {
    16. if (e.size > 10 * 1024 * 1024) {
    17. this.$toast.fail('文件大小不能超过10M')
    18. return false
    19. }
    20. return true
    21. },
    22. // 文件读取完成后触发
    23. afterRead(file) {
    24. if (file.length > 0) {
    25. // 多个上传
    26. file.map((item, index) => {
    27. this.uploadfiles.push({
    28. name: item.file.name,
    29. type: item.file.type
    30. })
    31. this.files[index].status = 'uploading'
    32. this.files[index].message = '上传中...'
    33. this.imgPreview(file[index].file, index)
    34. })
    35. } else {
    36. // 单个上传
    37. this.uploadfile.name = file.file.name // 获取文件名
    38. this.uploadfile.type = file.file.type // 获取类型
    39. this.files[this.files.length - 1].status = 'uploading'
    40. this.files[this.files.length - 1].message = '上传中...'
    41. // console.log('绑定的文件', this.files)
    42. this.imgPreview(file.file)
    43. }
    44. },
    45. // 删除文件
    46. deleteImg(file) {
    47. // 匹配fileList的项的fileName,相同的话把filesId对应删除
    48. this.fileList.map((item, index) => {
    49. if (item.file.name == file.file.name) {
    50. this.fileList.splice(index, 1)
    51. this.fileIds.splice(index, 1)
    52. }
    53. })
    54. },
    55. // 处理文件
    56. async imgPreview(file, index) {
    57. const self = this
    58. // 看支持不支持FileReader
    59. if (!file || !window.FileReader) return
    60. if (/^image/.test(file.type)) {
    61. // 创建一个reader
    62. const reader = new FileReader()
    63. // 将图片转成 base64 格式
    64. reader.readAsDataURL(file)
    65. // 读取成功后的回调
    66. reader.onloadend = function () {
    67. const result = this.result
    68. const img = new Image()
    69. img.src = result
    70. // 判断图片是否小于1M,是就直接上传,反之压缩图片
    71. if (this.result.length <= 1024 * 1024) {
    72. // 上传图片
    73. self.postImg(this.result, index)
    74. } else {
    75. img.onload = function () {
    76. const data = self.compress(img)
    77. // 上传图片
    78. self.postImg(data, index)
    79. }
    80. }
    81. }
    82. } else {
    83. // 其他格式的文件
    84. const formData = new window.FormData()
    85. formData.append('file', file)
    86. // 计算现在所有图片的大小,如果加上该文件超过10M则不可上传
    87. let totalSize = 0
    88. this.fileList.map(item => {
    89. totalSize += Number(item.file.size)
    90. })
    91. let size = totalSize + file.size
    92. if (size > 10 * 1024 * 1024) {
    93. this.$toast.fail('当前上传附件超过最大内存10M!')
    94. return
    95. }
    96. const res = await businessUpload(formData)
    97. if (res.code === 200) {
    98. this.$toast.success('文件已上传')
    99. this.fileIds.push(res.fileId)
    100. this.fileList.push({
    101. id: res.fileId,
    102. file: file
    103. })
    104. if (index == undefined) {
    105. this.files[this.files.length - 1].status = 'done'
    106. this.files[this.files.length - 1].message = '上传成功'
    107. } else {
    108. this.files[index].status = 'done'
    109. this.files[index].message = '上传成功'
    110. }
    111. } else {
    112. this.$toast.fail('文件上传失败')
    113. if (index == undefined) {
    114. this.files[this.files.length - 1].status = 'failed'
    115. this.files[this.files.length - 1].message = '上传失败'
    116. } else {
    117. this.files[index].status = 'failed'
    118. this.files[index].message = '上传失败'
    119. }
    120. }
    121. }
    122. },
    123. // 压缩图片
    124. compress(img, Orientation) {
    125. const canvas = document.createElement('canvas')
    126. const ctx = canvas.getContext('2d')
    127. // 瓦片canvas
    128. const tCanvas = document.createElement('canvas')
    129. const tctx = tCanvas.getContext('2d')
    130. // let initSize = img.src.length;
    131. let width = img.width
    132. let height = img.height
    133. // 如果图片大于四百万像素,计算压缩比并将大小压至400万以下
    134. let ratio
    135. if ((ratio = (width * height) / 4000000) > 1) {
    136. // console.log("大于400万像素");
    137. ratio = Math.sqrt(ratio)
    138. width /= ratio
    139. height /= ratio
    140. } else {
    141. ratio = 1
    142. }
    143. canvas.width = width
    144. canvas.height = height
    145. // 铺底色
    146. ctx.fillStyle = '#fff'
    147. ctx.fillRect(0, 0, canvas.width, canvas.height)
    148. // 如果图片像素大于100万则使用瓦片绘制
    149. let count
    150. if ((count = (width * height) / 1000000) > 1) {
    151. // console.log("超过100W像素");
    152. count = ~~(Math.sqrt(count) + 1) // 计算要分成多少块瓦片
    153. // 计算每块瓦片的宽和高
    154. const nw = ~~(width / count)
    155. const nh = ~~(height / count)
    156. tCanvas.width = nw
    157. tCanvas.height = nh
    158. for (let i = 0; i < count; i++) {
    159. for (let j = 0; j < count; j++) {
    160. tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh)
    161. ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh)
    162. }
    163. }
    164. } else {
    165. ctx.drawImage(img, 0, 0, width, height)
    166. }
    167. // 进行最小压缩
    168. const ndata = canvas.toDataURL('image/jpeg', 0.4)
    169. tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0
    170. return ndata
    171. },
    172. // 提交图片到后端
    173. async postImg(base64, index) {
    174. const file = this.dataURLtoFile(base64, index)
    175. const formData = new window.FormData()
    176. formData.append('file', file)
    177. // 计算现在所有图片的大小,如果加上该文件超过10M则不可上传
    178. let totalSize = 0
    179. this.fileList.map(item => {
    180. totalSize += Number(item.file.size)
    181. })
    182. let size = totalSize + file.size
    183. if (size > 10 * 1024 * 1024) {
    184. this.$toast.fail('当前上传附件超过最大内存10M!')
    185. return
    186. }
    187. const res = await businessUpload(formData)
    188. if (res.code === 200) {
    189. this.$toast.success('图片已上传')
    190. this.fileIds.push(res.fileId)
    191. this.fileList.push({
    192. id: res.fileId,
    193. file: file
    194. })
    195. if (index == undefined) {
    196. this.files[this.files.length - 1].status = 'done'
    197. this.files[this.files.length - 1].message = '上传成功'
    198. } else {
    199. this.files[index].status = 'done'
    200. this.files[index].message = '上传成功'
    201. }
    202. } else {
    203. this.$toast.fail('图片上传失败')
    204. if (index == undefined) {
    205. this.files[this.files.length - 1].status = 'failed'
    206. this.files[this.files.length - 1].message = '上传失败'
    207. } else {
    208. this.files[index].status = 'failed'
    209. this.files[index].message = '上传失败'
    210. }
    211. }
    212. },
    213. // 将base64转换为文件
    214. dataURLtoFile(dataurl, index) {
    215. var arr = dataurl.split(',')
    216. var bstr = atob(arr[1])
    217. var n = bstr.length
    218. var u8arr = new Uint8Array(n)
    219. while (n--) {
    220. u8arr[n] = bstr.charCodeAt(n)
    221. }
    222. if (index == undefined) {
    223. // 单图上传
    224. return new File([u8arr], this.uploadfile.name, {
    225. type: this.uploadfile.type
    226. })
    227. } else {
    228. // 多图上传
    229. return new File([u8arr], this.uploadfiles[index].name, {
    230. type: this.uploadfiles[index].type
    231. })
    232. }
    233. }
    234. }

  • 相关阅读:
    AI安全入门-人工智能数据与模型安全
    linux下统计当前目录子目录的个数
    MyBatisPlus + ShardingJDBC 批量插入不返回主键ID
    Vue2或Vue3使用Pinia快速入门
    《web课程设计》用HTML CSS做一个简洁、漂亮的个人博客网站
    数据化运营02 告别迷茫!北极星指标,为业务指明方向
    MySQL 三大日志(bin log、redo log、undo log)
    MySQL —— 索引
    挑战30天学完Python:Day2 夯实基础 - 变量和内置函数
    Python学习基础笔记三——字符串
  • 原文地址:https://blog.csdn.net/Igiveufireworks/article/details/132825992