下载PDF文档

文件存储管理系统 API 技术文档

基于 NestJS + React + MinIO 的全功能文件管理系统 | 版本 2.0.0

最后更新: 2026年1月19日

项目概述

文件存储管理系统是一个功能完整的文件上传、下载、管理和预览解决方案,采用现代化的微服务架构设计,支持文件夹管理、预签名上传、批量操作等功能。

核心特性

  • 预签名上传: 支持AWS S3预签名URL,直接上传到MinIO
  • 文件夹管理: 完整的文件夹创建、重命名、删除和浏览功能
  • 文件预览: 支持图片、PDF、文本等多种格式预览
  • 批量操作: 批量上传、下载、删除和移动文件
  • 权限控制: JWT认证和细粒度的访问控制
  • 存储统计: 实时存储使用情况和配额监控

基础信息

基础URL

http://www.fox360.cn:3000 // 后端API服务器
http://www.fox360.cn:9002 // MinIO代理(前端可访问)

环境架构

组件 地址 说明
前端应用 192.168.39.152:5173 React + Vite 应用
后端API 192.168.39.152:3000 NestJS + TypeORM
Nginx代理 www.fox360.cn:9002 MinIO集群代理
MinIO集群 www.fox360.cn:9000/9001 HTTPS对象存储

认证方式

所有API请求需要在Header中携带JWT Token:

Authorization: Bearer <access_token>

Token获取

POST /api/auth/login

请求体

参数名 类型 必填 说明
username string 用户名
password string 密码

响应示例

