文档
Express 文件上传 + 静态文件服务
目标
使用 Multer 处理单文件/多文件上传,同时提供静态文件访问,是毕设中「头像上传」「文件提交」的标配方案。
完整代码
// app.js
const express = require('express');
const multer = require('multer');
const path = require('path');
const crypto = require('crypto');
const app = express();
// ───── 配置 Multer ─────
const storage = multer.diskStorage({
destination: (req, file, cb) => cb(null, 'uploads/'),
filename: (req, file, cb) => {
const ext = path.extname(file.originalname);
const name = crypto.randomBytes(16).toString('hex');
cb(null, `${name}${ext}`);
},
});
const fileFilter = (req, file, cb) => {
const allowed = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'];
if (allowed.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error('仅支持 JPG/PNG/GIF/PDF 文件'), false);
}
};
const upload = multer({ storage, fileFilter, limits: { fileSize: 5 * 1024 * 1024 } });
// ───── 静态文件服务 ─────
app.use('/uploads', express.static('uploads'));
app.use(express.static('public'));
// ───── API 路由 ─────
// 单文件上传(头像)
app.post('/api/upload/avatar', upload.single('avatar'), (req, res) => {
if (!req.file) return res.status(400).json({ error: '请选择文件' });
res.json({
message: '上传成功',
url: `/uploads/${req.file.filename}`,
originalName: req.file.originalname,
size: req.file.size,
});
});
// 多文件上传(论文附件)
app.post('/api/upload/attachments', upload.array('files', 10), (req, res) => {
if (!req.files || req.files.length === 0) {
return res.status(400).json({ error: '请选择文件' });
}
const uploaded = req.files.map(f => ({
url: `/uploads/${f.filename}`,
name: f.originalname,
size: f.size,
}));
res.json({ message: `成功上传 ${uploaded.length} 个文件`, files: uploaded });
});
// 错误处理
app.use((err, req, res, next) => {
if (err instanceof multer.MulterError) {
if (err.code === 'LIMIT_FILE_SIZE') {
return res.status(400).json({ error: '文件大小不能超过 5MB' });
}
return res.status(400).json({ error: err.message });
}
res.status(500).json({ error: err.message });
});
app.listen(3000, () => console.log('📁 http://localhost:3000'));
配套 HTML 测试页面
<!-- public/index.html -->
<h2>头像上传</h2>
<form id="avatarForm">
<input type="file" name="avatar" accept="image/*">
<button type="submit">上传头像</button>
</form>
<img id="preview" src="" style="max-width:200px;display:none;">
<h2>附件上传(多选)</h2>
<form id="filesForm">
<input type="file" name="files" multiple>
<button type="submit">上传附件</button>
</form>
<div id="fileList"></div>
<script>
document.getElementById('avatarForm').addEventListener('submit', async (e) => {
e.preventDefault();
const fd = new FormData(e.target);
const res = await fetch('/api/upload/avatar', { method: 'POST', body: fd });
const data = await res.json();
document.getElementById('preview').src = data.url;
document.getElementById('preview').style.display = 'block';
});
document.getElementById('filesForm').addEventListener('submit', async (e) => {
e.preventDefault();
const fd = new FormData(e.target);
const res = await fetch('/api/upload/attachments', { method: 'POST', body: fd });
const data = await res.json();
document.getElementById('fileList').innerHTML = data.files
.map(f => `<p><a href="${f.url}">${f.name}</a> (${(f.size/1024).toFixed(1)}KB)</p>`)
.join('');
});
</script>
运行
mkdir uploads public
npm install express multer
node app.js