在 H5 移动端开发中,图片压缩上传是一个经典且重要的场景,主要目的是节省用户流量、提升上传速度、减轻服务器存储压力。
以下是完整的开发流程,涵盖从前端处理到后端接收的全链路方案:
核心流程概览
- 用户选择图片(拍照/相册)
- 前端读取文件(FileReader / Canvas)
- 前端压缩处理(Canvas 重绘 + 格式转换)
- 构建 FormData
- AJAX/Fetch 上传
- 后端接收与存储
详细开发步骤
触发图片选择
使用 <input type="file"> 标签,并设置 accept 属性限制图片类型。
<input type="file" id="imageInput" accept="image/" capture="environment">
accept="image/":仅允许选择图片。capture="environment":在移动端直接调用后置摄像头(可选)。capture="user":调用前置摄像头。
读取文件并预览
监听 change 事件,获取 File 对象,并使用 URL.createObjectURL 或 FileReader 进行预览。
const input = document.getElementById('imageInput');
input.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
// 可选:校验文件大小(例如限制 10MB 以内)
if (file.size > 10 1024 1024) {
alert('图片太大,请重新选择');
return;
}
// 预览图片
const imageUrl = URL.createObjectURL(file);
// ... 显示预览图
});
前端压缩(核心步骤)
这是最关键的一步,通过 Canvas 将图片绘制为指定尺寸和质量,再转换为 Base64 或 Blob。
压缩策略:
- 尺寸压缩
:限制最大宽度/高度(如 1920px 或 1080px)。
- 质量压缩:通过
toBlob的quality参数(0.0 – 1.0)控制 JPEG/WebP 质量。 - 格式转换:优先转换为
image/webp(体积更小,兼容性较好),降级为image/jpeg。
压缩函数示例:
function compressImage(file, maxWidth = 1920, maxHeight = 1920, quality = 0.7) {
return new Promise((resolve, reject) => {
const img = new Image();
img.src = URL.createObjectURL(file);
img.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 计算缩放比例
let width = img.width;
let height = img.height;
if (width > height) {
if (width > maxWidth) {
height = maxWidth / width;
width = maxWidth;
}
} else {
if (height > maxHeight) {
width = maxHeight / height;
height = maxHeight;
}
}
canvas.width = width;
canvas.height = height;
// 绘制图片
ctx.drawImage(img, 0, 0, width, height);
// 转换为 Blob
// 优先尝试 WebP,如果浏览器不支持则降级为 JPEG
const type = 'image/webp';
canvas.toBlob((blob) => {
if (!blob) {
// 降级处理
canvas.toBlob((blob) => {
resolve({
blob: blob,
name: file.name.replace(/.[^/.]+$/, "") + ".jpg",
type: 'image/jpeg'
});
}, 'image/jpeg', quality);
} else {
resolve({
blob: blob,
name: file.name.replace(/.[^/.]+$/, "") + ".webp",
type: 'image/webp'
});
}
}, type, quality);
// 释放内存
URL.revokeObjectURL(img.src);
};
img.onerror = (err) => reject(err);
});
}
构建 FormData 并上传
将压缩后的 Blob 放入 FormData 中,使用 fetch 或 axios 上传。
async function uploadImage(file) {
try {
// 1. 压缩图片
const { blob, name, type } = await compressImage(file);
// 2. 构建 FormData
const formData = new FormData();
formData.append('file', blob, name); // 注意第三个参数是文件名
// 3. 上传
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
// 注意:不要手动设置 Content-Type,浏览器会自动设置 multipart/form-data 并包含 boundary
});
const result = await response.json();
console.log('上传成功:', result);
} catch (error) {
console.error('上传失败:', error);
}
}
后端处理(简要说明)
后端接收到 multipart/form-data 后:
- 解析文件流。
- (可选)二次校验图片格式和大小。
- 存储到本地磁盘或对象存储(如简米云 OSS、酷番云 COS、AWS S3)。
- 返回图片 URL 给前端。
关键优化与注意事项
| 优化点 | 说明 |
|---|---|
| WebP 支持检测 | 使用 canvas.toBlob 时,如果浏览器不支持 WebP,会静默失败,务必做好降级到 JPEG 的准备。 |
| Exif 方向信息 | 手机拍照后,图片可能因方向传感器导致旋转,压缩前需读取 Exif 信息并校正方向(可使用 exif-js 库)。 |
| 内存管理 | 大图片在 Canvas 中处理时会占用大量内存,处理完后务必调用 URL.revokeObjectURL 释放内存,避免移动端页面崩溃。 |
| 异步处理 | 压缩是耗时操作,务必使用 Promise 或 async/await,避免阻塞 UI 线程。 |
|
断点续传/分片上传 | 如果图片较大(如视频封面或高清原图),建议后端支持分片上传,前端配合使用 Blob.slice() 分片。 |
| 压缩参数调优 | quality 参数建议从 0.7 开始测试,平衡画质与体积,对于缩略图,可设为 0.5;对于高清展示图,建议 0.8-0.9。 |
推荐第三方库(简化开发)
如果不想手动处理 Canvas 和 Exif,可使用成熟库:
-
Compressor.js
- 轻量级,专门用于前端图片压缩。
- 自动处理 Exif 方向、格式转换、质量压缩。
- GitHub:
https://github.com/fengyuanchen/compressorjs
-
browser-image-compression
- 功能类似,API 更简洁,支持 Promise。
- GitHub:
https://github.com/Donaldcwl/browser-image-compression
使用 Compressor.js 示例:
import Compressor from 'compressorjs';
new Compressor(file, {
quality: 0.6,
maxWidth: 1920,
maxHeight: 1920,
success(result) {
// result 是压缩后的 Blob
const formData = new FormData();
formData.append('file', result, result.name);
// 上传...
},
error(err) {
console.log(err.message);
},
});
| 阶段 | 关键动作 |
|---|---|
| 选择 | <input type="file" accept="image/"> |
| 压缩 | Canvas 重绘 + toBlob + 质量/尺寸控制 |
| 上传 | FormData + fetch/axios |
| 优化 | 处理 Exif 方向、WebP 降级、内存释放 |
通过以上流程,你可以实现一个高效、稳定、用户体验良好的 H5 图片上传功能。
首发原创文章,作者:世雄 - 原生数据库架构专家,如若转载,请注明出处:https://idctop.com/article/479861.html



