feat(attach): 附件可配置限额、流式落盘与分阶段上传
管理端可配类型/体积;上传改流式避免整文件进内存;发帖先选再传并拦截未上传提交;提高 Next proxy 体积上限。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
58
frontend/lib/postFile.ts
Normal file
58
frontend/lib/postFile.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
/** 帖子附件客户端校验(与后端 setting / fileallow 对齐;安全边界仍在 Go) */
|
||||
|
||||
export const FILE_MAX_MB_DEFAULT = 20;
|
||||
export const FILE_MAX_COUNT_DEFAULT = 10;
|
||||
|
||||
export function normalizeExt(nameOrExt: string): string {
|
||||
const base = nameOrExt.includes("/")
|
||||
? nameOrExt.split(/[/\\]/).pop() || ""
|
||||
: nameOrExt;
|
||||
const i = base.lastIndexOf(".");
|
||||
const ext = (i >= 0 ? base.slice(i + 1) : base).trim().toLowerCase();
|
||||
return ext.replace(/[^a-z0-9]/g, "").slice(0, 16);
|
||||
}
|
||||
|
||||
export function buildAcceptAttr(exts: string[], limit: boolean): string | undefined {
|
||||
if (!limit) return undefined;
|
||||
if (!exts.length) return undefined;
|
||||
return exts.map((e) => `.${e}`).join(",");
|
||||
}
|
||||
|
||||
export function isAllowedPostFileName(
|
||||
name: string,
|
||||
opts: { limit: boolean; exts: string[] }
|
||||
): boolean {
|
||||
if (!opts.limit) return true;
|
||||
const ext = normalizeExt(name);
|
||||
if (!ext) return false;
|
||||
return opts.exts.includes(ext);
|
||||
}
|
||||
|
||||
export function formatMB(n: number): string {
|
||||
return `${n}MB`;
|
||||
}
|
||||
|
||||
export type PostFileRejectReason = "size" | "type" | "count" | null;
|
||||
|
||||
export function rejectPostFile(
|
||||
file: File,
|
||||
opts: {
|
||||
limit: boolean;
|
||||
exts: string[];
|
||||
maxMB: number;
|
||||
maxCount: number;
|
||||
currentCount: number;
|
||||
}
|
||||
): { reason: PostFileRejectReason; message?: string } {
|
||||
if (opts.currentCount >= opts.maxCount) {
|
||||
return { reason: "count", message: `最多 ${opts.maxCount} 个附件` };
|
||||
}
|
||||
const maxBytes = opts.maxMB * 1024 * 1024;
|
||||
if (file.size > maxBytes) {
|
||||
return { reason: "size", message: `附件不能超过 ${opts.maxMB}MB` };
|
||||
}
|
||||
if (!isAllowedPostFileName(file.name, opts)) {
|
||||
return { reason: "type", message: "不支持该附件格式" };
|
||||
}
|
||||
return { reason: null };
|
||||
}
|
||||
Reference in New Issue
Block a user