model_train_dm/MinIO文件存储接口文档.md

710 lines
15 KiB
Markdown
Raw Permalink Normal View History

2026-07-27 17:51:49 +08:00
# MinIO 文件存储接口文档
## 一、概述
本项目已集成 MinIO 对象存储,并在 Django 后端提供统一的文件存储 API。
### MinIO 服务信息
| 项目 | 值 |
|------|-----|
| API 地址 | `http://127.0.0.1:9000` |
| Web 控制台 | `http://127.0.0.1:9001` |
| 用户名 | `admin` |
| 密码 | `zhengsl2026` |
| 默认 Bucket | `ai-trainprediction` |
### Django 配置
```python
MINIO_ENDPOINT = os.environ.get("MINIO_ENDPOINT", "127.0.0.1:9000")
MINIO_ACCESS_KEY = os.environ.get("MINIO_ACCESS_KEY", "admin")
MINIO_SECRET_KEY = os.environ.get("MINIO_SECRET_KEY", "zhengsl2026")
MINIO_SECURE = _env_bool("MINIO_SECURE", default=False)
MINIO_DEFAULT_BUCKET = os.environ.get("MINIO_DEFAULT_BUCKET", "ai-trainprediction")
MINIO_PRESIGN_EXPIRES = int(os.environ.get("MINIO_PRESIGN_EXPIRES", "3600"))
```
### 认证要求
所有 MinIO 接口均要求 JWT 认证:
```http
Authorization: Bearer {access_token}
```
### 接口前缀
```text
/server/minio/
```
---
## 二、接口总览
| 方法 | 路径 | 功能 |
|------|------|------|
| POST | `/server/minio/bucket/create/` | 创建 Bucket |
| GET | `/server/minio/bucket/list/` | 列出 Bucket |
| DELETE | `/server/minio/bucket/delete/` | 删除 Bucket |
| POST | `/server/minio/file/upload/` | 单文件上传 |
| POST | `/server/minio/files/upload/` | 批量文件上传 |
| POST | `/server/minio/file/upload-zip/` | ZIP 解压后批量入库 |
| POST | `/server/minio/file/upload-video-frames/` | 视频抽帧后图片入库 |
| POST | `/server/minio/file/upload-base64/` | Base64 文件上传 |
| GET | `/server/minio/file/download/` | 流式下载文件 |
| GET | `/server/minio/file/presign/` | 获取预签名下载 URL |
| GET | `/server/minio/file/presign-upload/` | 获取预签名上传 URL |
| GET | `/server/minio/file/preview/` | 流式预览文件 |
| GET | `/server/minio/file/info/` | 获取文件元信息 |
| GET | `/server/minio/files/` | 列出对象 |
| DELETE | `/server/minio/file/delete/` | 删除单个文件 |
| DELETE | `/server/minio/files/delete/` | 批量删除文件 |
| POST | `/server/minio/file/copy/` | 复制文件 |
---
## 三、Bucket 管理
### 3.1 创建 Bucket
```http
POST /server/minio/bucket/create/
Content-Type: application/json
```
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `bucket_name` | string | 否 | Bucket 名称,默认 `ai-trainprediction` |
成功响应:
```json
{
"code": 200,
"data": {
"bucket_name": "my-bucket",
"created": true
},
"msg": "Bucket 创建成功"
}
```
说明:
- 若 Bucket 已存在,返回 `created: false`
- MinIO 返回 `BucketAlreadyExists` / `BucketAlreadyOwnedByYou` 时会转换为 `409`
### 3.2 列出 Bucket
```http
GET /server/minio/bucket/list/
```
成功响应:
```json
{
"code": 200,
"data": ["ai-trainprediction", "my-bucket"],
"msg": "获取成功"
}
```
### 3.3 删除 Bucket
```http
DELETE /server/minio/bucket/delete/
Content-Type: application/json
```
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `bucket_name` | string | 是 | Bucket 名称 |
成功响应:
```json
{
"code": 200,
"data": {
"bucket_name": "my-bucket"
},
"msg": "Bucket 已删除"
}
```
错误说明:
- `404`Bucket 不存在
- `409`Bucket 非空,无法删除
---
## 四、文件上传
### 4.1 单文件上传
```http
POST /server/minio/file/upload/
Content-Type: multipart/form-data
```
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `file` | File | 是 | 上传文件 |
| `bucket_name` | string | 否 | 目标 Bucket默认 `ai-trainprediction` |
| `prefix` | string | 否 | 对象前缀,如 `images/datasets` |
| `object_name` | string | 否 | 指定对象名;不传则自动生成 |
自动命名规则:
```text
{prefix}/YYYY-MM-DD/{uuid12}_{original_filename}
```
成功响应:
```json
{
"code": 200,
"data": {
"bucket_name": "ai-trainprediction",
"object_name": "images/2026-07-01/a1b2c3d4e5f6_photo.jpg",
"original_name": "photo.jpg",
"size": 102400,
"content_type": "image/jpeg",
"presigned_url": "http://127.0.0.1:9000/..."
},
"msg": "上传成功"
}
```
说明:
- 该接口已改为流式上传,不会把整文件一次性读入内存
-`object_name` 明确指定且对象已存在,则会被覆盖
### 4.2 批量文件上传
```http
POST /server/minio/files/upload/
Content-Type: multipart/form-data
```
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `files` | File[] | 是 | 多个文件 |
| `bucket_name` | string | 否 | 目标 Bucket |
| `prefix` | string | 否 | 对象前缀 |
成功响应:
```json
{
"code": 200,
"data": [
{
"object_name": "images/2026-07-01/a1b2_photo1.jpg",
"original_name": "photo1.jpg",
"size": 102400,
"content_type": "image/jpeg",
"presigned_url": "http://127.0.0.1:9000/..."
}
],
"msg": "全部上传成功,共 1 个文件"
}
```
### 4.3 Base64 文件上传
```http
POST /server/minio/file/upload-base64/
Content-Type: application/json
```
请求体:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `file_base64` | string | 是 | Base64 编码内容 |
| `filename` | string | 是 | 原始文件名 |
| `bucket_name` | string | 否 | 目标 Bucket |
| `prefix` | string | 否 | 对象前缀 |
请求示例:
```json
{
"file_base64": "/9j/4AAQSkZJRgABAQ...",
"filename": "avatar.png",
"bucket_name": "ai-trainprediction",
"prefix": "avatars"
}
```
成功响应:
```json
{
"code": 200,
"data": {
"bucket_name": "ai-trainprediction",
"object_name": "avatars/2026-07-01/a1b2c3d4e5f6_avatar.png",
"original_name": "avatar.png",
"size": 51200,
"content_type": "image/png",
"presigned_url": "http://127.0.0.1:9000/..."
},
"msg": "上传成功"
}
```
说明:
- 此接口当前只支持 `application/json`
- Base64 解码失败时返回 `400`
### 4.4 ZIP 解压批量入库
```http
POST /server/minio/file/upload-zip/
Content-Type: multipart/form-data
```
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `file` | File | 是 | ZIP 压缩包 |
| `bucket_name` | string | 否 | 目标 Bucket |
| `prefix` | string | 否 | MinIO 对象前缀 |
| `images_only` | bool | 否 | 是否仅入库图片文件,默认 `false` |
说明:
- 接口会自动解压 ZIP 内文件并逐个上传到 MinIO
- 默认保留 ZIP 内相对目录结构
- 会自动过滤目录项、隐藏文件和非法路径片段(如 `..`
- `images_only=true` 时,仅上传图片扩展名文件:`jpg/jpeg/png/bmp/gif/webp/tif/tiff`
成功响应:
```json
{
"code": 200,
"data": {
"bucket_name": "ai-trainprediction",
"zip_name": "dataset.zip",
"uploaded_count": 2,
"uploaded": [
{
"source_name": "images/cat.jpg",
"object_name": "datasets/images/cat.jpg",
"size": 102400,
"content_type": "image/jpeg"
}
],
"skipped": ["docs/readme.txt"]
},
"msg": "ZIP 解压入库成功,共上传 2 个文件"
}
```
### 4.5 视频抽帧图片入库
```http
POST /server/minio/file/upload-video-frames/
Content-Type: multipart/form-data
```
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `file` | File | 是 | 视频文件 |
| `bucket_name` | string | 否 | 目标 Bucket |
| `prefix` | string | 否 | MinIO 对象前缀 |
| `interval_seconds` | int | 否 | 抽帧时间间隔,单位秒,默认 `1` |
| `start_second` | int | 否 | 开始抽帧时间,默认 `0` |
| `end_second` | int | 否 | 结束抽帧时间,不传则到视频末尾 |
| `max_frames` | int | 否 | 最多生成图片数,默认 `100` |
| `image_format` | string | 否 | 输出图片格式,支持 `jpg/jpeg/png`,默认 `jpg` |
说明:
- 当前支持的视频扩展名:`mp4/avi/mov/mkv/wmv/flv/mpeg/mpg/webm`
- 需要服务端安装 `opencv-python`
- 抽出的图片会存入 `{prefix}/{视频名}_frames/` 目录
- 文件名规则:`frame_00001_1200ms.jpg`
成功响应:
```json
{
"code": 200,
"data": {
"bucket_name": "ai-trainprediction",
"video_name": "demo.mp4",
"fps": 25.0,
"duration_seconds": 12.4,
"interval_seconds": 1,
"start_second": 0,
"end_second": 10,
"max_frames": 20,
"extracted_count": 10,
"extracted": [
{
"object_name": "videos/demo_frames/frame_00001_0ms.jpg",
"frame_index": 0,
"time_second": 0.0
}
]
},
"msg": "视频抽帧入库成功,共生成 10 张图片"
}
```
### 4.6 获取预签名上传 URL
```http
GET /server/minio/file/presign-upload/?bucket_name={bucket}&object_name={object}&expires={seconds}
```
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `bucket_name` | string | 否 | 目标 Bucket |
| `object_name` | string | 是 | 目标对象名 |
| `expires` | int | 否 | 有效期秒数,默认 `3600`,最大 `604800` |
成功响应:
```json
{
"code": 200,
"data": {
"presigned_url": "http://127.0.0.1:9000/ai-trainprediction/path/file.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&...",
"expires_in": 3600
},
"msg": "获取成功"
}
```
---
## 五、文件下载与预览
### 5.1 流式下载文件
```http
GET /server/minio/file/download/?bucket_name={bucket}&object_name={object}
```
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `bucket_name` | string | 否 | Bucket 名称 |
| `object_name` | string | 是 | 对象名 |
说明:
- 返回流式响应,适合大文件下载
- `Content-Disposition``attachment`
### 5.2 获取预签名下载 URL
```http
GET /server/minio/file/presign/?bucket_name={bucket}&object_name={object}&expires={seconds}
```
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `bucket_name` | string | 否 | Bucket 名称 |
| `object_name` | string | 是 | 对象名 |
| `expires` | int | 否 | 有效期秒数,默认 `3600`,最大 `604800` |
成功响应:
```json
{
"code": 200,
"data": {
"presigned_url": "http://127.0.0.1:9000/ai-trainprediction/path/file.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&...",
"expires_in": 3600
},
"msg": "获取成功"
}
```
说明:
- `expires <= 0` 时返回 `400`
- 超过 7 天会被自动截断为 `604800`
### 5.3 流式预览文件
```http
GET /server/minio/file/preview/?bucket_name={bucket}&object_name={object}
```
说明:
- 返回流式响应
- `Content-Disposition``inline`
- 适合图片、PDF、文本等浏览器可直接预览的文件
---
## 六、文件管理
### 6.1 获取文件元信息
```http
GET /server/minio/file/info/?bucket_name={bucket}&object_name={object}
```
成功响应:
```json
{
"code": 200,
"data": {
"name": "images/2026-07-01/a1b2_photo.jpg",
"size": 102400,
"etag": "d41d8cd98f00b204e9800998ecf8427e",
"content_type": "image/jpeg",
"last_modified": "2026-07-01T12:00:00+00:00",
"presigned_url": "http://127.0.0.1:9000/..."
},
"msg": "获取成功"
}
```
### 6.2 列出对象
```http
GET /server/minio/files/?bucket_name={bucket}&prefix={prefix}
```
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `bucket_name` | string | 否 | Bucket 名称 |
| `prefix` | string | 否 | 仅列出指定前缀下的对象 |
成功响应:
```json
{
"code": 200,
"data": [
{
"name": "images/2026-07-01/a1b2_photo.jpg",
"size": 102400,
"last_modified": "2026-07-01T12:00:00+00:00",
"etag": "d41d8cd98f00b204e9800998ecf8427e",
"content_type": "image/jpeg",
"is_dir": false
}
],
"msg": "共 1 个对象"
}
```
### 6.3 删除单个文件
```http
DELETE /server/minio/file/delete/
Content-Type: application/json
```
请求体:
```json
{
"bucket_name": "ai-trainprediction",
"object_name": "images/2026-07-01/a1b2_photo.jpg"
}
```
成功响应:
```json
{
"code": 200,
"data": {
"object_name": "images/2026-07-01/a1b2_photo.jpg"
},
"msg": "删除成功"
}
```
### 6.4 批量删除文件
```http
DELETE /server/minio/files/delete/
Content-Type: application/json
```
请求体:
```json
{
"bucket_name": "ai-trainprediction",
"object_names": [
"images/photo1.jpg",
"images/photo2.png"
]
}
```
成功响应:
```json
{
"code": 200,
"data": {
"deleted": 2
},
"msg": "全部删除成功"
}
```
部分成功响应:
```json
{
"code": 207,
"data": {
"deleted": 1,
"errors": ["images/photo2.png"]
},
"msg": "部分删除1 个失败"
}
```
### 6.5 复制文件
```http
POST /server/minio/file/copy/
Content-Type: application/json
```
请求体:
```json
{
"src_bucket": "ai-trainprediction",
"src_object": "images/photo.jpg",
"dst_bucket": "ai-trainprediction",
"dst_object": "backup/photo.jpg"
}
```
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `src_bucket` | string | 否 | 源 Bucket默认 `ai-trainprediction` |
| `src_object` | string | 是 | 源对象名 |
| `dst_bucket` | string | 否 | 目标 Bucket默认 `ai-trainprediction` |
| `dst_object` | string | 是 | 目标对象名 |
成功响应:
```json
{
"code": 200,
"data": {
"object_name": "backup/photo.jpg"
},
"msg": "复制成功"
}
```
说明:
- 若目标 Bucket 不存在,接口会自动创建
---
## 七、错误码说明
| HTTP 状态码 | 含义 |
|-------------|------|
| `200` | 成功 |
| `207` | 批量删除部分成功 |
| `400` | 参数错误、`expires` 不合法、Base64 解码失败等 |
| `401` | 未携带或携带了无效 JWT Token |
| `403` | MinIO 拒绝访问 |
| `404` | Bucket 或对象不存在 |
| `409` | Bucket 已存在、Bucket 非空等冲突 |
| `500` | 其他 MinIO / 服务端错误 |
错误响应格式:
```json
{
"code": 409,
"msg": "Bucket 非空,无法删除",
"detail": "S3 operation failed; code: BucketNotEmpty, ..."
}
```
---
## 八、代码结构
```text
backend/
├── config/settings/base.py
├── config/api_urls.py
└── apps/common/
├── minio_client.py
└── views/minio_storage.py
```
### `minio_client.py`
封装的核心方法包括:
| 方法 | 功能 |
|------|------|
| `bucket_exists()` | 检查 Bucket 是否存在 |
| `create_bucket()` | 创建 Bucket |
| `list_buckets()` | 列出 Bucket |
| `remove_bucket()` | 删除空 Bucket |
| `upload_file()` | 上传本地文件 |
| `upload_bytes()` | 上传字节数据 |
| `upload_stream()` | 流式上传文件对象 |
| `download_file()` | 下载到本地 |
| `download_bytes()` | 下载为 bytes |
| `get_object()` | 获取对象流响应 |
| `get_object_info()` | 获取对象元信息 |
| `object_exists()` | 判断对象是否存在 |
| `list_objects()` | 列出对象 |
| `delete_object()` | 删除单个对象 |
| `delete_objects()` | 批量删除对象 |
| `presign_get()` | 获取预签名下载 URL |
| `presign_upload()` | 获取预签名上传 URL |
| `generate_object_name()` | 生成唯一对象名 |
| `copy_object()` | 复制对象 |
---
## 九、注意事项
1. 所有接口均要求 JWT 认证
2. 下载和预览接口已经改为流式响应,适合较大文件
3. `upload-base64` 当前只支持 `application/json`
4. ZIP 入库默认保留压缩包中的相对目录结构,并会过滤非法路径
5. 视频抽帧依赖 `opencv-python`
6. `expires` 必须大于 0最大有效期为 7 天
7. 指定 `object_name` 时可能覆盖已有对象,请谨慎使用
8. 删除 Bucket 前必须先清空其中所有对象