Skip to content

文件管理

文件管理模块提供文件上传、下载、删除等功能,支持阿里云 OSS 云存储服务。

接口概览

文件上传

方法路径描述权限要求
POST/api/v1/oss/upload上传单个文件登录用户
POST/api/v1/oss/batch-upload批量上传文件登录用户

文件删除

方法路径描述权限要求
DELETE/api/v1/oss/delete删除单个文件登录用户
DELETE/api/v1/oss/batch-delete批量删除文件登录用户

文件信息

方法路径描述权限要求
GET/api/v1/oss/file-info获取文件信息登录用户
GET/api/v1/oss/files获取文件列表登录用户

预签名URL

方法路径描述权限要求
POST/api/v1/oss/presigned-url生成预签名URL登录用户

配置信息

方法路径描述权限要求
GET/api/v1/oss/config获取OSS配置信息登录用户

上传单个文件

上传单个文件到云存储服务。

接口信息

  • URL: /api/v1/oss/upload
  • 方法: POST
  • 内容类型: multipart/form-data
  • 认证: 需要登录

请求参数

字段类型必填描述示例
fileFile要上传的文件-
folderstring文件夹路径"documents/2024/01"
descriptionstring文件描述"项目文档"
is_publicbool是否公开访问false

请求示例

bash
curl -X POST http://localhost:8080/api/v1/oss/upload \
  -H "Authorization: Bearer <your-token>" \
  -F "file=@/path/to/document.pdf" \
  -F "folder=documents/2024/01" \
  -F "description=项目文档" \
  -F "is_public=false"

响应示例

成功响应 (200)

json
{
  "code": 200,
  "message": "上传成功",
  "data": {
    "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "filename": "document.pdf",
    "original_name": "项目需求文档.pdf",
    "mime_type": "application/pdf",
    "size": 2048576,
    "url": "https://your-bucket.oss-cn-hangzhou.aliyuncs.com/documents/2024/01/f47ac10b-58cc-4372-a567-0e02b2c3d479.pdf",
    "download_url": "https://your-bucket.oss-cn-hangzhou.aliyuncs.com/documents/2024/01/f47ac10b-58cc-4372-a567-0e02b2c3d479.pdf?Expires=1705123200&OSSAccessKeyId=LTAI4GCH1vX6DKqJWxd6****&Signature=...",
    "folder": "documents/2024/01",
    "description": "项目文档",
    "is_public": false,
    "uploaded_by": {
      "id": 1,
      "name": "John Doe"
    },
    "created_at": "2024-01-01T12:00:00Z"
  },
  "timestamp": "2024-01-01T12:00:00Z"
}

批量上传文件

同时上传多个文件。

接口信息

  • URL: /api/v1/oss/batch-upload
  • 方法: POST
  • 内容类型: multipart/form-data
  • 认证: 需要登录

请求参数

字段类型必填描述示例
filesFile[]要上传的文件列表-
folderstring文件夹路径"documents/batch"
descriptionstring批量描述"批量上传文档"
is_publicbool是否公开访问false

请求示例

bash
curl -X POST http://localhost:8080/api/v1/oss/batch-upload \
  -H "Authorization: Bearer <your-token>" \
  -F "files=@/path/to/doc1.pdf" \
  -F "files=@/path/to/doc2.pdf" \
  -F "files=@/path/to/image.png" \
  -F "folder=documents/batch" \
  -F "description=批量上传文档"

响应示例

成功响应 (200)

json
{
  "code": 200,
  "message": "批量上传完成",
  "data": {
    "total": 3,
    "success": 3,
    "failed": 0,
    "results": [
      {
        "id": "file1-id",
        "filename": "doc1.pdf",
        "original_name": "需求文档.pdf",
        "size": 1024000,
        "url": "https://your-bucket.oss-cn-hangzhou.aliyuncs.com/documents/batch/doc1.pdf",
        "status": "success"
      },
      {
        "id": "file2-id",
        "filename": "doc2.pdf",
        "original_name": "设计文档.pdf",
        "size": 2048576,
        "url": "https://your-bucket.oss-cn-hangzhou.aliyuncs.com/documents/batch/doc2.pdf",
        "status": "success"
      },
      {
        "id": "file3-id",
        "filename": "image.png",
        "original_name": "界面截图.png",
        "size": 512000,
        "url": "https://your-bucket.oss-cn-hangzhou.aliyuncs.com/documents/batch/image.png",
        "status": "success"
      }
    ]
  },
  "timestamp": "2024-01-01T12:00:00Z"
}

