约 7 分钟读完 · 更新于 2026-10-21 · 文中每条都能在下面的配套工具里动手验证
大多数「上传失败」其实是三件不同的事被混成了一句话。体积限制是文件字节数,2MB 就是 2MB,超一点就拒;尺寸限制是像素边长,常见于图片处理服务,防止有人传一张 10000×8000 的图拖垮内存;数量限制是单次或单次会话的文件个数。前端只提示「文件过大」而不说是哪一个,用户就只能靠猜。所以产品层面第一件该做的事,是把限制值和提示文案一一对应:最大 2MB、最长边 2000px、最多 5 个文件,写清楚比写得客气有用。
| 限制值 | 适合的场景 | 用户为什么会撞上 | 合理的引导 |
|---|---|---|---|
| 1MB | 头像、聊天表情、缩略图、评论附件 | 手机直出照片普遍 3–8MB | 提示可自动压缩到 1MB,或提示重新选择 |
| 2MB | 表单里的图片凭证、报销单据、工单截图 | 截图本身不大,但多张就超了 | 按「单张还是单次总量」分别给限制 |
| 5MB | 一般内容平台的图片上传 | 高清手机图 + 原图习惯 | 给目标 KB 压缩入口,一键达标 |
| 10MB 及以上 | 需要后端建档的设计稿、演示视频 | 通常还要分片上传 | 显示进度与断点续传状态 |
| 不限制字节,只限制最长边 | 图片处理服务、内部素材库 | 超大分辨率图耗内存、易超时 | 给自动等比缩放,保留原始分辨率标记 |
Base64 用 4 个字符表达 3 个字节,天然带 33% 的膨胀,再加上一层 data URI 的前缀。所以一张 1MB 的图转成 Base64 大约 1.37MB,2MB 的图会变成 2.7MB。判断标准是用途:内嵌到 HTML、CSS 或 JSON 里直接给浏览器解码(比如邮件模板、报表导出、离线页面),Base64 省掉一次额外的 HTTP 请求,值这点体积;走接口上传文件、表单提交、任何要走分片或断点续传的场景,就老老实实传二进制,膨胀会直接顶到限制线上。另一个容易忽略的细节是 MIME 前缀必须和真实格式一致——声明 image/jpeg 却塞了 PNG 数据,部分后端会直接拒绝。
只判断后缀是最常见的漏洞来源:把任意内容改名成 .jpg 就能混过前端检查,后端必须再验一次文件头(magic number)。前端这一侧更重要的是把选择范围收窄到用户真的需要的几种:头像上传只给 jpg / png / webp;需要透明的设计素材再放开 svg,并且用文件头确认它确实是 SVG 而不是改名的文本。AVIF 值得单独提一句:体积优势明显,但如果你要兼容的客户端里有较老的安卓机或第三方 IM,先确认对方的解码能力,或者干脆在上传前统一转成 WebP,别让用户传了之后才发现发出去别人看不见。
const MAX_BYTES = 2 * 1024 * 1024; // 2MB
const MAX_EDGE = 2000; // 最长边
const OK_TYPE = ["image/jpeg", "image/png", "image/webp"];
function preflight(file) {
if (!OK_TYPE.includes(file.type)) {
return "只支持 JPG / PNG / WebP";
}
if (file.size > MAX_BYTES) {
return `单个文件 ${(file.size / 1048576).toFixed(1)}MB,超过 2MB`;
}
return readSize(file).then(({ w, h }) =>
Math.max(w, h) > MAX_EDGE
? `图片 ${w}×${h},最长边需 ≤ ${MAX_EDGE}px`
: null);
}# 至少校验文件头,再决定是否接收 head -c 12 file | xxd | head -1 # 00000000: ffd8 ffff ............ → JPEG # 8950 4e47 0d0a 1a0a ........ → PNG # RIFF....WEBPVP8 ............... → WebP # ....ftypavif .................. → AVIF(注意 avif 与 heic 共用容器,要看 brand)
顺序反了会白白损失画质。先转格式再缩放,缩放时又引入一次重采样;先压到 200KB 再缩放,缩放后的图因为像素变少、细节被抹掉,压缩算法已经没有多少可压的东西,边缘还会出现块状痕迹。正确顺序是:按实际显示尺寸等比缩放 → 需要的话换到更省体积的格式 → 设定目标 KB 让压缩工具自动迭代质量,直到刚好达标。用 <a class="faq-link" href="/tools/image-kb/">压缩到指定大小</a>这类工具时,要看清它是「尽量接近目标」还是「保证不超过目标」:前者可能在 200KB 处停下却仍显示 220KB,后者会继续迭代到真正达标为止,接口有硬限制时必须选后者。
看用途不看习惯。头像和评论附件 200KB 到 500KB 足够,正文配图 1MB 到 2MB 视觉上几乎看不出差别,工单截图 2MB 够用,需要印刷或建档的原图则不该设字节上限、只限制最长边。宁可给一个「压到目标大小」的按钮,也不要写死一个让用户无路可走的数字。
因为它省掉一次网络往返:浏览器拿到一段文本就能直接渲染,不用再发一个请求去取图片。邮件模板、报表导出、需要离线打开的页面里很划算。反过来,接口上传、表单提交、需要分片和断点续传的场景就该传二进制——膨胀的体积会直接顶穿限制线,而且 Base64 字符串无法高效地做范围续传。
典型的解码不兼容:文件没问题,是对方设备或应用不认识 AVIF。稳妥做法是上传前统一转成 WebP 或 JPEG,或者按客户端能力只给 WebP 与 JPEG 两份;用 格式转换工具试一次就知道是不是格式问题——如果转成 JPEG 能显示,答案已经很清楚了。
逐个文件显示状态和原因,不要合并成一条汇总。「第 3 张 2.4MB 超过限制,已跳过,其余 6 张成功」比「部分文件上传失败」有用得多——用户能直接定位到要重传哪一张。另外失败项要能单独重试,不要让整批重来。