FileReader读取图片→canvas压缩→toBlob生成Blob→FormData.append替换原文件;需设quality参数、处理filename、适配dpr、防内存溢出。

用 FileReader 读取图片后用 canvas 压缩再转为 Blob
原生 HTML 表单不支持上传前压缩,必须在提交前用 JS 拦截并替换 File 对象。核心路径是:监听 input[type="file"] → 用 FileReader 读取为 data URL → 绘制到 canvas(可缩放/降质)→ 调用 canvas.toBlob() 生成压缩后的 Blob → 替换原始文件对象。
注意:不能直接修改 FileList(只读),得用 FormData 手动 append 新的 Blob;否则后端收到的仍是原图。
-
canvas.toBlob()的第三个参数(质量值)仅对image/jpeg和image/webp有效,image/png忽略该参数 - 压缩前建议检查
file.type,非图片类型跳过处理,避免报错 - 大图(如 4000×3000)在低端安卓 WebView 中可能触发 canvas 内存限制,需加
try/catch
表单 submit 事件里阻止默认行为,用 fetch 或 XMLHttpRequest 发送压缩后数据
不要依赖 form.submit() 自动提交,它无法注入修改后的文件。必须在 form.addEventListener("submit", e => { e.preventDefault(); ... }) 中手动构造请求。
推荐用 fetch 配合 FormData:先创建空 FormData 实例,再 .append("image", compressedBlob, filename)(第三个参数保留原始文件名,否则后端收到的文件名可能是 blob)。
立即学习“前端免费学习笔记(深入)”;
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 若表单含其他字段(如
title、desc),需手动.append()进去,FormData不会自动收集未被 JS 修改的 input 值 - 后端接收时,字段名必须和
FormData.append()第一个参数一致,比如前端用append("avatar", ...),后端就不能按image解析 - 避免在
fetch外层设Content-Type头,让浏览器自动设置带 boundary 的multipart/form-data
压缩参数怎么设才兼顾清晰度和体积?
没有万能值,得按使用场景调:0.7 是 JPEG 常用起点,但手机拍摄图可压到 0.5,头像类小图建议不低于 0.6;尺寸缩放比优先用等比,目标宽度设 1200 能覆盖多数屏幕且不糊。
别硬编码尺寸,先用 img.naturalWidth / naturalHeight 获取原始分辨率,再按比例缩放 canvas,否则拉伸变形。
- 目标尺寸超过原图时,跳过缩放,只做质量压缩,防止放大模糊
- WebP 格式体积通常比 JPEG 小 25%~30%,但 iOS Safari 14 以下不支持,需降级逻辑
- 压缩耗时随图片分辨率指数增长,>5MB 的图建议加 loading 提示,或用
setTimeout分帧防卡死
为什么压缩后文件名丢失或变成 blob?
因为 canvas.toBlob() 生成的是无名 Blob,直接 append 到 FormData 会导致后端收到 filename="blob"。必须显式传入第三参数。
正确写法:formData.append("photo", blob, originalFile.name)。其中 originalFile.name 来自 input.files[0],不是 blob.name(它始终是空字符串)。
- 如果原始文件名含中文或特殊字符,部分旧版 PHP
$_FILES可能乱码,建议前端用encodeURIComponent编码,后端对应解码 - 某些 CDN 或代理会重写
Content-Disposition,导致后端取不到原始名,此时可在 FormData 里额外 append 一个filename字段作备用
img.width 设置 canvas 尺寸会导致模糊,应乘以 window.devicePixelRatio 并用 ctx.scale() 补偿——这点连很多开源库都漏处理。


















