feat: 异步解析大文件WPS报备资料
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
import { assertUploadSize } from './files.service';
|
||||
|
||||
describe('FilesService report-material upload limits', () => {
|
||||
const workbook = {
|
||||
originalname: '行业报备.xlsx',
|
||||
mimetype: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
};
|
||||
|
||||
it('allows report-material imports up to 100MB without raising the generic file limit', () => {
|
||||
expect(() =>
|
||||
assertUploadSize('report_material_import', { ...workbook, size: 40 * 1024 * 1024 }),
|
||||
).not.toThrow();
|
||||
expect(() => assertUploadSize('other', { ...workbook, size: 40 * 1024 * 1024 })).toThrow('10MB');
|
||||
expect(() =>
|
||||
assertUploadSize('report_material_import', { ...workbook, size: 101 * 1024 * 1024 }),
|
||||
).toThrow('100MB');
|
||||
});
|
||||
});
|
||||
@@ -40,10 +40,12 @@ export class FilesService {
|
||||
) {}
|
||||
|
||||
list(tenantId?: string) {
|
||||
return this.prisma.fileObject.findMany({
|
||||
where: tenantId ? { tenantId } : undefined,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
}).then((items) => items.map(serializeFileObject));
|
||||
return this.prisma.fileObject
|
||||
.findMany({
|
||||
where: tenantId ? { tenantId } : undefined,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
})
|
||||
.then((items) => items.map(serializeFileObject));
|
||||
}
|
||||
|
||||
create(data: CreateFileObjectDto) {
|
||||
@@ -71,7 +73,7 @@ export class FilesService {
|
||||
}
|
||||
|
||||
async upload(data: UploadFileDto, file: { originalname: string; mimetype: string; size: number; buffer: Buffer }) {
|
||||
assertUploadSize(file);
|
||||
assertUploadSize(data.purpose, file);
|
||||
const fileName = normalizeMultipartFileName(file.originalname);
|
||||
const safeName = fileName.replace(/[^\w.\-\u4e00-\u9fa5]/g, '_');
|
||||
const objectKey = `${data.prefix ?? data.purpose}/${Date.now()}-${randomUUID()}-${safeName}`;
|
||||
@@ -87,7 +89,11 @@ export class FilesService {
|
||||
});
|
||||
}
|
||||
|
||||
async uploadForClient(userId: string | undefined, data: UploadFileDto, file: { originalname: string; mimetype: string; size: number; buffer: Buffer }) {
|
||||
async uploadForClient(
|
||||
userId: string | undefined,
|
||||
data: UploadFileDto,
|
||||
file: { originalname: string; mimetype: string; size: number; buffer: Buffer },
|
||||
) {
|
||||
const tenantId = await this.resolveClientTenantId(userId);
|
||||
const prefixRule = CLIENT_UPLOAD_RULES[data.purpose];
|
||||
if (!prefixRule || !data.prefix || !prefixRule.test(data.prefix)) {
|
||||
@@ -135,18 +141,33 @@ export class FilesService {
|
||||
|
||||
const IMAGE_UPLOAD_MAX_BYTES = 2 * 1024 * 1024;
|
||||
const FILE_UPLOAD_MAX_BYTES = 10 * 1024 * 1024;
|
||||
const REPORT_MATERIAL_IMPORT_MAX_BYTES = 100 * 1024 * 1024;
|
||||
const IMAGE_FILE_EXTENSION = /\.(?:avif|bmp|gif|heic|heif|jpe?g|png|svg|webp)$/i;
|
||||
|
||||
function assertUploadSize(file: { originalname: string; mimetype: string; size: number }) {
|
||||
export function assertUploadSize(purpose: string, file: { originalname: string; mimetype: string; size: number }) {
|
||||
const image = file.mimetype.toLowerCase().startsWith('image/') || IMAGE_FILE_EXTENSION.test(file.originalname);
|
||||
const limit = image ? IMAGE_UPLOAD_MAX_BYTES : FILE_UPLOAD_MAX_BYTES;
|
||||
const reportMaterialImport = purpose === 'report_material_import';
|
||||
const limit = reportMaterialImport
|
||||
? REPORT_MATERIAL_IMPORT_MAX_BYTES
|
||||
: image
|
||||
? IMAGE_UPLOAD_MAX_BYTES
|
||||
: FILE_UPLOAD_MAX_BYTES;
|
||||
if (file.size > limit) {
|
||||
throw new BadRequestException(image ? '图片大小不能超过 2MB' : '文件大小不能超过 10MB');
|
||||
throw new BadRequestException(
|
||||
reportMaterialImport
|
||||
? '报备资料文件大小不能超过 100MB'
|
||||
: image
|
||||
? '图片大小不能超过 2MB'
|
||||
: '文件大小不能超过 10MB',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeMultipartFileName(value: string) {
|
||||
if (![...value].some((character) => character.charCodeAt(0) > 0x7f) || [...value].some((character) => character.charCodeAt(0) > 0xff)) {
|
||||
if (
|
||||
![...value].some((character) => character.charCodeAt(0) > 0x7f) ||
|
||||
[...value].some((character) => character.charCodeAt(0) > 0xff)
|
||||
) {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user