头像上传、反馈截图、商品图等多图上传是 UniApp 的常见需求。把 选图、压缩、上传 三步串成一个 Promise 方法,页面传一个回调即可,代码统一且易维护。
实现思路
choose 阶段限制数量并允许拍照;拿到本地临时路径后逐张压缩,质量过高时才真正压缩以省性能;上传阶段用 uni.uploadFile,自带文件流与 formData,通过 header 带 Token,uploadTask 的 onProgressUpdate 可做进度条。
选图与压缩
// utils/upload.js
import { http } from "/utils/request";
export function chooseAndCompress(count = 9, sizeType = ["compressed"]) {
return new Promise((resolve, reject) => {
uni.chooseImage({
count,
sizeType,
sourceType: ["album", "camera"],
success: async (res) => {
const tasks = res.tempFilePaths.map((path) => compress(path));
try {
resolve(await Promise.all(tasks));
} catch (e) {
reject(e);
}
},
fail: reject
});
});
}
function compress(filePath) {
return new Promise((resolve) => {
uni.compressImage({
src: filePath,
quality: 80,
success: (res) => resolve(res.tempFilePath || filePath),
fail: () => resolve(filePath) // 压缩失败退回原图
});
});
}
单图上传方法
export function uploadFile(filePath, extra = {}) {
return new Promise((resolve, reject) => {
const token = uni.getStorageSync("token");
const uploadTask = uni.uploadFile({
url: "/api/upload",
filePath,
name: "file",
header: token ? { Authorization: "Bearer " + token } : {},
formData: extra,
success: (res) => {
const data = JSON.parse(res.data);
if (data.code !== 0) {
uni.showToast({ title: data.message || "上传失败", icon: "none" });
reject(data);
} else {
resolve(data.data);
}
},
fail: (err) => {
uni.showToast({ title: "上传失败", icon: "none" });
reject(err);
}
});
uploadTask.onProgressUpdate((res) => {
if (extra.onProgress) extra.onProgress(res.progress);
});
});
}
多图依次上传
选图返回的可能是多张,若接口要求逐张传,用串行循环依次上传,并在每张完成后回调当前进度。
export async function uploadMany(filePaths, extra = {}) {
const urls = [];
for (let i = 0; i < filePaths.length; i++) {
const url = await uploadFile(filePaths[i], extra);
urls.push(url);
if (extra.onStep) extra.onStep(i + 1, filePaths.length);
}
return urls;
}
页面中的用法
// pages/feedback/index.vue
import { chooseAndCompress, uploadMany } from "/utils/upload";
async function pickImage() {
const paths = await chooseAndCompress(3);
if (!paths.length) return;
const urls = await uploadMany(paths, {
dir: "feedback",
onStep: (done, total) => {
console.log("已上传 " + done + "/" + total);
}
});
images.value = urls;
}
需要注意
- chooseImage 的临时路径在小程序端仅本次会话有效,上传接口需立即消费,不能存库后下次再用;
- H5 端 uni.compressImage 部分浏览器不支持,可在 fail 回调直接返回原图;
- formData 里的字段必须为字符串或数字,对象会丢失;
- 响应 res.data 在部分端返回的是字符串,需先 JSON.parse 再判断业务码。
小结
把选图、压缩、上传封装成工具后,任意页面 3 行代码即可完成多图上传,后续替换 CDN 或加水印也只需改动一个文件。
评论(0)
暂无评论,快来抢沙发吧~