马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
🔥 打造基于 uView+uniapp+vue 的高性能图片上传组件(自动压缩 + 更加坚固的范例判断)
媒介
在移动端开辟(App/小步伐/H5)中, 图片上传 是一个极其高频且轻易产生性能瓶颈的场景。直接上传原图每每会带来以下题目:
- 上传迟钝 :现在的手机照相动辄 5MB-10MB,用户在非 WiFi 环境下体验极差。
- 体验欠好 :大文件导致哀求时间过长,轻易超时。
- 服务器压力 :不但占用大量带宽,还浪费存储空间。
固然 uView UI 的 u-upload 组件已经非常好用,但它默认不包罗“上传前压缩” 的逻辑。本日我们就来手撸一个 “带自动压缩功能的图片上传组件”,不但支持并发上传、进度体现,还具备更智能的图片范例判断逻辑。
🚀 核心方案计划
我们的目的是封装一个通用组件 MyUpload ,实现以鄙俚程:
- 拦截选择 :监听 u-upload 的 afterRead 事故。
- 智能判断 :
- 范例查抄 :不但限于 jpg、png ,兼容全部图片格式。
- 阈值控制 :仅对高出指定巨细(如 1MB,可自行调解)的图片举行压缩,小图直接上传,平衡清楚度与性能。
- 核心压缩 :使用 Canvas (通过 helang-compress 插件) 举行压缩。
- 格式转换 :将压缩后的 Base64 转回二进制文件对象(关键步调,否则 uni.uploadFile 无法辨认)。
- 同一上传 :处置惩罚上传进度、乐成回填、失败自动移除。
🛠️ 核心代码实现
1. 组件布局
我们基于 u-upload 举行二次封装,同时引入压缩插件。- <template>
- <view>
- <u-upload
- :fileList="fileList1"
- @afterRead="afterRead"
- @delete="deletePic"
- name="1"
- multiple
- :maxCount="maxCount"
- :accept="accept"
- ></u-upload>
-
-
- <helang-compress ref="helangCompress"></helang-compress>
- </view>
- </template>
复制代码 2. 更加坚固的压缩判断逻辑✨
- // 上传核心逻辑async uploadFilePromise(file, lists) { let OriginalUrl = file.url let afterCompressFile = null let ifcompress = false // 1. file.type 判空掩护:防止部分安卓机型或特殊场景下 type 丢失导致报错 // 2. 暗昧匹配 'image':覆盖 image/png, image/jpeg, image/gif 等全部图片范例 // 3. 巨细阈值:只有高出 1MB (1024KB) 才压缩,小图直接上传,阈值可自行调解 if (file.type && file.type.indexOf('image') != -1 && file.size / 1024 > 1024) { // 标志为必要压缩 ifcompress = true // 调用压缩插件,返回值是压缩后的 Base64 字符串 let afterCompressBase64 = await this.$refs.helangCompress.compress({ src: OriginalUrl, maxSize: 1024, // 限定最大分辨率 fileType: 'jpg', // 同一输出为 jpg 镌汰体积 quality: 0.8, // 压缩质量 minSize: 640 // 最小尺寸掩护 }) // uni.uploadFile 不支持直接传 Base64,必须转为 File 对象 afterCompressFile = await base64ToFile(afterCompressBase64, file.name) } return new Promise((resolve, reject) => { uni.uploadFile({ url: config.upLoadUrl, name: 'file', // 如果压缩了,filePath 传 null(或根据平台差异调解),file 传转换后的对象 // 如果没压缩,直接用原路径 filePath: !ifcompress ? file.url : file.name, file: !ifcompress ? null : afterCompressFile, header: { 'Authorization': 'Bearer ' + uni.getStorageSync('Token') ?? '', }, success: (res) => { // 处置惩罚服务端返回 let data = JSON.parse(res.data); if(data.code == 200){<template>
- <view>
- <u-upload
- :fileList="fileList1"
- @afterRead="afterRead"
- @delete="deletePic"
- name="1"
- multiple
- :maxCount="maxCount"
- :accept="accept"
- ></u-upload>
-
-
- <helang-compress ref="helangCompress"></helang-compress>
- </view>
- </template>resolve(data.url) } else {<template>
- <view>
- <u-upload
- :fileList="fileList1"
- @afterRead="afterRead"
- @delete="deletePic"
- name="1"
- multiple
- :maxCount="maxCount"
- :accept="accept"
- ></u-upload>
-
-
- <helang-compress ref="helangCompress"></helang-compress>
- </view>
- </template>uni.$u.toast(data.message)<template>
- <view>
- <u-upload
- :fileList="fileList1"
- @afterRead="afterRead"
- @delete="deletePic"
- name="1"
- multiple
- :maxCount="maxCount"
- :accept="accept"
- ></u-upload>
-
-
- <helang-compress ref="helangCompress"></helang-compress>
- </view>
- </template>reject(data) } }, fail: (err) => { console.log("Upload failed", err) reject(err) } }); })}
复制代码 3. 队列上传与状态管理
及时更新 UI 的 loading 状态,并在失败时自动整理,这点蛮告急的,很多时间上传失败但是组件上展示是有图片的(这是当地的blob图片,并不是真正上传服务器后的图片)。- async afterRead(event) { // 1. 预处置惩罚:将新选择的文件参加列表,状态设为 'uploading' let lists = [].concat(event.file) let fileListLen = this[`fileList${event.name}`].length lists.map((item) => { this[`fileList${event.name}`].push({ ...item, status: 'uploading', message: '上传中' }) }); // 2. 串行上传(也可以改为 Promise.all 并行,视服务器压力而定) for (let i = 0; i < lists.length; i++) { try { // 等候单个文件上传(含压缩耗时) const result = await this.uploadFilePromise(lists[i], lists)<template>
- <view>
- <u-upload
- :fileList="fileList1"
- @afterRead="afterRead"
- @delete="deletePic"
- name="1"
- multiple
- :maxCount="maxCount"
- :accept="accept"
- ></u-upload>
-
-
- <helang-compress ref="helangCompress"></helang-compress>
- </view>
- </template> // 3. 乐成回调:更新列表状态为 success,并回填 URL let item = this[`fileList${event.name}`][fileListLen] this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, { status: 'success', message: '', url: result })) fileListLen++ } catch(e) { // 4. 失败回滚:移除该项,制止 UI 体现错误的占位 this[`fileList${event.name}`].splice(fileListLen, 1) uni.$u.toast('上传失败,请重试') } } // 5. 关照父组件更新数据 this.emitInput(this[`fileList${event.name}`])}
复制代码 ⚠️ 避坑指南 & 最佳实践
- H5 与 App 的差异 :
- 在 H5 端,图片选择后通常是 Blob URL;在 App 端是绝对路径。 uni.uploadFile 在处置惩罚 Base64 转成的 File 对象时,差异平台的参数通报略有差异(重要体现在 filePath 和 file 字段的互斥使用上),代码中通过 !ifcompress ? ... : ... 做了很好的兼容。
- Base64 转 File :
- 压缩插件返回的是 Base64 字符串,必须通过 base64ToFile (使用 uni.getFileSystemManager 或 Blob ) 转换后才华上传,否则服务端无法剖析。
- 内存走漏 :
- 如果在循环中大量举行 Canvas 利用,记得及时烧毁或重用 Canvas 上下文。本方案使用了 helang-compress 插件,内部处置惩罚了 Canvas 的生命周期。
- 用户体验 :
- 务必在压缩时给用户反馈(如“处置惩罚中...”),由于大图压缩大概必要几百毫秒到 1 秒的时间。
完备代码
- /* File Info
- * 二次封装上传图片组件
- */
- <template>
- <view >
- <u-upload :fileList="fileList1" @afterRead="afterRead" @delete="deletePic" name="1" multiple
- :maxCount="maxCount" :accept="accept"></u-upload>
- <helang-compress ref="helangCompress"></helang-compress>
- <compress ref="compress" />
- </view>
- </template>
复制代码- /* File Info
- * 封装压缩图片的canvas
- */
- <template>
- <view v-if="canvasId">
- <canvas :canvas-id="canvasId" :></canvas>
- </view>
- </template>
复制代码- /* File Info
- * 转换base64方法
- */
- export function base64ToFile(base64Data, filename='xxx1.jpg') {
- // 将base64的数据部分提取出来
- const parts = base64Data.split(';base64,');
- const contentType = parts[0].split(':')[1];
- const raw = window.atob(parts[1]);
-
- // 将原始数据转换为Uint8Array
- const rawLength = raw.length;
- const uInt8Array = new Uint8Array(rawLength);
- for (let i = 0; i < rawLength; ++i) {
- uInt8Array[i] = raw.charCodeAt(i);
- }
-
- // 使用Blob创建一个新的文件
- const blob = new Blob([uInt8Array], {type: contentType});
-
- // 创建File对象
- const file = new File([blob], filename, {type: contentType});
- // console.log('创建File对象==',file,blob)
-
- return file;
- }
复制代码 🎯 总结
通过这次封装,我们不但办理了一个详细的业务需求,更告急的是提拔了代码的 复用性 和 坚固性 。
- 复用性 :任何页面必要上传图片,引入这个组件即可,无需关心压缩细节。
- 坚固性 :美满的范例判断 file.type.indexOf('image') 包管了各种奇葩图片格式也能被精确处置惩罚或透传,删除上传失败图片制止发生误会。
盼望这篇文章能帮你优化你的 Uni-app 项目!如果你以为有用,点个赞再走吧~ 👍
免责声明:如果侵犯了您的权益,请联系站长及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金. |