关于 file-upload
用于在 Node.js、Python 和 Java 服务中处理文件上传的开发参考技能,涵盖存储方式、验证、上传限制及生产环境防护。
主要功能
- 01Multer 磁盘与内存存储示例
- 02FastAPI 和 Spring Boot 上传模式
- 03使用文件魔数验证类型
- 04上传大小限制与安全文件名
- 05上传端点生产检查清单
适用场景
- 01在 Node.js 服务中实现文件上传
- 02超越 MIME 标记验证文件内容
- 03为用户上传端点设置防护措施
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ claude-dev-suite/ claude-dev-suite --skill file-upload安装后在 Claude Code 中输入/file-upload确认已加载。
详细介绍
文件上传处理
Node.js(Multer — 推荐)
import multer from 'multer';
import path from 'path';
import crypto from 'crypto';
// Storage configuration
const storage = multer.diskStorage({
destination: './uploads',
filename: (req, file, cb) => {
const uniqueName = `${crypto.randomUUID()}${path.extname(file.originalname)}`;
cb(null, uniqueName);
},
});
// File filter
const fileFilter = (req: Express.Request, file: Express.Multer.File, cb: multer.FileFilterCallback) => {
const allowedMimes = ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'];
if (allowedMimes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error(`File type ${file.mimetype} not allowed`));
}
};
const upload = multer({
storage,
fileFilter,
limits: { fileSize: 10 * 1024 * 1024 }, // 10MB
});
// Single file
app.post('/upload', upload.single('file'), (req, res) => {
res.json({ filename: req.file!.filename, size: req.file!.size });
});
// Multiple files
app.post('/upload/multiple', upload.array('files', 10), (req, res) => {
res.json({ count: (req.files as Express.Multer.File[]).length });
});
// Error handling
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
if (err instanceof multer.MulterError) {
if (err.code === 'LIMIT_FILE_SIZE') return res.status(413).json({ error: 'File too large' });
return res.status(400).json({ error: err.message });
}
if (err.message.includes('not allowed')) return res.status(415).json({ error: err.message });
next(err);
});
内存存储(用于转发到云端)
const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 10 * 1024 * 1024 } });
app.post('/upload', upload.single('file'), async (req, res) => {
// req.file.buffer contains the file — forward to S3
await s3.send(new PutObjectCommand({
Bucket: bucket, Key: key, Body: req.file!.buffer, ContentType: req.file!.mimetype,
}));
});
Python(FastAPI)
from fastapi import UploadFile, File, HTTPException
import aiofiles, uuid
ALLOWED_TYPES = {"image/jpeg", "image/png", "application/pdf"}
MAX_SIZE = 10 * 1024 * 1024 # 10MB
@app.post("/upload")
async def upload_file(file: UploadFile = File(...)):
if file.content_type not in ALLOWED_TYPES:
raise HTTPException(415, f"Type {file.content_type} not allowed")
content = await file.read()
if len(content) > MAX_SIZE:
raise HTTPException(413, "File too large")
filename = f"{uuid.uuid4()}{Path(file.filename).suffix}"
async with aiofiles.open(f"uploads/{filename}", "wb") as f:
await f.write(content)
return {"filename": filename, "size": len(content)}
Java(Spring Boot)
@PostMapping("/upload")
public ResponseEntity<Map<String, String>> upload(
@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) throw new ResponseStatusException(BAD_REQUEST, "Empty file");
if (file.getSize() > 10_000_000) throw new ResponseStatusException(PAYLOAD_TOO_LARGE);
String ext = StringUtils.getFilenameExtension(file.getOriginalFilename());
String filename = UUID.randomUUID() + "." + ext;
Path dest = Path.of("uploads", filename);
file.transferTo(dest);
return ResponseEntity.ok(Map.of("filename", filename));
}
Spring 配置:
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
MIME 类型之外的文件验证
// Validate actual file content (magic bytes), not just extension
import { fileTypeFromBuffer } from 'file-type';
const type = await fileTypeFromBuffer(req.file!.buffer);
if (!type || !['image/jpeg', 'image/png'].includes(type.mime)) {
return res.status(415).json({ error: 'Invalid file content' });
}
反模式
| 反模式 | 修复方案 |
|---|---|
| 只信任客户端提供的 MIME 类型 | 使用 file-type 验证魔数 |
| 将原始文件名用作存储键 | 使用 UUID,防止路径遍历和冲突 |
| 不限制文件大小 | 始终设置 limits.fileSize |
| 上传时同步写入磁盘 | 使用流或异步写入 |
| 将上传文件存储在应用目录中 | 使用单独的 /uploads 目录或云存储 |
| 不清理临时文件 | 实现生命周期清理或定时任务清理 |
生产环境检查清单
- 已配置文件大小限制
- MIME 类型白名单(验证魔数,而不仅是扩展名)
- 使用 UUID 作为文件名(存储时绝不使用原始文件名)
- Multer 错误处理中间件
- 对用户上传的文件进行病毒扫描(ClamAV)
- 对上传端点进行速率限制
- 孤立文件清理策略
评论
支持 Markdown