Loading... #### 技术选型 - 开发框架: [uniapp](https://uniapp.dcloud.net.cn/) - 主题UI框架: [uView](https://www.uviewui.com) #### 背景 uView框架[upload文件上传](https://www.uviewui.com/components/upload.html)组件中没有h5端控制压缩的参数,超出文件大小也没有响应的提示,但是一般的上传文件场景,是需要控制文件大小的。 > ![请输入图片描述](https://qiniu.program-er.com/blog/typecho/usr/uploads/文档.png) --- #### 效果图 > 原图是4.8M,长宽为:3024*4032 > ![请输入图片描述](https://qiniu.program-er.com/blog/typecho/usr/uploads/原图.png) > ![质量宽高同比例压缩](https://qiniu.program-er.com/blog/typecho/usr/uploads/对比图.png) > ![压缩质量,不调整宽高](https://qiniu.program-er.com/blog/typecho/usr/uploads/对比图2.png) --- #### 封装图片压缩工具方法 - 封装util.js ```javascript // 图片压缩 /** * imgSrc 地址 * scale 压缩质量 0-1 * type 文件类型 */ export function compressImg(imgSrc, scale, type, callback) { // uni.$u.toast('压缩中') var img = new Image(); img.src = imgSrc; img.onload = function() { var that = this; var h = (img.height * scale).toFixed(0); // 默认按质量比例压缩 var w = (img.width * scale).toFixed(0); var canvas = document.createElement('canvas'); var ctx = canvas.getContext('2d'); var width = document.createAttribute("width"); width.nodeValue = w; var height = document.createAttribute("height"); height.nodeValue = h; canvas.setAttributeNode(width); canvas.setAttributeNode(height); ctx.drawImage(that, 0, 0, w, h); var base64 = canvas.toDataURL('image/jpeg', scale); //压缩比例 canvas = null; if (type == 'base64') { let data = { size: getBase64Size(base64), type: type, source: base64 } callback(base64); } else { let blob = base64ToBlob(base64); // console.log('压缩后的大小', blob, blob.size, blob.type) const blobUrl = window.URL.createObjectURL(blob); //blob地址 blob.source = blobUrl callback(blob); } } } // base转Blob export function base64ToBlob(base64) { var arr = base64.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n); } return new Blob([u8arr], { type: mime }); } // 获取base64的文件大小 export function getBase64Size(base64Str) { let size = 0; if (base64Str) { // 获取base64图片byte大小 const equalIndex = base64Str.indexOf('='); // 获取=号下标 if (equalIndex > 0) { const str = base64Str.substring(0, equalIndex); // 去除=号 const strLength = str.length; const fileLength = strLength - (strLength / 8) * 2; // 真实的图片byte大小 size = Math.floor(fileLength); // 向下取整 } else { const strLength = base64Str.length; const fileLength = strLength - (strLength / 8) * 2; size = Math.floor(fileLength); // 向下取整 } } else { size = null; } return size } ``` ##### index.vue中引入util.js,并在上传时使用 ```javascript <template> <view class="components-wrap"> <u-form-item class='form-item-box' labelWidth='auto' :prop="column.name" borderBottom :ref="column.name"> <u-upload class="upload-wrap" width="102" :fileList="fileList" @afterRead="afterRead" @delete="deletePic" :sizeType="sizeType" @clickPreview="clickPreview" :name="column.name" multiple :disabled="disabled" :formkey="formkey"> <view class="material-add"> <u-button class='upload-btn' :disabled="disabled" text="上传图片"></u-button> </view> </u-upload> </u-form-item> </view> </template> <script> import { compressImg } from '@/utils/util.js' export default { data() { return { currentValue: false, fileList: [], sizeType: ['compressed'], fileMaxSize: 2 * 1024 * 1024, // 默认最大为2M fileMinSize: 50 * 1024 // 最小为50KB } }, methods: { clickPreview(url, lists, name) { console.log('预览图片', url, lists, name) }, getCompressionRatio(fileSize) { const multiple = (fileSize / this.fileMaxSize).toFixed(2) // 获取文件大小倍数,生成质量比 let compressionRatio = 1 if(multiple > 5) { compressionRatio = 0.5 } else if (multiple > 4) { compressionRatio = 0.6 } else if (multiple > 3) { compressionRatio = 0.7 }else if (multiple > 2) { compressionRatio = 0.8 } else if (multiple > 1) { compressionRatio = 0.9 } else { compressionRatio = 2 } return compressionRatio; }, // 新增图片 async afterRead(event) { // 当设置 mutiple 为 true 时, file 为数组格式,否则为对象格式 let lists = [].concat(event.file) let fileListLen = this[`fileList`].length for (let index in lists) { const item = lists[index] const fileSize = item.size const fileName = item.name ?? '' if (fileSize > this.fileMaxSize) { const compressionRatio = this.getCompressionRatio(fileSize) if (compressionRatio > 1) { uni.$u.toast('文件' + fileName + '大于10M') return false } // 自动压缩图片' await this.compressImg(item, compressionRatio) if (item.size > this.fileMaxSize) { uni.$u.toast('文件' + fileName + '压缩后超出2M') return false } } if (item.size < this.fileMinSize) { uni.$u.toast('文件' + fileName + '不能小于50KB') return false } this[`fileList`].push({ ...item, status: 'uploading', message: '上传中' }) } for (let i = 0; i < lists.length; i++) { const result = await this.uploadFilePromise(lists[i].url) // 垃圾回收 window.URL.revokeObjectURL(lists[i].url) console.log('上传结果', result) let item = this[`fileList`][fileListLen] this[`fileList`].splice(fileListLen, 1, Object.assign(item, { status: 'success', message: '上传成功', url: result })) fileListLen++ } }, compressImg(source, compressionRatio) { return new Promise((resolve, reject) => { compressImg(source.url, compressionRatio, source.type, compressRes => { resolve(compressRes); }) }).then((res) => { source.size = res.size // window.URL.revokeObjectURL(source.url) // 删除被压缩的缓存文件,这里注意,如果是相册选择上传,可能会删除相册的图片 source.url = res.source source.thumb = res.source return source }).catch(err => { console.log('图片压缩失败', err) }) }, uploadFilePromise(url) { let that = this return new Promise((resolve, reject) => { const uploadTask = uni.uploadFile({ url: uploadUrl, filePath: url, name: 'file', header: { "blade-auth": "Bearer xxxxxxxxxxxxx", "tenantCode": tenantCode }, success: (uploadFileRes) => { if (uploadFileRes.statusCode === that.Response.OK) { const data = JSON.parse(uploadFileRes.data) if (data.code === that.Response.OK) { console.log('form components upload uploadFilePromise onchange', uploadFileRes) let fileId = data.data[0].id; const sourceUrl = 'https://xxxxxxx/download-document/id/' + fileId; that.data.value.push({ id: fileId, name: data.data[0].name, source: sourceUrl }); // 添加图片数量 that.data.count = that.fileList.length that.$emit('change', that.data, that.formkey) setTimeout(() => { resolve(sourceUrl) }, 500) } else { uni.showToast({ title: data.msg, icon: 'none', duration: that.ShowToast.DURATION }); } } else { console.log(uploadFileRes) uni.showToast({ title: '上传失败', icon: 'none', duration: that.ShowToast.DURATION }); } }, fail(error) { // reject(false) uni.$u.toast(error.errMsg) console.log('图片上传失败', error) } }); // uploadTask.onProgressUpdate((res) => { // console.log('上传进度' + res.progress); // console.log('已经上传的数据长度' + res.totalBytesSent); // console.log('预期需要上传的数据总长度' + res.totalBytesExpectedToSend); // // 测试条件,取消上传任务。 // if (res.progress > 50) { // uploadTask.abort(); // } // }); }) }, } } <script/> ``` 最后修改:2023 年 04 月 24 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 0 感谢赏赐的coffee~