vue之上传图片并压缩图片大小
vue之上传图片并压缩图片大小
这里使用的是 compressorjs
, 安装 npm i compressorjs
注意:这个插件是利用canvas对图片进行重绘,如果压缩太过的话,会导致一定程度上的图片失真。
compressorjs官网
主要代码如下:
<div class="van-uploader__upload">
<i class="van-icon van-icon-photograph van-uploader__upload-icon"></i>
<input type="file" multiple accept="image/*" @change="handleImgUpload($event, item)"/>
</div>
<style lang="scss">
.van-uploader__upload {
display: none;
}
</style>
<script>
import Compressor from 'compressorjs';
export default {
methods: {
async handleImgUpload(e, item) {
if (!e.target.files || !e.target.files.length) {
return
}
const files = e.target.files
if (files.length > 9) {
this.$toast('一次最多上传9张,请分批次上传!')
e.target.value = ''
return
}
$loading.show()
files.forEach(async (file, index) => {
if (!file.name || !file.type.includes('image/')) {
this.$toast('上传失败,只能上传照片!')
// 上传完成
if (index === files.length - 1) {
//清空上传控件的值,防止不能重复上传
e.target.value = ''
$loading.hide()
}
return //forEach里的return相当于continue
}
if (file.size > 1024 * 1024 * 10) {
this.$toast('文件太大,不能超过10M!')
// 上传完成
if (index === files.length - 1) {
//清空上传控件的值,防止不能重复上传
e.target.value = ''
$loading.hide()
}
return
}
try {
let formData = new FormData()
// 大于512k则先压缩
if (file.size > 512 * 1024) {
const compressorFile = await this.compressor(file)
formData.append('file', compressorFile, compressorFile.name)
} else {
formData.append('file', file, file.name)
}
const res = await imgUpload(formData)
if (res.code === 0 && res.item) {
item.answer.push({
url: res.item,
})
} else {
this.$toast('上传失败,请稍后重试!')
}
} catch (error) {
this.$toast('上传失败,请稍后重试!')
console.error(error)
}
// 上传完成
if (index === files.length - 1) {
//清空上传控件的值,防止不能重复上传
e.target.value = ''
$loading.hide()
}
})
},
// 压缩
async compressor(file) {
return new Promise(resolve => {
new Compressor(file, {
quality: 0.2,
success: resolve,
error(err) {
console.log(err.message)
},
})
})
},
}
}
</script>
代码片段如下:
<template>
<div>
<div class="scroll-wrapper" v-if="taskInfo">
<template v-if="taskInfo.fileUrls && taskInfo.fileUrls.length">
<div
class="ml63 flex-center mt10 fs14"
v-for="(item, idx) in taskInfo.fileUrls"
:key="idx"
>
<div class="icon-file mr12">
<img src="@/assets/img/icon-file@2x.png" alt="" />
</div>
<div class="task-file mw25 ellipse">{{ item.fileName }}</div>
<div class="btn flex-center-center ml40">
<a :href="item.fileUrl">预览</a>
</div>
<div class="btn flex-center-center ml32">
<a :href="item.fileUrl">下载</a>
</div>
</div>
</template>
<div class="task-detail">
<div class="task-caption" v-for="(item, idx) in questions" :key="idx">
<!-- 图片上传 -->
<div class="flex mt20" v-if="item.questionType === 4">
<div class="van-uploader__wrapper" :class="{ 'task-img': isDisabled }">
<div class="van-uploader__preview" v-for="(img, imgIdx) in item.answer" :key="imgIdx">
<div class="van-image van-uploader__preview-image" @click="handlePreview(item.answer, imgIdx)">
<img class="van-image__img" :src="img.url" style="object-fit: cover"/>
</div>
<div v-if="!isDisabled" class="van-uploader__preview-delete" @click="item.answer.splice(imgIdx, 1)">
<i class="van-icon van-icon-cross van-uploader__preview-delete-icon"></i>
</div>
</div>
<div class="van-uploader__upload" v-if="!isDisabled">
<i class="van-icon van-icon-photograph van-uploader__upload-icon"></i>
<input type="file" multiple accept="image/*" @change="handleImgUpload($event, item)" class="van-uploader__input"/>
</div>
</div>
</div>
<!-- 附件上传 -->
<div class="mt20 file-upload" v-if="item.questionType === 5">
<div class="flex file-item" v-for="(file, fileIdx) in item.answer" :key="fileIdx">
<div class="file-left">
<div class="icon-file mr12">
<img src="@/assets/img/icon-file@2x.png" alt="" class="" />
</div>
<div class="task-file file-name ellipse">
{{ file.fileName }}
</div>
</div>
<div class="task-file ml40" v-if="!isDisabled">
<span @click="removeFile(item, fileIdx)">删除</span>
</div>
<div class="ml20 task-file flex-center-center" v-else>
<div class="btn flex-center-center">
<a :href="file.fileUrl">预览</a>
</div>
<div class="btn flex-center-center ml32">
<a :href="file.fileUrl">下载</a>
</div>
</div>
</div>
<van-button color="#4646E6" class="mt40 ml40" @click="handleFileClick('file' + idx)" :disabled="isDisabled">
<div class="flex-center fs14">
<input :id="'file' + idx" type="file" style="display: none" @change="handleFileUpload($event, item)"/>
<div class="file-icon mr12">
<img src="@/assets/img/icon-file_upload@2x.png" />
</div>
附件上传
</div>
</van-button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { getTaskDetail, imgUpload, fileUpload, userExecute } from '@/api/myTask'
import { ImagePreview } from 'vant'
import Compressor from 'compressorjs'
const fileExtensions = ['xlsx', 'xls', 'docx', 'doc', 'pdf']
export default {
name: 'index',
components: {},
props: {},
data() {
return {
id: this.$route.params.id,
from: this.$route.query.from,
shareAccount: this.$route.query.shareAccount,
taskInfo: null,
questions: [],
currentItem: null,
imgList: [],
}
},
mounted() {
this.getTaskDetail(this.id)
},
methods: {
async getTaskDetail(id) {
try {
$loading.show()
const data = { id }
if (this.from === 'behalfTask') {
data.userAccountId = this.shareAccount
}
const res = await getTaskDetail(data)
$loading.hide()
if (res.code === 0) {
this.taskInfo = res.item
this.initQuestions(this.taskInfo.taskItem)
}
} catch (error) {
$loading.hide()
console.error(error)
}
},
initQuestions(taskItem) {
for (const item of taskItem) {
let question = {}
question.itemId = item.itemId
question.title = item.title
question.questionDescribe = (item.questionDescribe && '(' + item.questionDescribe + ')') || ''
question.questionType = item.questionType
question.required = item.required
question.options = item.itemContent
// 处理问题答案
switch (item.questionType) {
// 图片上传
case 4:
if (item.itemExecute && item.itemExecute.taskItemExecute) {
question.answer = item.itemExecute.taskItemExecute
.split(',')
.map(img => {
return { url: img }
})
} else {
question.answer = []
}
break
// 附件上传
case 5:
if (item.itemExecute && item.itemExecute.taskItemExecute) {
question.answer = item.itemExecute.taskItemExecute
.split(',')
.map(file => {
if (file.split('&').length === 2) {
return {
fileUrl: file.split('&')[0],
fileName: file.split('&')[1],
}
}
return { fileUrl: '', fileName: '' }
})
} else {
question.answer = []
}
break
}
this.questions.push(question)
}
},
// 点击事件
handleFileClick(el) {
document.getElementById(el).click()
},
// 附件上传
async handleFileUpload(e, item) {
if (!e.target.files || !e.target.files.length) {
return
}
if (!e.target.files[0].name || e.target.files[0].name.split('.').length < 2) {
this.$toast('无效文件!')
return
}
if (!fileExtensions.includes(e.target.files[0].name.split('.')[1].toLowerCase())) {
this.$toast.fail('请选择附件文件类型')
return
}
if (e.target.files[0].size > 1024 * 1024 * 10) {
this.$toast.fail('文件太大,不能超过10M!')
return
}
try {
const res = await fileUpload({ file: e.target.files[0] })
// 清空上传控件的值,防止不能重复上传
e.target.value = ''
if (res.code === 0) {
item.answer.push({
fileName: res.item.fileName,
fileUrl: res.item.fileUrl,
})
}
} catch (error) {
e.target.value = ''
console.error(error)
}
},
// 图片上传
async handleImgUpload(e, item) {
if (!e.target.files || !e.target.files.length) {
return
}
const files = e.target.files
if (files.length > 5) {
this.$toast('一次最多上传5张,请分批次上传!')
e.target.value = ''
return
}
$loading.show()
files.forEach(async (file, index) => {
if (!file.name || !file.type.includes('image/')) {
this.$toast('上传失败,只能上传照片!')
// 上传完成
if (index === files.length - 1) {
//清空上传控件的值,防止不能重复上传
e.target.value = ''
$loading.hide()
}
return //forEach 里的 return 相当于 continue
}
if (file.size > 1024 * 1024 * 10) {
this.$toast('文件太大,不能超过10M!')
// 上传完成
if (index === files.length - 1) {
//清空上传控件的值,防止不能重复上传
e.target.value = ''
$loading.hide()
}
return
}
try {
let formData = new FormData()
// 大于512k则先压缩
if (file.size > 512 * 1024) {
const compressorFile = await this.compressor(file)
formData.append('file', compressorFile, compressorFile.name)
} else {
formData.append('file', file, file.name)
}
const res = await imgUpload(formData)
if (res.code === 0 && res.item) {
// 给item 添加图片路径
item.answer.push({
url: res.item,
})
} else {
this.$toast('上传失败,请稍后重试!')
}
} catch (error) {
this.$toast('上传失败,请稍后重试!')
console.error(error)
}
// 上传完成
if (index === files.length - 1) {
//清空上传控件的值,防止不能重复上传
e.target.value = ''
$loading.hide()
}
})
},
// 图片压缩
async compressor(file) {
return new Promise(resolve => {
new Compressor(file, {
quality: 0.2,
success: resolve,
error(err) {
console.log(err.message)
},
})
})
},
// 图片预览
handlePreview(imgList, startPosition) {
ImagePreview(
imgList.map(item => item.url),
startPosition
)
},
// 附件删除
removeFile(ques, fileIdx) {
this.$dialog
.confirm({ message: `是否删除附件“${ques.answer[fileIdx].fileName}”` })
.then(() => {
ques.answer.splice(fileIdx, 1)
})
.catch(() => {})
},
},
computed: {
isDisabled() {
return (
// 直接进入此页面的只允许查看
!this.from ||
//从执行记录进来的只允许查看
this.from === 'executeRecord' ||
//代执行却没有代执行人的ID不允许执行
(this.from === 'behalfTask' && !this.shareAccount) ||
//已执行任务不允许再执行
this.taskInfo.execStatus === 1 ||
//不允许超期执行且已超期则不允许执行
(!this.taskInfo.allowOverDue && !!this.taskInfo.overdueFlag)
)
},
},
}
</script>
上传代码部分感谢龙哥的帮忙和指导;
其中接口调接口写法
import request from '@/utils/request'
import store from '@/store'
function addParams(params) {
if (!params.userAccountId) {
params.userAccountId = store.getters.userAccountID
}
}
// 图片上传
export function imgUpload(data = {}) {
// addParams(data)
// let postBody = new FormData()
// for (const key in data) {
// postBody.append(key, data[key])
// }
return request({
url: '/api/image/upload',
method: 'post',
data
})
}
// 附件上传
export function fileUpload(data = {}) {
addParams(data)
let postBody = new FormData()
for (const key in data) {
postBody.append(key, data[key])
}
return request({
url: '/api/file/upload',
method: 'post',
data: postBody
})
}
注意点:
选择图片时,需要清空一下这个值,防止同一张图片不让多次选择上传;
e.target.value = ''
其他写法:
<template>
<div class="ele-upload-styl">
<!-- <el-upload
action=""
:headers="uploadProps.headers"
:show-file-list="false"
:http-request="fnUploadRequest"
:on-success="handleSuccess"
:before-upload="handleUpload"
accept=".png,.jpg,.jpeg,.gif,.webp"
multiple
:limit="5"
:on-exceed="handleExceed"
ref="upload"
>
<div class="img-cont pr">
<img slot="trigger" class="img-icon" src="@/assets/img/icon-img-upload.png" alt="">
<span slot="tip" class="img-text">图片</span>
</div>
</el-upload> -->
<-- 原生写法-->
<label for="myFile">
<input
type="file"
multiple
accept="image/*"
id="myFile"
@change="handleImgUpload($event)"
style="display: none"
/>
<div class="img-cont pr">
<img
slot="trigger"
class="img-icon"
src="@/assets/img/icon-img-upload.png"
alt=""
/>
<span slot="tip" class="img-text">图片</span>
</div>
</label>
</div>
</template>
<script>
import {
getAccessToken,
getRefreshToken,
getAccessTokenTTL
} from '@/utils/auth'
import { uploadOSS } from '@/utils/ossImage'
export default {
name: 'imgUpload',
components: {},
props: {},
computed: {
userAccountID() {
return this.$store.state.user.userAccountID
},
uploadProps() {
return {
// action: `${process.env.VUE_APP_BASE_API}/api/image/upload`,
headers: {
// 接口可能要带token: "",
Authorization: getAccessToken()
},
data: {}
}
}
},
data() {
return {}
},
methods: {
// handleExceed(file, fileList) {
// // console.log(file, fileList);
// this.$message.error("上传失败,限制上传数量10张图片以内!");
// },
// beforeUpload_u(file, fileList) {
// // console.log(file, fileList);
// var testmsg = file.name.substring(file.name.lastIndexOf(".") + 1);
// const extension =
// testmsg === "png" ||
// testmsg === "jpg" ||
// testmsg === "jpeg" ||
// testmsg === "gif" ||
// testmsg === "webp";
// const isLimit10M = file.size / 1024 / 1024 < 10;
// var bool = false;
// if (extension && isLimit10M) {
// bool = true;
// } else {
// bool = false;
// }
// if (!extension) {
// this.$message.error("请上传图片格式文件!");
// return bool;
// }
// if (!isLimit10M) {
// this.$message.error("上传失败,不能超过10M!");
// return bool;
// }
// return bool;
// },
// handleSuccess(res) {
// // console.log(res);
// if (res.code == 0) {
// this.$emit("imgData", res.item);
// this.$message.success("上传图片成功!");
// } else {
// this.$message.error("上传图片失败!");
// }
// },
// handleError(err) {
// this.$message.error("上传图片失败!");
// },
// 上传图片判断
handleUpload(file) {
// console.log(file);
var testmsg = file.name.substring(file.name.lastIndexOf('.') + 1)
const extension =
testmsg.toLowerCase() === 'png' ||
testmsg.toLowerCase() === 'jpg' ||
testmsg.toLowerCase() === 'jpeg' ||
testmsg.toLowerCase() === 'gif' ||
testmsg.toLowerCase() === 'webp'
const isLimit10M = file.size / 1024 / 1024 < 10
var bool = false
if (extension && isLimit10M) {
bool = true
} else {
bool = false
}
if (!extension) {
this.$message.error('请上传图片格式文件!')
return bool
}
if (!isLimit10M) {
this.$message.error('文件太大,单个文件不能超过10M! ')
return bool
}
return bool
},
handleExceed(files, fileList) {
// console.log(files, fileList);
this.$message.warning(` 一次最多上传5张,请分批次上传! `)
},
// 上传图片
async fnUploadRequest(options) {
// console.log(options);
try {
this.$loading.show()
// console.log(options);
let file = options.file // 拿到 file
let res = await uploadOSS(file)
// console.log(res);
// 返回的就是图片地址
this.$emit('imgData', res)
this.$loading.hide()
} catch (e) {
this.$loading.hide()
this.$message.error('上传图片失败!请重新上传')
}
},
//图片上传成功回调
handleSuccess(res) {
// console.log(res);
this.$refs.upload.clearFiles() //清空选择列表
if (res) {
this.$emit('imgData', res)
}
},
// 原生的方法 触发调接口,其他操作自行研究
handleImgUpload(e) {
console.log(e.target.files)
e.target.files.forEach(async (file) => {
console.log(file)
try {
this.$loading.show()
let res = await uploadOSS(file)
// console.log(res);
// 返回的就是图片地址
this.$emit('imgData', res)
this.$loading.hide()
} catch (e) {
this.$loading.hide()
this.$message.error('上传图片失败!请重新上传')
}
})
e.target.value = ''
}
}
}
</script>
<style lang="scss" scoped>
::v-deep .el-upload,
::v-deep .el-upload--picture-card {
// width: 50px;
height: 24px;
border: none;
line-height: 0;
display: block;
background: #f5f6fb;
}
::v-deep .el-upload {
width: 50px;
}
.img-cont {
width: 50px;
height: 24px;
background: #f5f6fb;
.img-icon {
color: #ccc;
}
.img-text {
font-size: 12px;
height: 24px;
color: #000;
margin-left: 3px;
}
}
</style>