跳到主要内容

API参考文档 (API Reference)

本文档提供文件模块所有API接口的完整说明,包括请求参数、响应格式、错误码和使用示例。


🌐 基本信息

Base URL

http://localhost:8080/api/v1/files

认证方式

所有API需要JWT认证,请在请求头中携带token:

Authorization: Bearer <your_jwt_token>

响应格式

所有API返回统一的JSON格式:

{
"code": 0, // 0=成功, 非0=错误码
"message": "成功", // 提示消息
"data": { ... }, // 响应数据
"timestamp": 1702465200000
}

📋 API列表

方法路径说明
POST/presigned生成预签名上传URL
PATCH/{fileId}/confirm确认上传完成
GET/{fileId}查询文件信息
GET/{fileId}/access-url获取文件访问URL
DELETE/{fileId}删除文件

1. 生成预签名上传URL

请求

POST /api/v1/files/presigned
Content-Type: application/json
Authorization: Bearer <token>

{
"fileName": "example.jpg",
"fileSize": 1024000,
"fileType": "image/jpeg",
"md5": "abc123def456"
}

请求参数

字段类型必填说明示例
fileNameString文件名(含扩展名)"avatar.jpg"
fileSizeLong文件大小(字节)1024000
fileTypeStringMIME类型"image/jpeg"
md5StringMD5哈希(秒传用)"a1b2c3..."

响应

成功(普通上传)

{
"code": 0,
"message": "成功",
"data": {
"uploadUrl": "https://s3.bitiful.net/blog-files/uploads/2025/12/13/xxx.jpg?X-Amz-...",
"fileId": "1999842821338591234",
"fileKey": "uploads/2025/12/13/abc123.jpg",
"instant": false
}
}

成功(秒传)

{
"code": 0,
"message": "成功",
"data": {
"uploadUrl": null,
"fileId": "1888765432109876543",
"fileKey": "uploads/2025/12/10/old123.jpg",
"instant": true
}
}

字段说明

字段类型说明
uploadUrlString预签名URL(秒传时为null)
fileIdString文件ID(Long序列化为String)
fileKeyString云端存储路径
instantBoolean是否秒传

使用示例

const response = await fetch('/api/v1/files/presigned', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
fileName: file.name,
fileSize: file.size,
fileType: file.type,
md5: await calculateMD5(file)
})
});

const {uploadUrl, fileId, instant} = (await response.json()).data;

if (instant) {
console.log('⚡ 秒传成功');
} else {
await fetch(uploadUrl, {method: 'PUT', body: file});
}

2. 确认上传完成

请求

PATCH /api/v1/files/1999842821338591234/confirm
Authorization: Bearer <token>

路径参数

参数类型说明
fileIdLong文件ID

响应

{
"code": 0,
"message": "成功",
"data": null
}

注意事项

  • 幂等性:重复调用不会报错
  • ✅ 仅更新状态:PENDING → COMPLETED
  • ⚠️ 不验证文件是否真实存在于云端

3. 查询文件信息

请求

GET /api/v1/files/1999842821338591234
Authorization: Bearer <token>

响应

{
"code": 0,
"message": "成功",
"data": {
"id": "1999842821338591234",
"fileName": "example.jpg",
"fileSize": 1024000,
"fileType": "image/jpeg",
"fileKey": "uploads/2025/12/13/abc123.jpg",
"md5": "a1b2c3def456",
"storageType": "BITIFUL",
"bucketName": "blog-files",
"uploadStatus": 1,
"createTime": "2025-12-13T14:30:00",
"updateTime": "2025-12-13T14:35:00"
}
}

4. 获取文件访问URL

请求

GET /api/v1/files/1999842821338591234/access-url?expireMinutes=60
Authorization: Bearer <token>

查询参数

参数类型必填默认值说明
expireMinutesInteger60过期时间(分钟,最大60)

响应

{
"code": 0,
"message": "成功",
"data": "https://s3.bitiful.net/blog-files/uploads/2025/12/13/abc123.jpg?X-Amz-Expires=3600&..."
}

使用示例