获取文件信息

获取指定文件的详细信息。

接口信息

  • URL: /api/v1/oss/file-info
  • 方法: GET
  • 认证: 需要登录

查询参数

参数类型必填描述示例
file_idstring文件ID"f47ac10b-58cc-4372-a567-0e02b2c3d479"

请求示例

bash
GET /api/v1/oss/file-info?file_id=f47ac10b-58cc-4372-a567-0e02b2c3d479

响应示例

成功响应 (200)

json
{
  "code": 200,
  "message": "获取成功",
  "data": {
    "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "filename": "document.pdf",
    "original_name": "项目需求文档.pdf",
    "mime_type": "application/pdf",
    "size": 2048576,
    "size_formatted": "2.0 MB",
    "url": "https://your-bucket.oss-cn-hangzhou.aliyuncs.com/documents/2024/01/document.pdf",
    "download_url": "https://your-bucket.oss-cn-hangzhou.aliyuncs.com/documents/2024/01/document.pdf?Expires=1705123200&Signature=...",
    "folder": "documents/2024/01",
    "description": "项目文档",
    "is_public": false,
    "uploaded_by": {
      "id": 1,
      "name": "John Doe",
      "avatar": "https://example.com/avatar.jpg"
    },
    "created_at": "2024-01-01T12:00:00Z",
    "last_accessed_at": "2024-01-01T15:30:00Z",
    "download_count": 5
  },
  "timestamp": "2024-01-01T12:00:00Z"
}

获取文件列表

获取文件列表,支持分页和筛选。

接口信息

  • URL: /api/v1/oss/files
  • 方法: GET
  • 认证: 需要登录

查询参数

参数类型必填描述默认值
pageint页码1
page_sizeint每页数量20
folderstring文件夹路径筛选-
searchstring搜索关键词-
mime_typestring文件类型筛选-
uploaded_byint上传者ID筛选-
date_fromstring开始日期-
date_tostring结束日期-
sort_bystring排序字段(name, size, created_at)created_at
sort_orderstring排序方向(asc, desc)desc

请求示例

bash
GET /api/v1/oss/files?page=1&page_size=20&folder=documents&search=项目&sort_by=created_at&sort_order=desc

响应示例

成功响应 (200)

json
{
  "code": 200,
  "message": "获取成功",
  "data": {
    "files": [
      {
        "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
        "filename": "document.pdf",
        "original_name": "项目需求文档.pdf",
        "mime_type": "application/pdf",
        "size": 2048576,
        "size_formatted": "2.0 MB",
        "folder": "documents/2024/01",
        "description": "项目文档",
        "is_public": false,
        "uploaded_by": {
          "id": 1,
          "name": "John Doe"
        },
        "created_at": "2024-01-01T12:00:00Z"
      }
    ],
    "pagination": {
      "page": 1,
      "page_size": 20,
      "total": 1,
      "pages": 1
    },
    "summary": {
      "total_files": 1,
      "total_size": 2048576,
      "total_size_formatted": "2.0 MB"
    }
  },
  "timestamp": "2024-01-01T12:00:00Z"
}

删除单个文件

删除指定的文件。

接口信息

  • URL: /api/v1/oss/delete
  • 方法: DELETE
  • 认证: 需要登录

请求参数

字段类型必填描述示例
file_idstring文件ID"f47ac10b-58cc-4372-a567-0e02b2c3d479"

请求示例

