领导:最近网络有点卡,图片加载的比较慢!
牛马:那咋办?
领导:你把图片都压缩一下,上传图片之前先压缩,能做到吗?
牛马:能(内心99个不愿意)
分析
后台的常规上传
一般做后台的比较多,后台的上传方式一般都是:
- 构建
formData
- 传参,形如下图
其中的binary,一般都是一个file对象,里面包含了:文件名、大小、文件类型等
这样上传之后,后台会给我们返回对应的地址等信息
uni-app的上传
uni-app的上传和常规的后台不一样,uni-app自己封装了上传APIuni.uploadFile
。
它的使用方法如下
javascript
uni.chooseImage({
success: (chooseImageRes) => {
const tempFilePaths = chooseImageRes.tempFilePaths;
uni.uploadFile({
url: 'https://www.example.com/upload', //仅为示例,非真实的接口地址
filePath: tempFilePaths[0],
name: 'file',
formData: {
'user': 'test'
},
success: (uploadFileRes) => {
console.log(uploadFileRes.data);
}
});
}
});
它只需要传filePath或者files对象数组。
其中的filePath是一个blob形式的本地url,形如:
blob:https://192.168.3.98:8080/704d5537-5849-4db6-9815-cc7efd8d8cf3
这个blob地址是可以本地临时访问的。
uni-app的上传形式和后台管理,看起来是一样的!
你要是这么以为,你就错了!
异同分析
相同点
上传格式:都是(具体和后台约定格式)
- file: (binary)
- folder: images
不同点
file的binary
:
- 后台的是file文件对象
- uniapp的是blob地址字符串(files里的uri也是)
图片压缩
图片压缩可以用browser-image-compression
库
yarn add browser-image-compression
简单封装一下压缩函数:
javascript
import imageCompression from 'browser-image-compression'
// 压缩图片
export async function compressImage(file) {
// 默认配置
const options = {
initialQuality: 0.5, // 压缩率
}
try {
let tempFile = await imageCompression(file, options)
// 创建一个新的 File 对象
const newFile = new File([tempFile], file.name, {
type: tempFile.type,
})
return newFile;
} catch (error) {
console.error('压缩图片失败', error)
return file
}
}
我们现在可以得到compressImage
压缩后的file对象,然后把它转为blob地址就行了,这不就成功了吗 把上面的函数里的newFile
转化一下
const blobUrl = URL.createObjectURL(newFile)
然后这个值就是filePath的值了,也就是下面的方法二:
javascript
uni.uploadFile({
url: baseUrl + '/file/upload', //真实的接口地址
// filePath: myUploadFile.path, // 方法一:原版的直接传blob路径
filePath: await compressImage(myUploadFile.file), // 方法二:压缩后再转为blob路径
name: 'file',
header: { Authorization: 'Bearer ' + getToken() },
formData: { folder: 'images' },
success: (_data) => {
console.log('上传成功返回:', _data.data)
},
fail: (res) => {
console.log('上传失败', res)
},
})
于是我兴冲冲地试了一下,图片大小果然压缩了50%,而且不影响清晰度,但是,图片名称怎么没了,名称可是需要显示的呀!!!
于是我又看向代码,uni-app也没传文件名称啊,怎么它的就能显示名称,我压缩之后的就不能显示名称呢?
原因
于是,我问了AI的解决方案,AI给了我2个解决方案:
- 在
formData
中加文件名name,或者files里加name; - 明文传参文件名称,后端加上文件名称解析;
第一种方案是不合理的,我们尝试一下,如果你在formData中传name,是这样的
如果用files的方式传,结果是这样的,直接把file给覆盖了
第二种方案呢需要后端改动,显然不合理,因为之前的后台就可以直接传,也能直接返回文件名称......
等等,为什么后台的可以,而uni-app的却不可以呢?他们用的是同一个接口呀。
改造之路
上面其实已经有对比了,后台传的file对象的binary格式,uni-app传的是bloburl的字符串地址格式的binary,所以,我只要改造一下不就好了吗。
小插曲,我问了AI为什么uni-app上传的file都是blob url字符串地址,原版的就可以解析出名称,而压缩后的就无法解析呢?
AI回答:uni-app的uploadFile函数,底层做了自动处理,自动把名称以某种对应方式传过去了,而你压缩后的blob地址,就无法自动处理,也就没有名称了
于是,我就准备用uni.request重新封装,构建formData,在formData里传入压缩后的file对象,就像后台的处理方式一样,可是又报错了,因为uni.request请求的data参数不支持formData格式的
这下麻爪了,最后我就想,干脆我连请求都不用你的uni.request了,限制这么多,直接用原始的XMLHttpRequest
构建封装请求,重新封装的代码如下:
javascript
// 创建 FormData 对象
const formData = new FormData();
formData.append('file', await compressImage(toUploadArr[i].file));
formData.append('folder', 'images');
// 使用 XMLHttpRequest 手动发送请求
const xhr = new XMLHttpRequest();
xhr.open('POST', baseUrl + '/file/upload', true);
xhr.setRequestHeader('Authorization', 'Bearer ' + getToken());
xhr.onload = () => {
if (xhr.status === 200) {
// 提示上传成功等操作
} else {
uni.showToast({ title: '上传失败', icon: 'none' });
}
};
xhr.onerror = () => {
this.$modal.closeLoading();
this.fileLists.pop();
uni.showToast({ title: '上传失败', icon: 'none' });
};
xhr.send(formData);
看下效果,改造后的确实能正确返回文件名称了,而且也确实是压缩过了的:
打完收工!
往期文章推荐
《若依ruoyi全栈(nodejs + vue3)+ AI助手,前端表示很香》
《🚀若依Nodejs后台、实现90%以上接口,附体验地址、源码、拓展特色功能🚀》
《前端怎么测网速?》