feat(upload): 前端上传前 Canvas 压缩图片(大图缩至1920 + JPEG q=0.78)

Co-Authored-By: Claude <noreply@anthropic.com>
@
This commit is contained in:
cc 2026-06-24 23:24:55 +08:00
parent 26443a1df5
commit f4b417bcaf

View File

@ -498,6 +498,31 @@ export function PhotoWallPage({ onExit, token, folderName, isAdmin, onForbidden
})
}, [addEl, spawnPos])
/* ── 图片压缩Canvas缩小 + 转 JPEG─────────────────────── */
/** data URL data URL > 1920 1920JPEG q=0.78
* */
function compressImage(dataUrl: string): Promise<string> {
return new Promise((resolve) => {
const img = new Image()
img.onload = () => {
try {
let w = img.naturalWidth
let h = img.naturalHeight
// 大图缩小
if (w > 1920) { h = Math.round(h * 1920 / w); w = 1920 }
const canvas = document.createElement('canvas')
canvas.width = w; canvas.height = h
const ctx = canvas.getContext('2d')
if (!ctx) { resolve(dataUrl); return }
ctx.drawImage(img, 0, 0, w, h)
resolve(canvas.toDataURL('image/jpeg', 0.78))
} catch { resolve(dataUrl) }
}
img.onerror = () => resolve(dataUrl)
img.src = dataUrl
})
}
/* ── 图片上传 ─────────────────────────────────────────────── */
const handleFiles = useCallback(
(files: FileList | File[]) => {
@ -511,8 +536,8 @@ export function PhotoWallPage({ onExit, token, folderName, isAdmin, onForbidden
imgs.forEach((file) => {
if (file.size > 10 * 1024 * 1024) showToast('图片较大,建议压缩后上传')
const reader = new FileReader()
reader.onload = (ev) => {
const src = ev.target?.result as string
reader.onload = async (ev) => {
const rawSrc = ev.target?.result as string
// 个人墙有张数上限
if (maxPhotos > 0) {
const count = elsRef.current.filter((e) => e.type === 'photo').length
@ -526,6 +551,8 @@ export function PhotoWallPage({ onExit, token, folderName, isAdmin, onForbidden
showToast(`已达上限(${maxItems} 条)`)
return
}
// 客户端压缩(大图缩小 + 转 JPEG q=0.78
const src = await compressImage(rawSrc)
const uploadFn = isPersonal
? (t: string, d: string) => uploadPhoto(t, d)
: (t: string, d: string) => uploadFolderPhoto(t, folderName!, d)