json
{
  "file_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}

响应示例

成功响应 (200)

json
{
  "code": 200,
  "message": "删除成功",
  "data": {
    "deleted": true,
    "file_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
  },
  "timestamp": "2024-01-01T12:00:00Z"
}

批量删除文件

同时删除多个文件。

接口信息

  • URL: /api/v1/oss/batch-delete
  • 方法: DELETE
  • 认证: 需要登录

请求参数

字段类型必填描述示例
file_idsstring[]文件ID列表["id1", "id2", "id3"]

请求示例

json
{
  "file_ids": [
    "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "12345678-90ab-cdef-1234-567890abcdef"
  ]
}

响应示例

成功响应 (200)

json
{
  "code": 200,
  "message": "批量删除完成",
  "data": {
    "total": 3,
    "success": 2,
    "failed": 1,
    "results": [
      {
        "file_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
        "status": "success"
      },
      {
        "file_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "status": "success"
      },
      {
        "file_id": "12345678-90ab-cdef-1234-567890abcdef",
        "status": "failed",
        "error": "文件不存在"
      }
    ]
  },
  "timestamp": "2024-01-01T12:00:00Z"
}

生成预签名URL

生成文件的预签名访问URL,用于临时访问私有文件。

接口信息

  • URL: /api/v1/oss/presigned-url
  • 方法: POST
  • 认证: 需要登录

请求参数

字段类型必填描述示例
file_idstring文件ID"f47ac10b-58cc-4372-a567-0e02b2c3d479"
expires_inint过期时间(秒)3600

请求示例

json
{
  "file_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "expires_in": 3600
}

响应示例

成功响应 (200)

json
{
  "code": 200,
  "message": "生成成功",
  "data": {
    "presigned_url": "https://your-bucket.oss-cn-hangzhou.aliyuncs.com/documents/2024/01/document.pdf?Expires=1705123200&OSSAccessKeyId=LTAI4GCH1vX6DKqJWxd6****&Signature=abc123...",
    "expires_at": "2024-01-01T13:00:00Z",
    "expires_in": 3600
  },
  "timestamp": "2024-01-01T12:00:00Z"
}

获取OSS配置信息

获取OSS存储的配置信息(公开信息)。

接口信息

  • URL: /api/v1/oss/config
  • 方法: GET
  • 认证: 需要登录

响应示例

成功响应 (200)

json
{
  "code": 200,
  "message": "获取成功",
  "data": {
    "max_file_size": 104857600,
    "max_file_size_formatted": "100 MB",
    "allowed_types": [
      "image/jpeg",
      "image/png",
      "image/gif",
      "application/pdf",
      "application/msword",
      "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
      "application/vnd.ms-excel",
      "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
      "text/plain"
    ],
    "storage_endpoint": "https://your-bucket.oss-cn-hangzhou.aliyuncs.com",
    "features": {
      "multipart_upload": true,
      "resumable_upload": true,
      "image_processing": true,
      "video_processing": false
    }
  },
  "timestamp": "2024-01-01T12:00:00Z"
}

数据模型

FileResponse

typescript
interface FileResponse {
  id: string;                    // 文件唯一ID
  filename: string;              // 存储文件名
  original_name: string;         // 原始文件名
  mime_type: string;             // MIME类型
  size: number;                  // 文件大小(字节)
  size_formatted: string;        // 格式化的文件大小
  url: string;                   // 文件访问URL
  download_url?: string;         // 下载URL(包含签名)
  folder?: string;               // 文件夹路径
  description?: string;          // 文件描述
  is_public: boolean;            // 是否公开
  uploaded_by: User;             // 上传者信息
  created_at: string;            // 创建时间
  last_accessed_at?: string;     // 最后访问时间
  download_count?: number;       // 下载次数
}

FileListResponse

typescript
interface FileListResponse {
  files: FileResponse[];         // 文件列表
  pagination: {                  // 分页信息
    page: number;
    page_size: number;
    total: number;
    pages: number;
  };
  summary: {                     // 汇总信息
    total_files: number;
    total_size: number;
    total_size_formatted: string;
  };
}

错误代码

错误代码描述解决方案
400请求参数错误检查请求参数格式和必填字段
401未认证检查 JWT token 是否有效
403无权限确认用户具有相应权限
404文件不存在检查文件ID是否正确
413文件过大检查文件大小是否超出限制
415不支持的文件类型检查文件类型是否允许
500服务器内部错误联系系统管理员
503存储服务不可用检查OSS配置和网络连接

集成示例

React Upload Component

jsx
import React, { useState } from 'react';
import axios from 'axios';

const FileUpload = () => {
  const [uploading, setUploading] = useState(false);
  const [progress, setProgress] = useState(0);
  const [files, setFiles] = useState([]);

  const handleFileSelect = (event) => {
    setFiles(Array.from(event.target.files));
  };

  const handleUpload = async () => {
    if (files.length === 0) return;

    setUploading(true);
    setProgress(0);

    try {
      const token = localStorage.getItem('token');
      const formData = new FormData();

      files.forEach(file => {
        formData.append('files', file);
      });

      formData.append('folder', 'uploads/' + new Date().toISOString().split('T')[0]);
      formData.append('description', '批量上传文件');

      const response = await axios.post('/api/v1/oss/batch-upload', formData, {
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'multipart/form-data'
        },
        onUploadProgress: (progressEvent) => {
          const percentCompleted = Math.round(
            (progressEvent.loaded * 100) / progressEvent.total
          );
          setProgress(percentCompleted);
        }
      });

      console.log('上传成功:', response.data);
      setFiles([]);
      setProgress(0);
    } catch (error) {
      console.error('上传失败:', error.response?.data?.message || error.message);
    } finally {
      setUploading(false);
    }
  };

  return (
    <div className="file-upload">
      <h3>文件上传</h3>

      <div className="upload-area">
        <input
          type="file"
          multiple
          onChange={handleFileSelect}
          disabled={uploading}
        />

        <button
          onClick={handleUpload}
          disabled={uploading || files.length === 0}
        >
          {uploading ? '上传中...' : '上传文件'}
        </button>
      </div>

      {uploading && (
        <div className="progress">
          <div>上传进度: {progress}%</div>
          <div className="progress-bar">
            <div
              className="progress-fill"
              style={{ width: `${progress}%` }}
            />
          </div>
        </div>
      )}

      {files.length > 0 && (
        <div className="file-list">
          <h4>待上传文件:</h4>
          <ul>
            {files.map((file, index) => (
              <li key={index}>
                {file.name} ({(file.size / 1024 / 1024).toFixed(2)} MB)
              </li>
            ))}
          </ul>
        </div>
      )}
    </div>
  );
};

