uni-app踩坑记录【图片先压缩再上传】

领导:最近网络有点卡,图片加载的比较慢!

牛马:那咋办?

领导:你把图片都压缩一下,上传图片之前先压缩,能做到吗?

牛马:能(内心99个不愿意)

分析

后台的常规上传

一般做后台的比较多,后台的上传方式一般都是:

  1. 构建formData
  2. 传参,形如下图

其中的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个解决方案:

  1. formData中加文件名name,或者files里加name;
  2. 明文传参文件名称,后端加上文件名称解析;

第一种方案是不合理的,我们尝试一下,如果你在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%以上接口,附体验地址、源码、拓展特色功能🚀

vue3怎么和大模型交互?

前端怎么测网速?

相关推荐
irises12 分钟前
tabby-vscode代码补全的一些阅读笔记
前端·javascript
2501_9068014813 分钟前
BY组态-低代码web可视化组件
前端·物联网·低代码·数学建模·编辑器·web
hang_bro15 分钟前
element-plus e-tabs与pinia 一起使用的问题
前端·vue.js
VitStratUp16 分钟前
antdvue+tree+transfer+vue3 实现树形带搜索穿梭框
前端·vue.js
wangpq16 分钟前
微信小程序map组件渲染几百个marker后,页面卡顿,如何解决?
vue.js·微信小程序
千野竹之卫17 分钟前
2025最新云渲染网渲100渲染农场使用方法,渲染100邀请码1a12
开发语言·前端·javascript·数码相机·3d·3dsmax
前端提桶人19 分钟前
Win11 安装 Sentry 监控
linux·前端
南茗啊20 分钟前
echarts地图轮播markpoint-自用记录📝
前端·echarts
__不想说话__24 分钟前
面试官问我React Router原理,我掏出了平底锅…
前端·javascript·react.js
yzzzz25 分钟前
面试官:聊聊数组扁平化
javascript·面试