需求:
实现图片和文件的上传,单个图片超过1M则压缩,全部文件加起来不得超过10M。
效果:
1. html
- <van-form ref="form">
- <van-field name="uploader" label="佐证材料" required>
- <template #input>
- <van-uploader
- v-model="files"
- multiple
- accept=""
- :before-read="beforeRead"
- :after-read="afterRead"
- @delete="deleteImg"
- />
- template>
- van-field>
- van-form>
2. js:
- data() {
- return {
- files: [],
- fileList: [], // 已上传的图片
- fileIds: [], // 佐证材料文件id,上传成功之后返回的数据
- uploadfile: {
- type: '',
- name: ''
- }, // 上传单图的情况
- uploadfiles: [], // 上传多图的情况
- }
- },
- methods: {
- // 文件读取前触发
- beforeRead(e) {
- if (e.size > 10 * 1024 * 1024) {
- this.$toast.fail('文件大小不能超过10M')
- return false
- }
- return true
- },
- // 文件读取完成后触发
- afterRead(file) {
- if (file.length > 0) {
- // 多个上传
- file.map((item, index) => {
- this.uploadfiles.push({
- name: item.file.name,
- type: item.file.type
- })
- this.files[index].status = 'uploading'
- this.files[index].message = '上传中...'
- this.imgPreview(file[index].file, index)
- })
- } else {
- // 单个上传
- this.uploadfile.name = file.file.name // 获取文件名
- this.uploadfile.type = file.file.type // 获取类型
- this.files[this.files.length - 1].status = 'uploading'
- this.files[this.files.length - 1].message = '上传中...'
- // console.log('绑定的文件', this.files)
- this.imgPreview(file.file)
- }
- },
- // 删除文件
- deleteImg(file) {
- // 匹配fileList的项的fileName,相同的话把filesId对应删除
- this.fileList.map((item, index) => {
- if (item.file.name == file.file.name) {
- this.fileList.splice(index, 1)
- this.fileIds.splice(index, 1)
- }
- })
- },
- // 处理文件
- async imgPreview(file, index) {
- const self = this
- // 看支持不支持FileReader
- if (!file || !window.FileReader) return
- if (/^image/.test(file.type)) {
- // 创建一个reader
- const reader = new FileReader()
- // 将图片转成 base64 格式
- reader.readAsDataURL(file)
- // 读取成功后的回调
- reader.onloadend = function () {
- const result = this.result
- const img = new Image()
- img.src = result
- // 判断图片是否小于1M,是就直接上传,反之压缩图片
- if (this.result.length <= 1024 * 1024) {
- // 上传图片
- self.postImg(this.result, index)
- } else {
- img.onload = function () {
- const data = self.compress(img)
- // 上传图片
- self.postImg(data, index)
- }
- }
- }
- } else {
- // 其他格式的文件
- const formData = new window.FormData()
- formData.append('file', file)
- // 计算现在所有图片的大小,如果加上该文件超过10M则不可上传
- let totalSize = 0
- this.fileList.map(item => {
- totalSize += Number(item.file.size)
- })
- let size = totalSize + file.size
- if (size > 10 * 1024 * 1024) {
- this.$toast.fail('当前上传附件超过最大内存10M!')
- return
- }
- const res = await businessUpload(formData)
- if (res.code === 200) {
- this.$toast.success('文件已上传')
- this.fileIds.push(res.fileId)
- this.fileList.push({
- id: res.fileId,
- file: file
- })
- if (index == undefined) {
- this.files[this.files.length - 1].status = 'done'
- this.files[this.files.length - 1].message = '上传成功'
- } else {
- this.files[index].status = 'done'
- this.files[index].message = '上传成功'
- }
- } else {
- this.$toast.fail('文件上传失败')
- if (index == undefined) {
- this.files[this.files.length - 1].status = 'failed'
- this.files[this.files.length - 1].message = '上传失败'
- } else {
- this.files[index].status = 'failed'
- this.files[index].message = '上传失败'
- }
- }
- }
- },
- // 压缩图片
- compress(img, Orientation) {
- const canvas = document.createElement('canvas')
- const ctx = canvas.getContext('2d')
- // 瓦片canvas
- const tCanvas = document.createElement('canvas')
- const tctx = tCanvas.getContext('2d')
- // let initSize = img.src.length;
- let width = img.width
- let height = img.height
- // 如果图片大于四百万像素,计算压缩比并将大小压至400万以下
- let ratio
- if ((ratio = (width * height) / 4000000) > 1) {
- // console.log("大于400万像素");
- ratio = Math.sqrt(ratio)
- width /= ratio
- height /= ratio
- } else {
- ratio = 1
- }
- canvas.width = width
- canvas.height = height
- // 铺底色
- ctx.fillStyle = '#fff'
- ctx.fillRect(0, 0, canvas.width, canvas.height)
- // 如果图片像素大于100万则使用瓦片绘制
- let count
- if ((count = (width * height) / 1000000) > 1) {
- // console.log("超过100W像素");
- count = ~~(Math.sqrt(count) + 1) // 计算要分成多少块瓦片
- // 计算每块瓦片的宽和高
- const nw = ~~(width / count)
- const nh = ~~(height / count)
- tCanvas.width = nw
- tCanvas.height = nh
- for (let i = 0; i < count; i++) {
- for (let j = 0; j < count; j++) {
- tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh)
- ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh)
- }
- }
- } else {
- ctx.drawImage(img, 0, 0, width, height)
- }
- // 进行最小压缩
- const ndata = canvas.toDataURL('image/jpeg', 0.4)
- tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0
- return ndata
- },
- // 提交图片到后端
- async postImg(base64, index) {
- const file = this.dataURLtoFile(base64, index)
- const formData = new window.FormData()
- formData.append('file', file)
-
- // 计算现在所有图片的大小,如果加上该文件超过10M则不可上传
- let totalSize = 0
- this.fileList.map(item => {
- totalSize += Number(item.file.size)
- })
- let size = totalSize + file.size
- if (size > 10 * 1024 * 1024) {
- this.$toast.fail('当前上传附件超过最大内存10M!')
- return
- }
- const res = await businessUpload(formData)
- if (res.code === 200) {
- this.$toast.success('图片已上传')
- this.fileIds.push(res.fileId)
- this.fileList.push({
- id: res.fileId,
- file: file
- })
- if (index == undefined) {
- this.files[this.files.length - 1].status = 'done'
- this.files[this.files.length - 1].message = '上传成功'
- } else {
- this.files[index].status = 'done'
- this.files[index].message = '上传成功'
- }
- } else {
- this.$toast.fail('图片上传失败')
- if (index == undefined) {
- this.files[this.files.length - 1].status = 'failed'
- this.files[this.files.length - 1].message = '上传失败'
- } else {
- this.files[index].status = 'failed'
- this.files[index].message = '上传失败'
- }
- }
- },
- // 将base64转换为文件
- dataURLtoFile(dataurl, index) {
- var arr = dataurl.split(',')
- var bstr = atob(arr[1])
- var n = bstr.length
- var u8arr = new Uint8Array(n)
- while (n--) {
- u8arr[n] = bstr.charCodeAt(n)
- }
- if (index == undefined) {
- // 单图上传
- return new File([u8arr], this.uploadfile.name, {
- type: this.uploadfile.type
- })
- } else {
- // 多图上传
- return new File([u8arr], this.uploadfiles[index].name, {
- type: this.uploadfiles[index].type
- })
- }
- }
- }