@file-ud.js/plugins 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,563 @@
1
+ # 文件验证插件 (FileValidatorPlugin)
2
+
3
+ ## 📖 功能介绍
4
+
5
+ 文件验证插件在文件传输前进行全面的校验,确保文件符合业务要求,避免无效或恶意文件传输到服务器。
6
+
7
+ ### ✨ 核心特性
8
+
9
+ - ✅ **大小验证**:支持最小/最大文件大小限制
10
+ - ✅ **类型验证**:基于 MIME 类型或文件扩展名
11
+ - ✅ **空文件检测**:防止上传空文件
12
+ - ✅ **自定义验证**:支持自定义验证函数
13
+ - ✅ **错误消息定制**:可自定义所有错误提示
14
+ - ✅ **最高优先级**:在所有插件之前执行(priority: 0)
15
+ - ✅ **完全非侵入式**:通过插件系统实现,不改动核心代码
16
+
17
+ ---
18
+
19
+ ## 🚀 快速开始
20
+
21
+ ### 1. 安装插件
22
+
23
+ ```bash
24
+ npm install @file-ud.js/plugins
25
+ ```
26
+
27
+ ### 2. 基本使用
28
+
29
+ ```typescript
30
+ import { FileUD } from '@file-ud.js/core';
31
+ import { FileValidatorPlugin } from '@file-ud.js/plugins/uploader';
32
+
33
+ const uploader = FileUD.createUploader("myUploader", {
34
+ action: '/api/upload',
35
+ });
36
+
37
+ // 使用默认配置(无限制)
38
+ uploader.use(new FileValidatorPlugin());
39
+ ```
40
+
41
+ ### 3. 常用配置
42
+
43
+ ```typescript
44
+ uploader.use(new FileValidatorPlugin({
45
+ maxSize: 10 * 1024 * 1024, // 最大 10MB
46
+ minSize: 1024, // 最小 1KB
47
+ accept: ['image/*', 'video/*'], // 仅图片和视频
48
+ allowEmpty: false // 不允许空文件
49
+ }));
50
+ ```
51
+
52
+ ---
53
+
54
+ ## ⚙️ 配置选项
55
+
56
+ | 参数 | 类型 | 默认值 | 说明 |
57
+ |------|------|--------|------|
58
+ | `maxSize` | `number` | `Infinity` | 最大文件大小(字节) |
59
+ | `minSize` | `number` | `0` | 最小文件大小(字节) |
60
+ | `accept` | `string[]` | `[]` | 允许的文件类型(MIME 类型或扩展名) |
61
+ | `allowEmpty` | `boolean` | `false` | 是否允许空文件 |
62
+ | `customValidate` | `(file: File, error: typeof FileUDError) => boolean \| Promise<boolean>` | - | 自定义验证函数 |
63
+ | `messages` | `object` | `{}` | 自定义错误消息 |
64
+
65
+ ### messages 配置项
66
+
67
+ | 键名 | 函数签名 | 默认消息 |
68
+ |------|---------|---------|
69
+ | `maxSize` | `(max: number, current: number) => string` | "文件 {fileName} 太大,最大 {maxSize}" |
70
+ | `minSize` | `(min: number, current: number) => string` | "文件 {fileName} 太小,最小 {minSize}" |
71
+ | `accept` | `(accept: string[], fileName: string) => string` | "文件 {fileName} 类型不支持" |
72
+ | `empty` | `() => string` | "文件 {fileName} 是空文件" |
73
+ | `custom` | `(fileName: string) => string` | "文件 {fileName} 验证失败" |
74
+
75
+ ---
76
+
77
+ ## 📊 使用示例
78
+
79
+ ### 示例 1:图片上传验证
80
+
81
+ ```typescript
82
+ uploader.use(new FileValidatorPlugin({
83
+ maxSize: 5 * 1024 * 1024, // 最大 5MB
84
+ accept: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
85
+ allowEmpty: false,
86
+ messages: {
87
+ maxSize: (max, current) =>
88
+ `图片不能超过 ${formatFileSize(max)},当前 ${formatFileSize(current)}`,
89
+ accept: (accept, fileName) =>
90
+ `只支持 JPG、PNG、GIF、WebP 格式的图片`
91
+ }
92
+ }));
93
+ ```
94
+
95
+ ### 示例 2:视频上传验证
96
+
97
+ ```typescript
98
+ uploader.use(new FileValidatorPlugin({
99
+ maxSize: 100 * 1024 * 1024, // 最大 100MB
100
+ minSize: 1024 * 1024, // 最小 1MB
101
+ accept: ['video/mp4', 'video/webm', 'video/ogg'],
102
+ messages: {
103
+ minSize: (min, current) =>
104
+ `视频文件不能小于 ${formatFileSize(min)}`,
105
+ maxSize: (max, current) =>
106
+ `视频文件不能超过 ${formatFileSize(max)}`
107
+ }
108
+ }));
109
+ ```
110
+
111
+ ### 示例 3:文档上传验证
112
+
113
+ ```typescript
114
+ uploader.use(new FileValidatorPlugin({
115
+ maxSize: 20 * 1024 * 1024, // 最大 20MB
116
+ accept: [
117
+ '.pdf',
118
+ '.doc', '.docx',
119
+ '.xls', '.xlsx',
120
+ '.ppt', '.pptx'
121
+ ],
122
+ messages: {
123
+ accept: () =>
124
+ '只支持 PDF、Word、Excel、PowerPoint 格式的文档'
125
+ }
126
+ }));
127
+ ```
128
+
129
+ ### 示例 4:自定义验证逻辑
130
+
131
+ ```typescript
132
+ uploader.use(new FileValidatorPlugin({
133
+ maxSize: 10 * 1024 * 1024,
134
+ accept: ['image/*'],
135
+
136
+ // 自定义验证:检查图片尺寸
137
+ customValidate: async (file, ErrorClass) => {
138
+ if (!file.type.startsWith('image/')) {
139
+ return true; // 非图片文件跳过此验证
140
+ }
141
+
142
+ return new Promise((resolve) => {
143
+ const img = new Image();
144
+ const url = URL.createObjectURL(file);
145
+
146
+ img.onload = () => {
147
+ URL.revokeObjectURL(url);
148
+
149
+ // 检查图片尺寸
150
+ if (img.width < 800 || img.height < 600) {
151
+ console.warn(`图片尺寸太小: ${img.width}x${img.height}`);
152
+ resolve(false); // 验证失败
153
+ } else {
154
+ resolve(true); // 验证通过
155
+ }
156
+ };
157
+
158
+ img.onerror = () => {
159
+ URL.revokeObjectURL(url);
160
+ resolve(false);
161
+ };
162
+
163
+ img.src = url;
164
+ });
165
+ },
166
+
167
+ messages: {
168
+ custom: (fileName) =>
169
+ `图片 ${fileName} 尺寸不符合要求(最小 800x600)`
170
+ }
171
+ }));
172
+ ```
173
+
174
+ ---
175
+
176
+ ## 💡 高级用法
177
+
178
+ ### 1. 动态验证规则
179
+
180
+ ```typescript
181
+ // 根据用户等级设置不同的文件大小限制
182
+ const getUserMaxSize = (userLevel: number) => {
183
+ switch (userLevel) {
184
+ case 'vip': return 100 * 1024 * 1024; // VIP: 100MB
185
+ case 'premium': return 50 * 1024 * 1024; // 高级: 50MB
186
+ default: return 10 * 1024 * 1024; // 普通: 10MB
187
+ }
188
+ };
189
+
190
+ const userLevel = getCurrentUserLevel();
191
+
192
+ uploader.use(new FileValidatorPlugin({
193
+ maxSize: getUserMaxSize(userLevel),
194
+ accept: ['image/*', 'video/*']
195
+ }));
196
+ ```
197
+
198
+ ### 2. 组合多个验证器
199
+
200
+ ```typescript
201
+ // 为不同类型的文件创建不同的验证规则
202
+ const imageValidator = new FileValidatorPlugin({
203
+ maxSize: 5 * 1024 * 1024,
204
+ accept: ['image/*']
205
+ });
206
+
207
+ const videoValidator = new FileValidatorPlugin({
208
+ maxSize: 100 * 1024 * 1024,
209
+ accept: ['video/*']
210
+ });
211
+
212
+ // 根据文件类型选择验证器
213
+ uploader.onSelect = async (file) => {
214
+ if (file.type.startsWith('image/')) {
215
+ uploader.use(imageValidator);
216
+ } else if (file.type.startsWith('video/')) {
217
+ uploader.use(videoValidator);
218
+ }
219
+ return true;
220
+ };
221
+ ```
222
+
223
+ ### 3. 异步验证(如调用 API)
224
+
225
+ ```typescript
226
+ uploader.use(new FileValidatorPlugin({
227
+ maxSize: 10 * 1024 * 1024,
228
+
229
+ // 异步验证:检查文件是否在黑名单中
230
+ customValidate: async (file) => {
231
+ try {
232
+ const response = await fetch('/api/check-file-hash', {
233
+ method: 'POST',
234
+ headers: { 'Content-Type': 'application/json' },
235
+ body: JSON.stringify({
236
+ hash: await calculateMD5(file),
237
+ fileName: file.name
238
+ })
239
+ });
240
+
241
+ const result = await response.json();
242
+ return !result.isBlocked; // 如果文件被阻止,返回 false
243
+ } catch (error) {
244
+ console.error('文件验证失败:', error);
245
+ return true; // 验证服务异常时,默认通过
246
+ }
247
+ },
248
+
249
+ messages: {
250
+ custom: (fileName) =>
251
+ `文件 ${fileName} 已被禁止上传`
252
+ }
253
+ }));
254
+ ```
255
+
256
+ ### 4. 结合其他插件使用
257
+
258
+ ```typescript
259
+ import {
260
+ FileValidatorPlugin,
261
+ CompressImagePlugin,
262
+ WatermarkPlugin
263
+ } from '@file-ud.js/plugins/uploader';
264
+
265
+ uploader.use([
266
+ // 1. 文件验证(优先级最高,最先执行)
267
+ new FileValidatorPlugin({
268
+ maxSize: 10 * 1024 * 1024,
269
+ accept: ['image/*']
270
+ }),
271
+
272
+ // 2. 添加水印
273
+ new WatermarkPlugin({
274
+ text: "© MyCompany"
275
+ }),
276
+
277
+ // 3. 图片压缩
278
+ new CompressImagePlugin({
279
+ quality: 0.8
280
+ })
281
+ ]);
282
+ ```
283
+
284
+ ---
285
+
286
+ ## 🔍 验证流程
287
+
288
+ ```
289
+ 1. 用户选择文件
290
+
291
+ 2. 插件拦截文件(onFileSelect 钩子,priority: 0)
292
+
293
+ 3. 空文件检查
294
+ ├─ 失败 → 抛出 FILE_EMPTY 错误
295
+ └─ 通过 ↓
296
+
297
+ 4. 最小大小检查
298
+ ├─ 失败 → 抛出 FILE_TOO_SMALL 错误
299
+ └─ 通过 ↓
300
+
301
+ 5. 最大大小检查
302
+ ├─ 失败 → 抛出 FILE_TOO_LARGE 错误
303
+ └─ 通过 ↓
304
+
305
+ 6. 文件类型检查
306
+ ├─ 失败 → 抛出 INVALID_TYPE 错误
307
+ └─ 通过 ↓
308
+
309
+ 7. 自定义验证
310
+ ├─ 失败 → 抛出 FILE_CORRUPTED 错误
311
+ └─ 通过 ↓
312
+
313
+ 8. 验证通过,继续上传流程
314
+ ```
315
+
316
+ ---
317
+
318
+ ## 🎯 最佳实践
319
+
320
+ ### 1. 合理设置文件大小限制
321
+
322
+ | 文件类型 | 推荐最大值 | 说明 |
323
+ |---------|-----------|------|
324
+ | **头像** | 1-2 MB | 小尺寸图片 |
325
+ | **普通照片** | 5-10 MB | 平衡质量和大小 |
326
+ | **高清照片** | 10-20 MB | 保留更多细节 |
327
+ | **短视频** | 50-100 MB | 1-2 分钟视频 |
328
+ | **长视频** | 200-500 MB | 需要分片上传 |
329
+ | **文档** | 10-20 MB | PDF、Word 等 |
330
+ | **压缩包** | 100-500 MB | 需要断点续传 |
331
+
332
+ ### 2. 精确的文件类型限制
333
+
334
+ ```typescript
335
+ // ❌ 不推荐:过于宽松
336
+ new FileValidatorPlugin({
337
+ accept: ['*/*'] // 允许所有文件
338
+ });
339
+
340
+ // ✅ 推荐:明确指定类型
341
+ new FileValidatorPlugin({
342
+ accept: [
343
+ 'image/jpeg',
344
+ 'image/png',
345
+ 'image/gif',
346
+ 'image/webp'
347
+ ]
348
+ });
349
+
350
+ // ✅ 或使用通配符
351
+ new FileValidatorPlugin({
352
+ accept: ['image/*'] // 所有图片类型
353
+ });
354
+ ```
355
+
356
+ ### 3. 友好的错误提示
357
+
358
+ ```typescript
359
+ new FileValidatorPlugin({
360
+ maxSize: 10 * 1024 * 1024,
361
+ accept: ['image/*'],
362
+
363
+ messages: {
364
+ maxSize: (max, current) => {
365
+ const ratio = (current / max).toFixed(1);
366
+ return `文件大小超出限制 ${ratio} 倍,请压缩后重新上传(最大 ${formatFileSize(max)})`;
367
+ },
368
+ accept: (accept, fileName) => {
369
+ const ext = fileName.split('.').pop()?.toUpperCase();
370
+ return `不支持 .${ext} 格式,请转换为 JPG、PNG 或 WebP 格式`;
371
+ }
372
+ }
373
+ });
374
+ ```
375
+
376
+ ### 4. 前端 + 后端双重验证
377
+
378
+ ```typescript
379
+ // 前端验证(用户体验)
380
+ uploader.use(new FileValidatorPlugin({
381
+ maxSize: 10 * 1024 * 1024,
382
+ accept: ['image/*']
383
+ }));
384
+
385
+ // 后端验证(安全性)
386
+ // 在服务器端再次验证文件大小和类型
387
+ // 不要完全信任前端验证!
388
+ ```
389
+
390
+ ---
391
+
392
+ ## 🐛 常见问题
393
+
394
+ ### Q1: 为什么设置了 accept 但还是能选择其他类型文件?
395
+
396
+ **原因**:`accept` 属性只是提示浏览器过滤文件选择器,用户可以手动选择"所有文件"绕过。
397
+
398
+ **解决方案**:
399
+ ```typescript
400
+ // 前端验证只是第一道防线,必须在后端再次验证
401
+ uploader.use(new FileValidatorPlugin({
402
+ accept: ['image/*']
403
+ }));
404
+
405
+ // 后端验证(Node.js 示例)
406
+ app.post('/api/upload', (req, res) => {
407
+ const file = req.files.file;
408
+
409
+ // 再次检查 MIME 类型
410
+ if (!file.mimetype.startsWith('image/')) {
411
+ return res.status(400).json({ error: '只支持图片文件' });
412
+ }
413
+
414
+ // 继续处理...
415
+ });
416
+ ```
417
+
418
+ ### Q2: 如何获取详细的验证错误信息?
419
+
420
+ ```typescript
421
+ uploader.onError = (error) => {
422
+ console.log('错误码:', error.code);
423
+ console.log('错误消息:', error.message);
424
+ console.log('上下文:', error.context);
425
+
426
+ // 根据错误码显示不同的提示
427
+ switch (error.code) {
428
+ case 'FILE_TOO_LARGE':
429
+ showToast('文件太大,请压缩后上传');
430
+ break;
431
+ case 'INVALID_TYPE':
432
+ showToast('文件格式不支持');
433
+ break;
434
+ default:
435
+ showToast(error.message);
436
+ }
437
+ };
438
+ ```
439
+
440
+ ### Q3: 自定义验证函数如何抛出错误?
441
+
442
+ ```typescript
443
+ uploader.use(new FileValidatorPlugin({
444
+ customValidate: async (file, ErrorClass) => {
445
+ // 方式1:返回 false(使用默认错误消息)
446
+ if (file.size === 0) {
447
+ return false;
448
+ }
449
+
450
+ // 方式2:抛出自定义错误(推荐)
451
+ if (isCorrupted(file)) {
452
+ throw ErrorClass.FILE_CORRUPTED
453
+ .setMessage('文件已损坏,请重新选择')
454
+ .setContext({ fileName: file.name });
455
+ }
456
+
457
+ return true;
458
+ }
459
+ }));
460
+ ```
461
+
462
+ ### Q4: 验证会影响性能吗?
463
+
464
+ **影响很小**:
465
+ - 大小和类型验证:O(1),几乎无开销
466
+ - 自定义验证:取决于你的实现
467
+ - 同步验证:可能阻塞主线程
468
+ - 异步验证:不会阻塞
469
+
470
+ **优化建议**:
471
+ ```typescript
472
+ // ✅ 推荐:异步验证
473
+ customValidate: async (file) => {
474
+ // 耗时操作放在异步函数中
475
+ return await someAsyncCheck(file);
476
+ }
477
+
478
+ // ❌ 不推荐:同步验证中的耗时操作
479
+ customValidate: (file) => {
480
+ // 这会阻塞主线程
481
+ expensiveSyncOperation(file);
482
+ return true;
483
+ }
484
+ ```
485
+
486
+ ---
487
+
488
+ ## 📈 安全建议
489
+
490
+ ### 1. 不要信任前端验证
491
+
492
+ ```typescript
493
+ // 前端验证只是为了提升用户体验
494
+ uploader.use(new FileValidatorPlugin({
495
+ maxSize: 10 * 1024 * 1024
496
+ }));
497
+
498
+ // 必须在后端再次验证!
499
+ // 攻击者可以绕过前端验证
500
+ ```
501
+
502
+ ### 2. 检查文件魔数(Magic Number)
503
+
504
+ ```typescript
505
+ // 高级验证:检查文件真实类型
506
+ uploader.use(new FileValidatorPlugin({
507
+ customValidate: async (file) => {
508
+ const buffer = await readFileHeader(file, 4);
509
+ const magicNumber = buffer.toString('hex');
510
+
511
+ // JPEG: FFD8FF
512
+ // PNG: 89504E47
513
+ // GIF: 47494638
514
+
515
+ if (file.type === 'image/jpeg' && !magicNumber.startsWith('ffd8ff')) {
516
+ return false; // 伪造的 JPEG 文件
517
+ }
518
+
519
+ return true;
520
+ }
521
+ }));
522
+ ```
523
+
524
+ ### 3. 限制文件数量
525
+
526
+ ```typescript
527
+ // 虽然 FileValidatorPlugin 不直接支持数量限制
528
+ // 但可以在 onSelect 回调中实现
529
+ let fileCount = 0;
530
+ const MAX_FILES = 10;
531
+
532
+ uploader.onSelect = (file) => {
533
+ if (fileCount >= MAX_FILES) {
534
+ alert(`最多只能上传 ${MAX_FILES} 个文件`);
535
+ return false;
536
+ }
537
+ fileCount++;
538
+ return true;
539
+ };
540
+ ```
541
+
542
+ ---
543
+
544
+ ## 📝 更新日志
545
+
546
+ ### v1.0.0 (2024-01-XX)
547
+ - ✨ 首次发布
548
+ - ✅ 支持大小验证(最小/最大)
549
+ - ✅ 支持类型验证
550
+ - ✅ 支持空文件检测
551
+ - ✅ 支持自定义验证函数
552
+ - ✅ 支持错误消息定制
553
+ - ✅ 最高优先级执行
554
+
555
+ ---
556
+
557
+ ## 🤝 贡献指南
558
+
559
+ 欢迎提交 Issue 和 Pull Request!
560
+
561
+ **报告 Bug**:[GitHub Issues](https://github.com/ChYuanJinlin/file-ud.js/issues)
562
+
563
+ **提出建议**:[Feature Requests](https://github.com/ChYuanJinlin/file-ud.js/discussions)