const response = await fetch(`/api/v1/files/${fileId}/access-url`, {
headers: {'Authorization': `Bearer ${token}`}
});

const accessUrl = (await response.json()).data;

// 显示图片
document.getElementById('img').src = accessUrl;

// 或下载文件
window.open(accessUrl);

5. 删除文件

请求

DELETE /api/v1/files/1999842821338591234
Authorization: Bearer <token>

响应

{
"code": 0,
"message": "成功",
"data": null
}

删除逻辑

  1. 逻辑删除:数据库记录标记为删除(is_deleted=1)
  2. 物理删除:异步调用云存储API删除文件
  3. 幂等性:文件不存在时也返回成功

⚠️ 错误码

通用错误码

错误码说明HTTP状态
0成功200
10001参数验证失败400
10002未授权401
10003无权限403

文件模块错误码

错误码常量名说明HTTP状态
11001FILE_UPLOAD_FAILED文件上传失败500
11002FILE_INVALID_TYPE文件类型不允许400
11003FILE_EXCEED_MAX_SIZE文件超过大小限制400
11004FILE_INVALID_NAME文件名无效400
11006FILE_STORAGE_ERROR存储服务异常500
11007FILE_PRESIGNED_URL_FAILED预签名URL生成失败500
11008FILE_NOT_FOUND文件不存在404

错误响应示例

{
"code": 11003,
"message": "文件大小超过限制(最大10MB)",
"data": null,
"timestamp": 1702465200000
}

📝 完整上传流程示例

async function uploadFile(file) {
const token = localStorage.getItem('token');

try {
// 1. 计算MD5
const md5 = await calculateMD5(file);

// 2. 请求预签名URL
const presignedResp = await fetch('/api/v1/files/presigned', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
fileName: file.name,
fileSize: file.size,
fileType: file.type,
md5: md5
})
});

if (!presignedResp.ok) {
throw new Error('获取上传URL失败');
}

const {uploadUrl, fileId, instant} = (await presignedResp.json()).data;

// 3. 判断是否秒传
if (instant) {
console.log('⚡ 秒传成功,fileId:', fileId);
return fileId;
}

// 4. 上传到云端
const uploadResp = await fetch(uploadUrl, {
method: 'PUT',
body: file
});

if (!uploadResp.ok) {
throw new Error('上传失败');
}

// 5. 确认上传
const confirmResp = await fetch(`/api/v1/files/${fileId}/confirm`, {
method: 'PATCH',
headers: {'Authorization': `Bearer ${token}`}
});

if (!confirmResp.ok) {
throw new Error('确认上传失败');
}

console.log('🎉 上传成功,fileId:', fileId);
return fileId;

} catch (error) {
console.error('上传失败:', error);
throw error;
}
}

// 使用示例
const fileId = await uploadFile(document.getElementById('file').files[0]);

// 获取访问URL
const accessResp = await fetch(`/api/v1/files/${fileId}/access-url`, {
headers: {'Authorization': `Bearer ${token}`}
});
const accessUrl = (await accessResp.json()).data;

// 显示图片
document.getElementById('preview').src = accessUrl;

🧪 Postman测试集合

环境变量

{
"base_url": "http://localhost:8080",
"token": "your_jwt_token"
}

请求集合

创建Postman Collection,包含以下请求:

  1. Generate Presigned URL

    • Method: POST
    • URL: {{base_url}}/api/v1/files/presigned
    • Headers: Authorization: Bearer {{token}}
    • Body: (见上文)
  2. Confirm Upload

    • Method: PATCH
    • URL: {{base_url}}/api/v1/files/{{fileId}}/confirm
  3. Get File Info

    • Method: GET
    • URL: {{base_url}}/api/v1/files/{{fileId}}
  4. Get Access URL

    • Method: GET
    • URL: {{base_url}}/api/v1/files/{{fileId}}/access-url
  5. Delete File

    • Method: DELETE
    • URL: {{base_url}}/api/v1/files/{{fileId}}

📚 相关文档


🎓 提示:本文档已包含完整的错误码说明,请参见上方"错误码"章节。