gongxiangp2/lib/task-detail-upload-policy.ts

78 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

export const TASK_DETAIL_UPLOAD_LIMITS = {
/** 单个图片原图的保护上限,避免浏览器解码/服务器兜底处理一次吞掉过多内存。 */
maxSingleImageBytes: 100 * 1024 * 1024,
/** 一次任务允许的图片数量上限;详情长图的切片不计入此门禁。 */
maxImagesPerGroup: 60,
maxTotalImageCount: 120,
/** PSD/PSB/TIFF 等源文件不是图片计数的一部分,单独限制,防止小文件堆积绕过图片上限。 */
maxPsdFileCount: 20,
/** 原图与源文件的总条目数上限。 */
maxTotalSourceFileCount: 140,
/** 原图总大小保护上限,给大批量上传留出空间,同时避免误操作打爆客户端和服务器。 */
maxTotalSourceBytes: 1024 * 1024 * 1024,
maxPreviewCountPerImage: 60,
maxTotalPreviewCount: 1_000,
maxSinglePreviewBytes: 10 * 1024 * 1024,
maxTotalPreviewBytes: 512 * 1024 * 1024,
/** 普通预览为 800x1200,详情切片最大 1600x1500。 */
maxPreviewWidth: 800,
maxPreviewHeight: 1_200,
maxPreviewPixels: 800 * 1_200,
maxSliceWidth: 1_600,
maxSliceHeight: 1_500,
maxSlicePixels: 1_600 * 1_500,
maxPreviewAnimationFrames: 1,
/** 单个用户最多排队 3 个,防止一个账号长期占满唯一图片 worker。 */
maxQueuedJobsPerUser: 3,
/** 全站最多等待 20 个任务;processing 任务不计入等待容量。 */
maxQueuedJobsGlobal: 20,
/** 合法最大提交:140 个源文件 + 1000 个客户端预览。 */
maxTotalUploadSessionCount: 1_140,
} as const
export type TaskDetailUploadFileGroups = {
main: File[]
sku: File[]
detail: File[]
psd?: File[]
}
export function validateTaskDetailUploadFiles(groups: TaskDetailUploadFileGroups): string | null {
const imageGroups: Array<[string, File[]]> = [
['主图', groups.main],
['SKU 图', groups.sku],
['详情图', groups.detail],
]
for (const [label, files] of imageGroups) {
if (files.length > TASK_DETAIL_UPLOAD_LIMITS.maxImagesPerGroup) {
return `${label}最多上传 ${TASK_DETAIL_UPLOAD_LIMITS.maxImagesPerGroup} 张,请分批提交`
}
const oversized = files.find((file) => file.size > TASK_DETAIL_UPLOAD_LIMITS.maxSingleImageBytes)
if (oversized) {
return `${label}「${oversized.name}」超过单图 ${Math.round(TASK_DETAIL_UPLOAD_LIMITS.maxSingleImageBytes / 1024 / 1024)}MB 限制,请先压缩或拆分`
}
}
const imageFiles = imageGroups.flatMap(([, files]) => files)
if (imageFiles.length > TASK_DETAIL_UPLOAD_LIMITS.maxTotalImageCount) {
return `本次图片最多上传 ${TASK_DETAIL_UPLOAD_LIMITS.maxTotalImageCount} 张,请分批提交`
}
const psdFiles = groups.psd || []
if (psdFiles.length > TASK_DETAIL_UPLOAD_LIMITS.maxPsdFileCount) {
return `PSD 源文件最多上传 ${TASK_DETAIL_UPLOAD_LIMITS.maxPsdFileCount} 个,请分批提交`
}
const allFiles = [...imageFiles, ...psdFiles]
if (allFiles.length > TASK_DETAIL_UPLOAD_LIMITS.maxTotalSourceFileCount) {
return `本次原图和源文件最多上传 ${TASK_DETAIL_UPLOAD_LIMITS.maxTotalSourceFileCount} 个,请分批提交`
}
const totalBytes = allFiles.reduce((sum, file) => sum + Math.max(0, file.size), 0)
if (totalBytes > TASK_DETAIL_UPLOAD_LIMITS.maxTotalSourceBytes) {
return `本次素材总大小超过 ${Math.round(TASK_DETAIL_UPLOAD_LIMITS.maxTotalSourceBytes / 1024 / 1024 / 1024)}GB,请分批提交`
}
return null
}