Node.js 实现的轻量级文件管理接口服务,提供完整的文件和目录管理功能,支持 JWT 认证、Swagger API 文档和完善的日志系统。
本项目已纳入根目录的 pnpm workspace,推荐在根目录统一管理:
# 根目录安装所有依赖
pnpm install
# 启动文件服务(在根目录执行)
pnpm --filter admin-file start
cd admin-file
# 安装依赖
pnpm install
# 启动服务
pnpm start
创建 .env 文件:
PORT=4000
JWT_SECRET=your-secret-key
启动服务后,访问 Swagger 文档:
http://localhost:4000/api-docs
POST /upload/avatar - 上传头像(支持 jpg/png/gif/webp/avif/svg,≤5MB)POST /upload - 上传文件(支持所有类型,≤1GB)DELETE /delete - 删除文件或目录(删图片时会连带删除同目录的 -320w/-800w/-1600w 缩略图,响应里返回 thumbnailCount)GET /files - 获取文件列表GET /file - 获取文件详情POST /directory - 创建命名空间目录GET /{category}/{filename} - 访问文件(如 /images/photo.jpg)GET /{namespace}/{filename} - 访问命名空间文件?download=1 参数强制下载| 接口 | 说明 |
|---|---|
GET /media/meta?path=blog/essay/foo.mp4 |
视频状态:ready / pending / processing / failed / none,以及 HLS、海报、原片、下载地址 |
GET /media/poster.jpg?path=... |
视频封面 JPEG(未生成时 404) |
GET /media/thumb?path=...&w=320\|800\|1600 |
图片缩略图,302 到带版本号的 WebP;按需生成,失败/原图较小回退原图 |
POST /media/transcode |
手动触发视频转码(需要认证,body: { "path": "...", "force": false }) |
视频原片永远保留在 uploads 原路径,HLS 只是派生资源:
uploads/blog/essay/foo.mp4 ← 原片(管理端可下载)
uploads/.video/blog/essay/foo-<版本hash>/ ← 派生目录
master.m3u8
v0/playlist.m3u8 + seg_00000.ts ... ← 720p
v1/playlist.m3u8 + seg_00000.ts ... ← 1080p(源片足够大时)
poster.jpg
meta.json
immutable 长缓存。nice 降优先级,适配 2C/2G。ffmpeg / ffprobe 缺失时自动降级:不生成 HLS,原片仍可播放/下载,不影响上传。hanphone.top 的请求受 Cloudflare 单请求大小限制(Free/Pro 100 MB,Business 200 MB),
超过 100 MB 的视频会在到达 admin-file 前被拒;大视频请用 R2 直传、直连源站或分片上传。# 历史视频回填(原片保留,只补 HLS/海报)
cd ~/server_file
node scripts/transcode-videos.js --env /home/hanphone/server_file/.env --dry-run
node scripts/transcode-videos.js --env /home/hanphone/server_file/.env
Cache-Control: public, max-age=31536000, immutablepublic, max-age=604800, stale-while-revalidate=86400public, max-age=31536000, immutable生产建议让 nginx 直接读 uploads/ 与 uploads/.video/(sendfile),只把 miss 和 API 回源到 Node;
示例见 nginx/hanphone.top.conf.example。
Cloudflare 默认只按扩展名缓存,
.m3u8/.ts不在默认列表里。必须给/hls/*加一条 Cache Rule (Cache Eligibility: Eligible,Edge TTL 1 年),否则每个 HLS 分片都会回源到 3Mbps 的小机器。 具体步骤见docs/media-delivery.md。
更多接口详情,请查看 Swagger 文档。
admin-file/
├── uploads/ # 上传文件存储目录
│ ├── temp/ # 临时上传目录
│ ├── .video/ # 视频 HLS/海报派生目录(由转码任务生成)
│ └── blog/avatars/ # 头像存储目录
├── lib/
│ ├── thumbnail.js # 图片缩略图(sharp)
│ ├── video.js # 视频 HLS 转码队列 / ffmpeg / meta
│ └── paths.js # 路径安全与 URL 编码
├── nginx/ # nginx 示例配置(静态直出 + HLS CORS)
├── scripts/ # 缩略图/视频回填脚本
├── logs/ # 日志文件目录
├── server.js # 主服务器文件
├── token.js # JWT 认证处理
├── logger.js # 日志系统
├── package.json # 项目配置
└── .env # 环境变量配置
完整列表见 .env.example。关键项:
| 变量 | 默认 | 说明 |
|---|---|---|
VIDEO_TRANSCODE_ENABLED |
true |
关闭后原片仍可上传/播放/下载,只是不生成 HLS |
VIDEO_HLS_MAX_HEIGHT |
1080 |
最高档位(短边);720 可显著省磁盘/CPU |
VIDEO_HLS_SEGMENT_SECONDS |
4 |
HLS 分片长度 |
VIDEO_TRANSCODE_CONCURRENCY |
1 |
同时转码任务数 |
VIDEO_FFMPEG_THREADS |
2 |
ffmpeg 线程数 |
VIDEO_TRANSCODE_NICE |
10 |
ffmpeg 的 nice 优先级 |
VIDEO_TRANSCODE_TIMEOUT_MS |
21600000 |
单任务超时(6 小时) |
PUBLIC_FILE_BASE_URL |
https://hanphone.top |
返回给前端的绝对文件域名 |
?download=1,拿到的始终是原格式原片。