需求:
实现图片和文件的上传,单个图片超过1M则压缩,全部文件加起来不得超过10M。
1. html
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
})
}
}
}