Compress site images before upload

This commit is contained in:
Frudrax Cheng
2026-06-02 11:18:10 +08:00
parent 2bf43ac3e3
commit 0e05568c7a
+59 -2
View File
@@ -22,6 +22,62 @@ const SERVICE_TYPE_LABEL: Record<AftersalesServiceType, string> = {
maintenance: '售后维保',
};
const SITE_IMAGE_MAX_EDGE = 1600;
const SITE_IMAGE_QUALITY = 0.78;
async function compressSiteImage(file: File): Promise<File> {
if (!file.type.startsWith('image/') || file.type === 'image/gif') {
return file;
}
const imageUrl = URL.createObjectURL(file);
try {
const img = await loadImage(imageUrl);
const scale = Math.min(1, SITE_IMAGE_MAX_EDGE / Math.max(img.width, img.height));
const width = Math.max(1, Math.round(img.width * scale));
const height = Math.max(1, Math.round(img.height * scale));
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
if (!ctx) return file;
ctx.drawImage(img, 0, 0, width, height);
const blob = await canvasToBlob(canvas, 'image/jpeg', SITE_IMAGE_QUALITY);
if (!blob || blob.size >= file.size) {
return file;
}
const name = file.name.replace(/\.[^.]+$/, '') || 'site-image';
return new File([blob], `${name}.jpg`, {
type: 'image/jpeg',
lastModified: Date.now(),
});
} catch {
return file;
} finally {
URL.revokeObjectURL(imageUrl);
}
}
function loadImage(src: string): Promise<HTMLImageElement> {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = src;
});
}
function canvasToBlob(
canvas: HTMLCanvasElement,
type: string,
quality: number,
): Promise<Blob | null> {
return new Promise((resolve) => canvas.toBlob(resolve, type, quality));
}
function AftersalesConfirmPage() {
const { serialNumber = '' } = useParams<{ serialNumber: string }>();
const [order, setOrder] = useState<AftersalesPublicView | null>(null);
@@ -125,7 +181,8 @@ function AftersalesConfirmPage() {
if (!files || files.length === 0) return;
setUploadingImages(true);
try {
const images = await aftersalesApi.uploadSiteImages(serialNumber, Array.from(files));
const compressedFiles = await Promise.all(Array.from(files).map(compressSiteImage));
const images = await aftersalesApi.uploadSiteImages(serialNumber, compressedFiles);
setOrder((prev) => (prev ? { ...prev, siteImages: images } : prev));
message.success('现场图片上传成功');
} catch (err: any) {
@@ -298,7 +355,7 @@ function AftersalesConfirmPage() {
<label className="aftersales-upload-trigger">
<UploadOutlined />
<span>{uploadingImages ? '上传中...' : '上传现场图片'}</span>
<small> 6 5MB</small>
<small> 6 </small>
<input
type="file"
accept="image/jpeg,image/png,image/webp,image/heic,image/heif"