UniApp文件上传大小限制问题解决方案

问题背景

最近在开发一个基于UniApp的wgt项目(小程序)时,遇到了一个让人头疼的文件上传问题。本项目是嵌入在安卓应用中的小程序,需要实现视频文件上传功能。由于是wgt项目,所以是在安卓应用内部运行的小程序,对兼容性和稳定性要求较高。

第一版方案:uni.chooseVideo插件

最初我使用了UniApp官方的 uni.chooseVideo 插件来实现视频选择功能。看起来很简单,但很快就遇到了兼容性问题:

兼容性问题

  • 低版本安卓:基本可以正常使用
  • 高版本安卓:打开相册时出现乱码,用户体验极差

这个问题让我意识到,官方插件在不同安卓版本上的兼容性并不稳定,特别是在wgt项目中,兼容性问题更加突出。

第二版方案:原生插件

为了解决兼容性问题,我们的团队想出了一个合理保守的方案:让安卓同事开发一个原生插件,通过 sendNativeEvent 来调用。

技术实现

javascript 复制代码
// 使用sendNativeEvent调用原生插件
uni.sendNativeEvent('插件名称xxx', {
  title: "",
  multiple: false,
  filetypes: "video/mp4"
}, (ret) => {
  // 处理返回结果
});

优势

  • 完全控制文件选择逻辑
  • 解决了安卓版本兼容性问题
  • 用户体验更加稳定
  • 使用自己开发的插件后,确实解决了眼下的兼容性问题

新的挑战:文件大小限制

解决了兼容性问题后,我遇到了更大的挑战------文件上传大小限制

问题现象

当文件超过20MB时,UniApp默认的nginx服务器直接返回错误:

html 复制代码
<html>
<head><title>413 Request Entity Too Large</title>
</head>
<body>
<center><h1>413 Request Entity Too Large</h1>
</center>
<hr><center>nginx</center>
</body>
</html>

问题分析

通过多次验证,我发现:

  • 20MB以下:可以正常"通过"
  • 20MB以上:UniApp默认的nginx直接断开连接,请求无法发送出去

这里的"通过"很重要,说明20MB是一个关键的分界线。

深入调查:uni.chooseVideo的秘密

于是我好奇,为什么会出现这样的情况呢?然后我用 uni.chooseVideo 插件来试一下,发现了一个有趣的现象:

观察到的现象

  1. 选择视频后会显示长时间的loading,等待时间很长,至少30秒
  2. 最终上传的文件大小比原文件小很多
  3. 本地40MB的视频被压缩到14MB

关键发现

原来 uni.chooseVideo 插件默认会自动压缩视频!这就是为什么它能够处理大文件的原因。

uni.chooseVideo 有个属性叫 compressed,你需要把它关掉,不然它就默认给你压缩了。但是问题来了,即使把 compressed 关掉也没用,如果文件超过20MB的话,还是会被断掉。

解决方案:手动视频压缩

那只能选择压缩视频的方法了。所以我使用 sendNativeEvent 选择完毕视频后,也给视频进行压缩。UniApp官网也提供了免费的压缩插件,我就直接使用了它,压缩效果还不错,但有一点慢。使用的是 uni.compressVideo

技术实现

javascript 复制代码
// 判断文件类型
const isVideo = this.isVideoFile(file);

if (isVideo) {
  // 视频文件进行压缩
  const compressedFile = await compressVideoWithUni(file);
  await this.handleFileUpload(compressedFile);
} else {
  // 非视频文件直接上传
  await this.handleFileUpload(file);
}

// 使用UniApp官方压缩插件
export const compressVideoWithUni = async (file) => {
  return new Promise((resolve, reject) => {
    uni.compressVideo({
      src: file.path,
      quality: 'low', // 使用最低质量,最大压缩
      success: (res) => {
        const compressedFile = {
          ...file,
          path: res.tempFilePath,
          size: res.size
        };
        resolve(compressedFile);
      },
      fail: (error) => {
        reject(new Error(error.errMsg || '压缩失败'));
      }
    });
  });
};

文件类型判断

javascript 复制代码
isVideoFile(file) {
  if (!file || !file.path) return false;
  
  const extension = file.path.split('.').pop()?.toLowerCase();
  const videoExtensions = ['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv', 'webm', 'm4v', '3gp'];
  
  return videoExtensions.includes(extension);
}

最终效果

解决的问题

  1. ✅ 安卓版本兼容性问题
  2. ✅ 文件大小限制问题
  3. ✅ 用户体验优化

技术特点

  • 智能处理:只有视频文件才进行压缩
  • 容错机制:压缩失败时自动降级到原文件
  • 用户友好:显示压缩进度和状态
  • 性能优化:避免不必要的压缩操作

经验总结

技术要点

  1. UniApp的局限性:官方插件在某些场景下存在兼容性问题
  2. 文件大小限制 :20MB是uni.uploadFile默认nginx的限制
  3. 视频压缩的重要性:大文件必须压缩才能正常上传

开发建议

  1. 测试覆盖:在不同安卓版本上充分测试
  2. 用户体验:提供清晰的状态反馈
  3. 错误处理:实现完善的容错机制
  4. 性能考虑:避免不必要的文件处理

技术选型

  • 原生插件 > 官方插件(在兼容性要求高的场景)
  • 手动压缩 > 依赖默认行为(在需要精确控制的场景)

UniApp官方文档不够完善

在解决这个问题的过程中,我发现了一个让人无语的问题:UniApp官方文档的严重不足

文档缺失问题

  1. uni.uploadFile 文档:完全没有提到20MB的文件大小限制
  2. uni.chooseVideo 文档 :虽然有 compressed 属性说明,但没有明确说明压缩的具体行为和时间
  3. 错误处理:413错误在官方文档中没有任何说明

开发者体验问题

  • 需要自己摸索和踩坑才能发现问题
  • 官方文档过于简单,缺乏实际使用场景的说明
  • 错误信息不够友好,调试困难

建议改进

  1. 完善文档:明确标注各种限制和约束
  2. 错误说明:提供常见错误的解决方案
  3. 最佳实践:增加实际项目中的使用建议

结语

这个问题的解决过程让我深刻体会到,在移动端开发中,兼容性和性能优化永远是绕不开的话题。通过这次经历,我不仅解决了当前的问题,还为自己积累了宝贵的经验。

同时,我也深刻认识到官方文档的局限性,作为开发者,我们不能完全依赖官方文档,需要在实际项目中不断探索和总结。

记住:有时候官方插件并不是最佳选择,根据具体需求选择合适的技术方案才是王道!


本文记录了UniApp文件上传大小限制问题的完整解决过程,希望能为遇到类似问题的开发者提供参考。