必须等uni.getimageinfo的success回调返回宽高后再创建canvas并drawimage,否则绘制空白或导出黑块;需兼容h5的img.onload与uni.getimageinfo,安卓低版本webview返回0×0时应fallback至固定尺寸(如800×600),且canvas缩放须严格按原图宽高比动态计算目标尺寸,禁止硬编码宽高导致拉伸变形。

uni.getImageInfo 拿不到宽高就别画 canvas
很多压缩失败,根源是 uni.getImageInfo 还没返回,就急着创建 canvas 并 drawImage。结果 canvas 绘制空白,导出的图是黑块或 0x0 尺寸。
- 必须等
success回调拿到width和height后再计算目标尺寸 - H5 下
img.onload和uni.getImageInfo都要处理——前者兼容旧版、后者更稳定 - 某些安卓机(尤其低版本 WebView)会返回
width: 0, height: 0,需加兜底:比如 fallback 到固定尺寸(如 800×600)或直接跳过压缩
canvas 绘制前必须做等比缩放计算
直接设 canvas.width = 800、canvas.height = 600 是错的——会拉伸变形。必须按原图宽高比动态算出 targetWidth / targetHeight。
- 先判断长边:若
width > height,则以maxWidth为基准;否则以maxHeight为基准 - 公式统一用:
targetHeight = Math.round(originalHeight * maxWidth / originalWidth)(横向为主时) - 别用
Math.min或硬编码比例,否则小图(如头像)会被错误放大
App 端 compressImage 的 quality 不是质量值
传 {quality: 30} 想压体积?结果文件更大——因为 App 端 uni.compressImage 的 quality 参数实际是「最大边长」,单位 px,不是 JPEG 质量(0–1)。
- 实测:传
quality: 1000≈ 把长边缩到 1000px,系统自动用 JPEG + 默认质量(约 0.8),文件大小取决于原图分辨率 - 真要控体积(比如 ≤300KB),App 端也得走 canvas 方案,不能依赖
compressImage - 跨端判断写法:
if (uni.getSystemInfoSync().platform === 'app') { useCanvas() } else { uni.compressImage() }
toBlob / toDataURL 选哪个?看平台和后端要求
H5 下 canvas.toBlob 兼容性差(IE、部分安卓 WebView 不支持),而小程序不支持 toBlob,必须用 canvas.toDataURL 或 uni.canvasToTempFilePath。
- H5:优先
toDataURL('image/jpeg', 0.7)→ 得到 base64 字符串 → 用uni.uploadFile({ filePath: 'data:image/jpeg;base64,...' }),注意 header 加Content-Type: image/jpeg - 小程序:必须用
uni.canvasToTempFilePath,且fileType设为'jpg',quality才生效(0–1) - 别把 base64 当成临时路径传给
filePath,会静默失败
真实项目里最易忽略的是:压缩后没校验输出结果。比如 canvas 导出 webp(某些安卓机默认),但后端只认 jpg;或 base64 字符串超长导致请求头爆炸;又或者压缩完还是 4.8MB,卡在微信 5MB 限制外。这些都得在 uploadFile 前加一层检查。