export default FileUpload;

Python Upload Client

python
import requests
import os
from typing import List, Optional

class OSSClient:
    def __init__(self, base_url: str, token: str):
        self.base_url = base_url
        self.token = token
        self.headers = {
            'Authorization': f'Bearer {token}'
        }

    def upload_file(self, file_path: str, folder: Optional[str] = None,
                   description: Optional[str] = None) -> dict:
        """上传单个文件"""
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"文件不存在: {file_path}")

        with open(file_path, 'rb') as f:
            files = {'file': f}
            data = {}

            if folder:
                data['folder'] = folder
            if description:
                data['description'] = description

            response = requests.post(
                f'{self.base_url}/api/v1/oss/upload',
                headers=self.headers,
                files=files,
                data=data
            )
            response.raise_for_status()
            return response.json()

    def upload_multiple_files(self, file_paths: List[str],
                            folder: Optional[str] = None,
                            description: Optional[str] = None) -> dict:
        """批量上传文件"""
        files = []
        try:
            for file_path in file_paths:
                if not os.path.exists(file_path):
                    raise FileNotFoundError(f"文件不存在: {file_path}")

                files.append((
                    'files',
                    (os.path.basename(file_path), open(file_path, 'rb'))
                ))

            data = {}
            if folder:
                data['folder'] = folder
            if description:
                data['description'] = description

            response = requests.post(
                f'{self.base_url}/api/v1/oss/batch-upload',
                headers=self.headers,
                files=files,
                data=data
            )
            response.raise_for_status()
            return response.json()

        finally:
            # 关闭所有文件
            for _, (_, f) in files:
                f.close()

    def get_file_info(self, file_id: str) -> dict:
        """获取文件信息"""
        response = requests.get(
            f'{self.base_url}/api/v1/oss/file-info',
            headers=self.headers,
            params={'file_id': file_id}
        )
        response.raise_for_status()
        return response.json()

    def delete_file(self, file_id: str) -> dict:
        """删除文件"""
        response = requests.delete(
            f'{self.base_url}/api/v1/oss/delete',
            headers=self.headers,
            json={'file_id': file_id}
        )
        response.raise_for_status()
        return response.json()

    def generate_presigned_url(self, file_id: str, expires_in: int = 3600) -> dict:
        """生成预签名URL"""
        response = requests.post(
            f'{self.base_url}/api/v1/oss/presigned-url',
            headers=self.headers,
            json={'file_id': file_id, 'expires_in': expires_in}
        )
        response.raise_for_status()
        return response.json()

# 使用示例
client = OSSClient('http://localhost:8080', 'your-jwt-token')

# 上传单个文件
try:
    result = client.upload_file(
        '/path/to/document.pdf',
        folder='documents/2024/01',
        description='项目文档'
    )
    print(f"上传成功: {result['data']['url']}")
except Exception as e:
    print(f"上传失败: {e}")

# 批量上传
try:
    files = ['/path/to/doc1.pdf', '/path/to/doc2.pdf', '/path/to/image.png']
    result = client.upload_multiple_files(
        files,
        folder='batch-upload',
        description='批量上传文档'
    )
    print(f"批量上传完成: 成功 {result['data']['success']}/{result['data']['total']} 个文件")
except Exception as e:
    print(f"批量上传失败: {e}")

相关链接

基于 MIT 许可发布