UniApp 图片上传与压缩:兼容小程序与 App 端的实践

在移动端应用开发中,图片上传是一个高频需求,而图片压缩则是提升上传效率、节省用户流量、降低服务器存储成本的关键环节。UniApp 作为跨端开发框架,需要同时兼顾小程序与 App 端的差异。本文将分享一套在 UniApp 中实现图片选择、压缩与上传的完整方案,并重点解决两端兼容性问题。

一、需求场景与技术选型

假设我们需要实现一个“用户头像上传”功能,要求:

  • 用户可以从相册选择或相机拍摄图片;
  • 图片大小限制在 2MB 以内,超出则自动压缩;
  • 压缩后上传至服务器,并显示上传进度;
  • 兼容微信小程序与 App(Android/iOS)。

UniApp 提供了 uni.chooseImageuni.compressImageuni.uploadFile 等 API,但不同平台的支持程度不同。例如,uni.compressImage 在微信小程序中支持较好,但在 App 端需要依赖原生插件或使用 plus.zip.compressImage。因此,我们需要封装一个统一的工具函数,根据平台调用不同的实现。

二、图片选择:统一入口

使用 uni.chooseImage 选择图片,它返回临时文件路径。注意:小程序返回的是 tempFilePaths,App 端返回的也是临时路径,但路径格式不同(如 _doc/file://)。我们只需关注后续处理。

function chooseImage() {
  return new Promise((resolve, reject) => {
    uni.chooseImage({
      count: 1,
      sizeType: ['compressed'], // 小程序端优先压缩,App 端忽略
      sourceType: ['album', 'camera'],
      success: (res) => {
        const tempFilePath = res.tempFilePaths[0];
        resolve(tempFilePath);
      },
      fail: reject
    });
  });
}

三、图片压缩:分平台实现

3.1 微信小程序端

小程序端可以直接使用 uni.compressImage,它底层调用 wx.compressImage。注意:该 API 仅支持压缩 JPG 格式,且压缩质量参数 quality 范围 0-100。

function compressImageMini(filePath, quality = 80) {
  return new Promise((resolve, reject) => {
    uni.compressImage({
      src: filePath,
      quality: quality,
      success: (res) => {
        resolve(res.tempFilePath);
      },
      fail: reject
    });
  });
}

3.2 App 端

App 端没有 uni.compressImage 的官方实现(HBuilderX 3.6+ 后部分支持,但兼容性不佳)。推荐使用 plus.zip.compressImage,它支持更丰富的参数,如宽度、高度、质量等。

function compressImageApp(filePath, quality = 80) {
  return new Promise((resolve, reject) => {
    plus.zip.compressImage({
      src: filePath,
      dst: '_doc/compressed_' + Date.now() + '.jpg',
      quality: quality,
      width: 'auto',
      height: 'auto',
      overwrite: true,
      success: (res) => {
        resolve(res.target);
      },
      fail: reject
    });
  });
}

3.3 统一压缩函数

根据当前平台判断调用哪个函数。可以通过 uni.getSystemInfoSync().platform 获取平台标识。

async function compressImage(filePath, quality = 80) {
  const platform = uni.getSystemInfoSync().platform;
  if (platform === 'android' || platform === 'ios') {
    return compressImageApp(filePath, quality);
  } else {
    // 小程序端
    return compressImageMini(filePath, quality);
  }
}

注意:小程序端 uni.compressImage 可能因为图片过大而失败,建议先判断文件大小。如果小于 200KB,可以不压缩。

四、获取文件大小与条件压缩

在压缩前,我们需要知道图片大小。小程序端可以使用 uni.getFileInfo,App 端可以使用 plus.io.resolveLocalFileSystemURL 获取文件信息。

function getFileSize(filePath) {
  return new Promise((resolve, reject) => {
    const platform = uni.getSystemInfoSync().platform;
    if (platform === 'android' || platform === 'ios') {
      plus.io.resolveLocalFileSystemURL(filePath, (entry) => {
        entry.getMetadata((metadata) => {
          resolve(metadata.size);
        }, reject);
      }, reject);
    } else {
      uni.getFileInfo({
        filePath: filePath,
        success: (res) => {
          resolve(res.size);
        },
        fail: reject
      });
    }
  });
}

然后封装一个智能压缩函数:

async function smartCompress(filePath, maxSize = 2 * 1024 * 1024, quality = 80) {
  const size = await getFileSize(filePath);
  if (size <= maxSize) {
    return filePath; // 无需压缩
  }
  return compressImage(filePath, quality);
}

五、图片上传:统一上传与进度处理

uni.uploadFile 在小程序与 App 端均可用,但需要注意:

  • 小程序端需要配置合法域名;
  • App 端可以直接使用 uni.uploadFile,但若需要更精细的控制(如分片上传),可使用 plus.uploader

这里使用 uni.uploadFile 实现基础上传,并监听进度。

function uploadFile(filePath, url, formData = {}) {
  return new Promise((resolve, reject) => {
    const uploadTask = uni.uploadFile({
      url: url,
      filePath: filePath,
      name: 'file',
      formData: formData,
      success: (res) => {
        if (res.statusCode === 200) {
          resolve(JSON.parse(res.data));
        } else {
          reject(new Error('上传失败:' + res.statusCode));
        }
      },
      fail: reject
    });

    uploadTask.onProgressUpdate((res) => {
      // 可以在这里更新进度条
      console.log('上传进度:' + res.progress + '%');
    });
  });
}

六、完整流程示例

将上述步骤组合,实现一个完整的头像上传函数:

async function uploadAvatar() {
  try {
    // 1. 选择图片
    const tempFilePath = await chooseImage();
    
    // 2. 智能压缩
    const compressedPath = await smartCompress(tempFilePath, 2 * 1024 * 1024, 80);
    
    // 3. 上传
    const result = await uploadFile(compressedPath, 'https://your-api.com/upload', {
      userId: '12345'
    });
    
    uni.showToast({ title: '上传成功', icon: 'success' });
    return result;
  } catch (error) {
    uni.showToast({ title: '上传失败', icon: 'none' });
    console.error(error);
  }
}

七、兼容性注意事项

  1. 小程序端uni.compressImage 仅支持 JPG,若选择 PNG 图片,需先转换为 JPG 或使用 Canvas 压缩。另外,压缩后的临时文件在小程序重启后会失效,需及时上传。
  2. App 端plus.zip.compressImagedst 路径建议使用 _doc/ 目录,避免权限问题。iOS 端需要注意相册权限和相机权限的配置。
  3. H5 端:本文未涉及,但可以使用 Canvas 进行压缩,上传则使用 uni.uploadFileXMLHttpRequest
  4. 性能优化:对于大图,建议先按最大边缩放(如 1080px)再压缩质量,避免内存溢出。小程序端可使用 uni.getImageInfo 获取宽高。

八、总结

通过封装 chooseImagecompressImagegetFileSizeuploadFile 四个核心函数,我们实现了 UniApp 下兼容小程序与 App 的图片上传与压缩方案。关键点在于:

  • 使用 uni.getSystemInfoSync().platform 区分平台;
  • 小程序端调用 uni.compressImage,App 端调用 plus.zip.compressImage
  • 上传前先判断文件大小,避免不必要的压缩;
  • 统一使用 uni.uploadFile 处理上传,并利用 onProgressUpdate 实现进度反馈。

这套方案已在多个项目中验证,能够有效降低图片体积 60%-80%,同时保证两端体验一致。开发者可根据实际需求调整压缩质量与尺寸阈值。

未经允许不得转载:任鹏个人博客 » UniApp 图片上传与压缩:兼容小程序与 App 端的实践

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