Node.js Multer 文件上传与进度追踪实现
Multer 文件处理中间件
Multer 是专为 Node.js 设计的文件上传处理中间件,能够有效处理 multipart/form-data 格式的表单数据,特别适用于文件上传场景。该中间件提供了文件接收、保存、大小限制和类型验证等功能。
Multer 基础配置流程
- 安装依赖:通过 npm 包管理器安装 multer 模块
npm install multer
- 配置存储选项:
// 设置文件存储引擎
const fileStorage = multer.diskStorage({
// 文件存储目录
destination: function (request, file, callback) {
callback(null, process.cwd() + '/assets/uploads')
},
// 自定义文件名
filename: function (request, file, callback) {
// 提取文件名和扩展名
const originalName = path.parse(file.originalname).name
const fileExtension = path.parse(file.originalname).ext
// 生成唯一文件名
const uniqueName = originalName + Date.now() + '-' + Math.floor(Math.random() * 9000 + 1000) + fileExtension
callback(null, uniqueName)
}
})
// 导出 multer 实例
module.exports.fileUpload = multer({
storage: fileStorage,
limits: {
fileSize: 2000000, // 限制文件大小为 2MB
files: 1 // 限制同时上传的文件数量
}
})
- 路由处理:
// 处理图片上传请求
router.post('/upload', function(request, response, next) {
// single 方法中的参数对应表单中文件输入域的 name 属性
fileUpload.single('document')(request, response, function(error) {
if (error instanceof multer.MulterError) {
next(new Error('文件上传失败,请确保文件大小不超过 2MB'))
} else {
response.json({
code: 0,
message: '',
data: '/assets/uploads/' + request.file.filename
})
}
})
})
通过 request.file 可以访问上传文件的详细信息。
前端上传进度实现
HTML 结构
<input type="file" id="fileSelector" />
<progress id="progressIndicator" value="0" max="100"></progress>
JavaScript 实现
// 文件选择事件监听
document.getElementById('fileSelector').addEventListener('change', function(event) {
const selectedFile = event.target.files[0];
if (selectedFile) {
handleFileUpload(selectedFile);
}
});
// 文件上传处理函数
function handleFileUpload(file) {
const xhr = new XMLHttpRequest();
const progressBar = document.getElementById('progressIndicator');
// 监听上传进度
xhr.upload.addEventListener('progress', function(event) {
if (event.lengthComputable) {
const percentage = (event.loaded / event.total) * 100;
progressBar.value = percentage;
}
});
// 上传完成回调
xhr.addEventListener('load', function() {
if (xhr.status === 200) {
showNotification('文件上传成功!', 'success');
} else {
showNotification('文件上传失败!', 'error');
}
});
// 上传错误处理
xhr.addEventListener('error', function() {
showNotification('上传过程中发生错误!', 'error');
});
// 初始化请求
xhr.open('POST', '/api/upload', true);
// 创建表单数据对象
const formData = new FormData();
formData.append('document', file);
// 发送请求
xhr.send(formData);
}
// 通知提示函数
function showNotification(message, type) {
const notification = document.createElement('div');
notification.className = `notification ${type}`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
}, 3000);
}
使用 Fetch API 替代方案
async function uploadFileWithFetch(file) {
const progressBar = document.getElementById('progressIndicator');
try {
const response = await fetch('/api/upload', {
method: 'POST',
body: file,
// 使用自定义跟踪上传进度
onUploadProgress: (progressEvent) => {
const percent = Math.round((progressEvent.loaded / progressEvent.total) * 100);
progressBar.value = percent;
}
});
if (response.ok) {
const result = await response.json();
console.log('上传成功:', result);
} else {
throw new Error('上传失败');
}
} catch (error) {
console.error('上传错误:', error);
}
}