A comprehensive file storage and processing system similar to S3 pre-signed URL uploads, built with Express.js, MongoDB, and Socket.IO. Features real-time progress updates for video transcoding and media processing.
一个类似 S3 预签名 URL 上传的综合文件存储和处理系统,使用 Express.js、MongoDB 和 Socket.IO 构建。具有视频转码和媒体处理的实时进度更新功能。
- Secure file uploads with temporary URLs / 使用临时 URL 进行安全文件上传
- Local file storage with organized directory structure / 文件以有组织的目录结构本地存储
- Real-time progress updates via WebSocket / 通过 WebSocket 进行实时进度更新
- API key authentication with granular permissions / 具有细粒度权限的 API 密钥认证
- Third-party CMS integration with webhook support / 支持 webhook 的第三方 CMS 集成
使用预签名 URL 进行安全的小文件上传,支持临时 URL 验证。
生成签名 URL:
POST /api/upload/generate-signed-url
Content-Type: application/json
X-API-Key: your-api-key
X-API-Secret: your-api-secret
{
"filename": "image.jpg",
"contentType": "image/jpeg",
"expiresIn": 3600
}响应格式:
{
"uploadUrl": "/api/upload/file/{signed-token}",
"fileId": "generated-file-id",
"expiresIn": 3600,
"headers": {
"Content-Type": "image/jpeg",
"X-File-Name": "image.jpg"
}
}上传文件:
POST /api/upload/file/{signed-token}
Content-Type: multipart/form-data
X-API-Key: your-api-key
X-API-Secret: your-api-secret
file: [binary data]上传成功响应:
{
"id": "file-id",
"originalName": "image.jpg",
"filename": "unique-filename.jpg",
"size": 1024000,
"mimeType": "image/jpeg",
"uploadDate": "2023-01-01T00:00:00.000Z",
"message": "File uploaded successfully"
}支持大文件(>5GB)的分块上传,具有断点续传功能。
初始化切片上传:
POST /api/upload/chunked/init
Content-Type: application/json
X-API-Key: your-api-key
X-API-Secret: your-api-secret
{
"filename": "large-video.mp4",
"fileSize": 5368709120,
"contentType": "video/mp4",
"chunkSize": 5242880
}初始化响应格式:
{
"uploadId": "unique-upload-id",
"chunkSize": 5242880,
"totalChunks": 1024,
"uploadUrl": "/api/upload/chunk/unique-upload-id",
"expiresAt": "2023-01-01T01:00:00.000Z"
}上传文件块:
POST /api/upload/chunk/{uploadId}
Content-Type: multipart/form-data
X-API-Key: your-api-key
X-API-Secret: your-api-secret
chunk: [binary data]
chunkIndex: 0文件块上传响应格式:
{
"uploadId": "unique-upload-id",
"chunkIndex": 0,
"receivedChunks": 1,
"totalChunks": 1024,
"progress": 0,
"status": "uploading"
}完成上传:
POST /api/upload/complete/{uploadId}
Content-Type: application/json
X-API-Key: your-api-key
X-API-Secret: your-api-secret完成上传响应格式:
{
"uploadId": "unique-upload-id",
"status": "completed",
"filename": "unique-filename.mp4",
"path": "/path/to/uploads/unique-filename.mp4",
"size": 5368709120,
"contentType": "video/mp4"
}获取上传状态:
GET /api/upload/chunked/status/{uploadId}
X-API-Key: your-api-key
X-API-Secret: your-api-secret上传状态响应格式:
{
"uploadId": "unique-upload-id",
"filename": "large-video.mp4",
"fileSize": 5368709120,
"chunkSize": 5242880,
"totalChunks": 1024,
"receivedChunks": 512,
"progress": 50,
"status": "uploading",
"createdAt": "2023-01-01T00:00:00.000Z",
"completedAt": "2023-01-01T00:30:00.000Z",
"expiresAt": "2023-01-01T01:00:00.000Z"
}支持多种音频格式的转换和处理。
支持的格式: MP3, AAC, OGG, WAV, FLAC
处理参数:
{
"type": "audio-convert",
"parameters": {
"format": "mp3",
"quality": "high",
"sampleRate": 44100,
"channels": 2,
"bitrate": "192k"
}
}质量设置:
- Low: 低质量,小文件
- Medium: 中等质量
- High: 高质量
- Very High: 超高质量
视频转码、分辨率调整和格式转换。
支持的格式: MP4, WebM, MOV, AVI
处理参数:
{
"type": "video-transcode",
"parameters": {
"width": 1920,
"height": 1080,
"format": "mp4",
"bitrate": "2000k"
}
}HLS 流媒体:
{
"type": "video-hls",
"parameters": {
"segmentDuration": 10,
"resolution": "1920x1080"
}
}图片格式转换、调整大小和质量优化。
支持的格式: JPEG, PNG, WebP
处理参数:
{
"type": "image-convert",
"parameters": {
"format": "webp",
"width": 1920,
"height": 1080,
"quality": 80
}
}针对已上传的压缩包文件进行解压和处理。
支持的格式: ZIP, 7z
创建压缩包处理任务:
POST /api/processing/job
Content-Type: application/json
X-API-Key: your-api-key
X-API-Secret: your-api-secret
{
"fileId": "archive-file-id",
"type": "archive-process",
"parameters": {
"extractImages": true,
"convertToWebp": true,
"quality": 80,
"preserveMetadata": true
},
"webhookUrl": "https://your-cms.com/webhook",
"webhookSecret": "your-webhook-secret"
}处理流程:
- 验证文件是否为支持的压缩包格式
- 安全解压到临时目录(防止路径遍历攻击)
- 验证解压内容(文件数量、大小限制)
- 提取图片文件并转换为 WebP 格式
- 清理临时文件
- 发送处理结果通知
安全特性:
- 文件数量限制: 最多 5000 个文件
- 大小限制: 最大 2GB 解压内容
- 路径遍历防护: 防止恶意路径攻击
- 超时控制: 5 分钟处理超时
- 图片数量限制: 最少 5 张,最多 5000 张图片
切片上传安全特性:
- 会话过期: 上传会话 24 小时后自动过期
- 自动清理: 每小时清理过期会话和临时文件
- 状态验证: 每次操作都会检查会话有效性
- 资源管理: 防止未完成的上传占用磁盘空间
处理参数说明:
extractImages: 是否提取图片文件convertToWebp: 是否转换为 WebP 格式quality: WebP 转换质量 (1-100)preserveMetadata: 是否保留原始元数据
列出文件:
GET /api/upload/files?page=1&limit=10&type=image
X-API-Key: your-api-key
X-API-Secret: your-api-secret下载文件:
GET /api/upload/file/{id}/download
X-API-Key: your-api-key
X-API-Secret: your-api-secret删除文件:
DELETE /api/upload/file/{id}
X-API-Key: your-api-key
X-API-Secret: your-api-secret获取文件信息:
GET /api/upload/file/{id}
X-API-Key: your-api-key
X-API-Secret: your-api-secret创建批量处理任务:
POST /api/processing/batch
Content-Type: application/json
X-API-Key: your-api-key
X-API-Secret: your-api-secret
{
"fileIds": ["file1", "file2", "file3"],
"processingType": "image-convert",
"parameters": {
"format": "webp",
"quality": 80
},
"webhookUrl": "https://your-cms.com/webhook"
}批量任务状态:
GET /api/processing/batch/{batchId}
X-API-Key: your-api-key
X-API-Secret: your-api-secret处理完成通知:
{
"jobId": "job-id",
"status": "completed",
"progress": 100,
"result": {
"outputPath": "/processed/file.mp4",
"size": 1024000,
"format": "mp4",
"duration": 120,
"bitrate": "2000k",
"width": 1920,
"height": 1080
},
"cmsId": "cms-id",
"timestamp": "2023-01-01T00:00:00.000Z"
}处理失败通知:
{
"jobId": "job-id",
"status": "failed",
"progress": 0,
"error": "Error message describing the failure",
"cmsId": "cms-id",
"timestamp": "2023-01-01T00:00:00.000Z"
}压缩包处理完成:
{
"results": [
{
"url": "/api/processed/image1.webp",
"width": 1920,
"height": 1080,
"originalName": "image1.jpg",
"size": 256000
},
{
"url": "/api/processed/image2.webp",
"width": 1280,
"height": 720,
"originalName": "image2.png",
"size": 128000
}
],
"cmsId": "your-cms-id",
"status": "completed",
"totalImages": 2,
"timestamp": "2023-01-01T00:00:00.000Z"
}任务进度更新:
socket.on("job-progress", (data) => {
console.log("Job progress:", data);
// data.status: 'processing', 'completed', 'failed'
// data.progress: 0-100
// data.result: 处理结果
});批量处理进度:
socket.on("batch-progress", (data) => {
console.log("Batch progress:", data);
// data.completedFiles, data.totalFiles
// data.progress: 0-100
});压缩包处理进度:
socket.on("archive-progress", (data) => {
console.log("Archive progress:", data);
// data.progress: 0-100
// data.currentFile: 当前处理的文件
// data.processedFiles, data.totalFiles
});JavaScript 示例:
const socket = io("http://localhost:3000");
// 连接服务器
socket.on("connect", () => {
console.log("Connected to server");
// 订阅任务更新
socket.emit("subscribe-job", "job-id");
// 订阅批量更新
socket.emit("subscribe-batch", "batch-id");
// 订阅压缩包更新
socket.emit("subscribe-archive", "cms-id");
});
// 监听任务进度
socket.on("job-progress", (data) => {
if (data.status === "completed") {
console.log("Processing completed!");
console.log("Output file:", data.result.outputPath);
}
});Python 示例:
import socketio
sio = socketio.Client()
@sio.event
def connect():
print('Connected to server')
sio.emit('subscribe-job', 'job-id')
@sio.on('job-progress')
def on_job_progress(data):
print(f'Job progress: {data}')
if data['status'] == 'completed':
print('Processing completed!')
sio.connect('http://localhost:3000')
sio.wait()- Node.js v14+
- MongoDB v4.0+
- FFmpeg - 视频音频处理
- 7-Zip - 压缩包处理
- 克隆仓库并安装依赖:
git clone <repository-url>
cd cms-back
npm install- 安装系统依赖:
# macOS
brew install p7zip
# Ubuntu/Debian
sudo apt-get install p7zip-full
# CentOS/RHEL
sudo yum install p7zip p7zip-plugins- 配置环境变量:
cp .env.example .env
# 编辑.env文件,配置数据库连接等- 启动服务:
# 开发环境
npm run dev
# 生产环境
npm startPORT=3000
MONGODB_URI=mongodb://localhost:27017/cms-back
JWT_SECRET=your-jwt-secret
UPLOAD_DIR=./uploads
PROCESSED_DIR=./processed
FFMPEG_PATH=/usr/local/bin/ffmpeg
FFPROBE_PATH=/usr/local/bin/ffprobe
7Z_PATH=/usr/local/bin/7z所有 API 请求都需要 API 密钥认证:
X-API-Key: your-api-key
X-API-Secret: your-api-secret上传相关:
POST /api/upload/generate-signed-url- 生成签名 URLPOST /api/upload/file/{signed-token}- 上传文件POST /api/upload/chunked/init- 初始化切片上传POST /api/upload/chunk/{uploadId}- 上传文件块POST /api/upload/complete/{uploadId}- 完成切片上传GET /api/upload/status/{uploadId}- 获取上传状态
文件管理:
GET /api/upload/files- 列出文件GET /api/upload/file/{id}- 获取文件信息GET /api/upload/file/{id}/download- 下载文件DELETE /api/upload/file/{id}- 删除文件
处理任务:
POST /api/processing/job- 创建处理任务GET /api/processing/job/{id}- 获取任务状态GET /api/processing/jobs- 列出任务DELETE /api/processing/job/{id}- 删除任务
批量处理:
POST /api/processing/batch- 创建批量处理任务GET /api/processing/batch/{batchId}- 获取批量任务状态
详细的 API 文档请参考各功能章节的具体示例。
- API 密钥认证 - 基于密钥/密钥对的安全访问
- 请求限流 - 防止 API 滥用
- 文件大小限制 - 可配置的上传限制
- 临时签名 URL - 具有过期时间的安全上传 URL
- 源站验证 - CORS 保护和 origin 验证
- 安全头 - Helmet 安全头配置
API 返回适当的 HTTP 状态码和错误消息:
400Bad Request - 无效的请求参数401Unauthorized - 无效的 API 凭据403Forbidden - 权限不足404Not Found - 资源未找到500Internal Server Error - 服务器错误
// 1. 生成签名URL
const signedResponse = await fetch("/api/upload/generate-signed-url", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": apiKey,
"X-API-Secret": apiSecret,
},
body: JSON.stringify({
filename: "video.mp4",
contentType: "video/mp4",
}),
});
const { uploadUrl } = await signedResponse.json();
// 2. 上传文件
const formData = new FormData();
formData.append("file", file);
await fetch(uploadUrl, { method: "POST", body: formData });
// 3. 创建处理任务
const jobResponse = await fetch("/api/processing/job", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": apiKey,
"X-API-Secret": apiSecret,
},
body: JSON.stringify({
fileId: "file-id",
type: "video-transcode",
parameters: {
width: 1920,
height: 1080,
format: "mp4",
},
webhookUrl: "https://your-cms.com/webhook",
}),
});
const { id: jobId } = await jobResponse.json();
// 4. 通过WebSocket监听进度
const socket = io("http://localhost:3000");
socket.emit("subscribe-job", jobId);
socket.on("job-progress", (data) => {
console.log(`Progress: ${data.progress}%`);
});// 初始化切片上传
const initResponse = await fetch("/api/upload/chunked/init", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": apiKey,
"X-API-Secret": apiSecret,
},
body: JSON.stringify({
filename: "large-video.mp4",
fileSize: 5368709120, // 5GB
contentType: "video/mp4",
chunkSize: 5242880, // 5MB chunks
}),
});
const { uploadId, totalChunks } = await initResponse.json();
// 上传文件块
for (let i = 0; i < totalChunks; i++) {
const chunk = getChunkData(i);
const formData = new FormData();
formData.append("chunk", chunk);
formData.append("chunkIndex", i);
await fetch(`/api/upload/chunked/upload/${uploadId}`, {
method: "POST",
body: formData,
headers: {
"X-API-Key": apiKey,
"X-API-Secret": apiSecret,
},
});
}
// 完成上传
await fetch(`/api/upload/chunked/complete/${uploadId}`, {
method: "POST",
headers: {
"X-API-Key": apiKey,
"X-API-Secret": apiSecret,
},
});欢迎提交问题报告和功能请求。请遵循以下步骤:
- Fork 项目
- 创建功能分支 (
git checkout -b feature/AmazingFeature) - 提交更改 (
git commit -m 'Add some AmazingFeature') - 推送到分支 (
git push origin feature/AmazingFeature) - 打开 Pull Request
本项目采用 MIT 许可证 - 查看 LICENSE 文件了解详情。
如有问题或需要帮助,请:
- 查看 Issues 页面
- 创建新的 Issue 描述问题
- 联系维护团队
Media Processing Server - 一个功能强大的媒体处理和文件管理平台