{ "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "user": { "id": "user-uuid", "username": "testuser", "email": "test@example.com" } }

文件夹管理接口 New

✅ 新增完整的文件夹管理功能,支持文件夹内上传文件

创建文件夹

POST /api/folders

请求体

参数名 类型 必填 说明
name string 文件夹名称
parentId string | null 父文件夹ID,null表示根目录
description string 描述信息
metadata object 自定义元数据

响应示例

{ "filename": "新建文件夹", "originalName": "新建文件夹", "mimetype": "application/folder", "size": 0, "status": "completed", "parentId": null, "id": "ea887dc5-7643-41e7-9b8c-046ef0a6506b", "createdAt": "2026-01-18T17:36:11.211Z", "updatedAt": "2026-01-18T17:36:11.211Z" }

获取文件夹内容

GET /api/folders/{folderId}/files

查询参数

参数名 类型 必填 说明
page number 页码,默认1
limit number 每页数量,默认20
sort string 排序字段,默认updatedAt
order string 排序方向,默认desc

获取文件夹树

GET /api/folders/tree

重命名文件夹

PUT /api/folders/{folderId}

请求体

{ "name": "新文件夹名" }

删除文件夹

DELETE /api/folders/{folderId}

文件上传接口

支持预签名URL上传和文件夹内上传,前端直接上传到MinIO

生成预签名URL(支持文件夹)

POST /api/presigned/public/generate-upload-url

请求体

参数名 类型 必填 说明
filename string 文件名
contentType string 文件类型
size number 文件大小(字节)
expiresIn number URL过期时间(秒),默认3600
parentId string 父文件夹ID(用于文件夹内上传)
prefix string 存储路径前缀

响应示例

{ "success": true, "data": { "url": "http://www.fox360.cn:9002/smart-upload/uploads/...", "key": "uploads/user-uuid/folders/folder-id/2026/01/19/.../filename.txt", "uploadId": "upload-1768758781612", "expiresIn": 3600, "filename": "filename.txt", "contentType": "text/plain", "parentId": "folder-id", // ✅ 返回父文件夹ID "expiresAt": "2026-01-19T10:30:00.000Z" } }

文件夹内上传URL(专用接口)

POST /api/presigned/folders/{folderId}/upload-url

路径参数

参数名 说明
folderId 文件夹ID

请求体

参数名 类型 必填 说明
filename string 文件名
contentType string 文件类型,默认application/octet-stream
expiresIn number URL过期时间,默认3600秒

完成上传记录

POST /api/presigned/public/complete-upload

请求体

参数名 类型 必填 说明
fileKey string 文件在MinIO中的key
filename string 原始文件名
mimetype string 文件MIME类型
size number 文件大小(字节)
parentId string 父文件夹ID(用于文件夹内上传)
hash string 文件哈希值(用于秒传)
etag string MinIO返回的ETag
metadata object 自定义元数据

文件下载接口

下载单个文件

GET /api/files/{fileId}/download

响应示例

{ "url": "http://www.fox360.cn:9002/smart-upload/...", "cached": false }

批量下载

POST /api/files/batch/download

请求体

{ "fileIds": [ "file-id-1", "file-id-2", "file-id-3" ] }

文件管理接口

获取文件列表

GET /api/files

查询参数

参数名 类型 必填 说明
page number 页码,默认1
limit number 每页数量,默认20
parentId string | null 父文件夹ID,null表示根目录
sort string 排序字段,默认updatedAt
order string 排序方向,默认desc

响应示例

{ "data": [ { "id": "file-id", "filename": "document.pdf", "originalName": "document.pdf", "size": 1024000, "mimetype": "application/pdf", "isFolder": false, "parentId": "folder-id", // 父文件夹ID "url": "http://...", "createdAt": "2026-01-18T17:36:11.211Z", "updatedAt": "2026-01-18T17:36:11.211Z" } ], "pagination": { "page": 1, "limit": 20, "total": 95, "totalPages": 5, "hasNext": true, "hasPrev": false } }

获取文件信息

GET /api/files/{fileId}

重命名文件

PUT /api/files/{fileId}

请求体

{ "filename": "新文件名.txt" }

删除文件

DELETE /api/files/{fileId}

移动文件/文件夹

PUT /api/files/{itemId}/move

查询参数

参数名 类型 必填 说明
parentId string | null 目标文件夹ID,null表示根目录

统计信息接口

存储统计

GET /api/statistics/storage

文件类型分布

GET /api/statistics/storage/type-distribution

存储配额

GET /api/statistics/storage/quota

最近活动

GET /api/statistics/storage/recent-activity

查询参数

参数名 类型 必填 说明
page number 页码,默认1
pageSize number 每页大小,默认5

批量操作接口

批量生成上传URL

POST /api/presigned/batch-generate

请求体

{ "files": [ { "filename": "file1.txt", "contentType": "text/plain" }, { "filename": "file2.jpg", "contentType": "image/jpeg" } ], "expiresIn": 3600, "parentId": "folder-id" // 可选:指定父文件夹 }

批量文件夹内上传URL

POST /api/presigned/folders/{folderId}/batch-generate

批量更新文件

PUT /api/files/batch/update

批量删除

POST /api/files/batch/delete

错误码说明

状态码 说明 常见原因
200 请求成功 正常响应
201 创建成功 资源创建成功
400 请求参数错误 缺少必要参数、参数格式错误
401 未授权 Token无效、过期或未提供
403 禁止访问 权限不足、无权访问该资源
404 资源不存在 文件、文件夹不存在
413 请求实体过大 上传文件超过大小限制
415 不支持的媒体类型 文件类型不被支持
500 服务器内部错误 后端服务异常
502 网关错误 MinIO服务不可达
503 服务不可用 服务维护或过载

使用示例

前端上传示例(JavaScript)

// 1. 获取预签名URL(支持文件夹内上传) async function getUploadUrl(file, parentId = null) { const response = await fetch('/api/presigned/public/generate-upload-url', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': 'your-api-key' // 可选 }, body: JSON.stringify({ filename: file.name, contentType: file.type, size: file.size, parentId: parentId // 指定上传到哪个文件夹 }) }); return await response.json(); } // 2. 上传到MinIO async function uploadToMinIO(presignedUrl, file) { const response = await fetch(presignedUrl, { method: 'PUT', headers: { 'Content-Type': file.type }, body: file }); if (!response.ok) { throw new Error(`上传失败: ${response.status}`); } return response.headers.get('ETag'); } // 3. 完成后端记录 async function completeUpload(fileKey, filename, size, mimetype, parentId, etag) { const response = await fetch('/api/presigned/public/complete-upload', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ fileKey, filename, size, mimetype, parentId, // 记录父文件夹关系 etag }) }); return await response.json(); }

文件夹内上传示例

// 在指定文件夹内上传文件 async function uploadToFolder(file, folderId) { // 使用文件夹专用接口 const response = await fetch(`/api/presigned/folders/${folderId}/upload-url`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer your-token' }, body: JSON.stringify({ filename: file.name, contentType: file.type, expiresIn: 3600 }) }); const result = await response.json(); if (result.success) { // 上传到MinIO... await uploadToMinIO(result.data.url, file); // 完成后端记录(parentId已包含在URL中) await completeUpload( result.data.key, file.name, file.size, file.type, folderId, // 传递文件夹ID result.etag ); } }

系统架构

架构图

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│  前端应用        │    │  Nginx代理      │    │  MinIO集群      │
│  (React+Vite)   │────▶ (www.fox360.cn:  │────▶ (www.fox360.cn:  │
│  192.168.39.152:│    │  9002)          │    │  9000/9001)     │
│  5173           │    │                 │    │                 │
└─────────────────┘    └─────────────────┘    └─────────────────┘
         │                         │                       │
         ▼                         ▼                       ▼
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│  后端API        │    │  数据库         │    │  Redis缓存      │
│  (NestJS)       │◀───▶  (PostgreSQL)   │◀───▶  (缓存服务)     │
│  192.168.39.152:│    │                 │    │                 │
│  3000           │    │                 │    │                 │
└─────────────────┘    └─────────────────┘    └─────────────────┘
                    

文件路径规则

根目录上传

uploads/{userId}/{year}/{month}/{day}/{timestamp}-{random}-{filename}

文件夹内上传

uploads/{userId}/folders/{folderId}/{year}/{month}/{day}/{timestamp}-{random}-{filename}

注意:系统自动处理路径中的双斜杠问题,确保路径正确性。