10 KiB
Design Document
Overview
本设计文档描述图片上传功能的技术实现方案。系统采用策略模式实现多存储提供者支持,后端使用 ASP.NET Core 实现上传接口,前端使用 Vue 3 + Element Plus 实现上传组件。
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Frontend (Vue 3) │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ ImageUpload │ │ Business Pages │ │
│ │ Component │◄───│ (10 pages) │ │
│ └────────┬────────┘ └─────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ upload.ts API │ │
│ └────────┬────────┘ │
└───────────┼─────────────────────────────────────────────────────┘
│ HTTP POST /api/admin/upload/image
▼
┌───────────────────────────────────────────────────────────────────┐
│ Backend (ASP.NET Core) │
│ ┌─────────────────┐ │
│ │ UploadController│ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ IUploadService │◄───│ UploadService │ │
│ └─────────────────┘ └────────┬────────┘ │
│ │ │
│ ┌──────────────────────┼──────────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐ │
│ │ IStorageProvider│ │LocalStorage │ │TencentCos │ │
│ │ (Interface) │◄───│Provider │ │Provider │ │
│ └─────────────────┘ └─────────────────┘ └──────────────┘ │
└───────────────────────────────────────────────────────────────────┘
Components and Interfaces
Backend Components
1. IStorageProvider 接口
public interface IStorageProvider
{
/// <summary>
/// 存储类型标识
/// </summary>
string StorageType { get; }
/// <summary>
/// 上传文件
/// </summary>
Task<UploadResult> UploadAsync(Stream fileStream, string fileName, string contentType);
/// <summary>
/// 删除文件
/// </summary>
Task<bool> DeleteAsync(string fileUrl);
}
2. LocalStorageProvider 本地存储
public class LocalStorageProvider : IStorageProvider
{
public string StorageType => "1";
// 保存到 wwwroot/uploads/{yyyy}/{MM}/{dd}/{filename}
// 返回 /uploads/{yyyy}/{MM}/{dd}/{filename}
}
3. TencentCosProvider 腾讯云COS存储
public class TencentCosProvider : IStorageProvider
{
public string StorageType => "3";
// 使用腾讯云COS SDK上传
// 返回 {Domain}/uploads/{yyyy}/{MM}/{dd}/{filename}
}
4. IUploadService 上传服务接口
public interface IUploadService
{
/// <summary>
/// 上传图片
/// </summary>
Task<UploadResponse> UploadImageAsync(IFormFile file);
/// <summary>
/// 批量上传图片
/// </summary>
Task<List<UploadResponse>> UploadImagesAsync(List<IFormFile> files);
}
5. UploadService 上传服务实现
public class UploadService : IUploadService
{
// 根据配置选择存储提供者
// 验证文件格式和大小
// 生成唯一文件名
// 调用存储提供者上传
}
6. UploadController 上传控制器
[Route("api/admin/upload")]
public class UploadController : BusinessControllerBase
{
[HttpPost("image")]
public async Task<IActionResult> UploadImage(IFormFile file);
[HttpPost("images")]
public async Task<IActionResult> UploadImages(List<IFormFile> files);
}
Frontend Components
1. ImageUpload 组件 (已创建)
位置: src/components/ImageUpload/index.vue
Props:
modelValue: string - 图片URL (v-model)disabled: boolean - 是否禁用placeholder: string - 占位文字showUrlInput: boolean - 是否显示URL输入框accept: string - 接受的文件类型maxSize: number - 最大文件大小(MB)tip: string - 提示文字
Events:
update:modelValue- URL变化change- 图片变化upload-success- 上传成功upload-error- 上传失败
2. upload.ts API (已创建)
位置: src/api/upload.ts
export function uploadFile(file: File, onProgress?: (percent: number) => void): Promise<ApiResponse<UploadResponse>>
export function uploadFiles(files: File[], onProgress?: (percent: number) => void): Promise<ApiResponse<UploadResponse[]>>
Data Models
Backend Models
/// <summary>
/// 上传响应
/// </summary>
public class UploadResponse
{
/// <summary>
/// 文件URL
/// </summary>
public string Url { get; set; } = string.Empty;
/// <summary>
/// 文件名
/// </summary>
public string FileName { get; set; } = string.Empty;
/// <summary>
/// 文件大小(字节)
/// </summary>
public long FileSize { get; set; }
}
/// <summary>
/// 上传结果(内部使用)
/// </summary>
public class UploadResult
{
public bool Success { get; set; }
public string? Url { get; set; }
public string? ErrorMessage { get; set; }
}
上传配置 (已存在)
public class UploadSetting
{
public string? Type { get; set; } // 1=本地, 3=腾讯云
public string? Bucket { get; set; } // COS Bucket名称
public string? Region { get; set; } // COS 地域
public string? AccessKeyId { get; set; } // COS SecretId
public string? AccessKeySecret { get; set; } // COS SecretKey
public string? Domain { get; set; } // 访问域名
}
Correctness Properties
A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.
Property 1: 文件格式验证
For any 上传的文件,如果文件扩展名不在允许列表(jpg, jpeg, png, gif, webp)中,THE Upload_Service SHALL 返回格式错误。
Validates: Requirements 1.2, 1.4
Property 2: 文件大小验证
For any 上传的文件,如果文件大小超过10MB,THE Upload_Service SHALL 返回大小超限错误。
Validates: Requirements 1.3
Property 3: 文件名唯一性
For any 两次上传操作,即使上传相同的文件,生成的文件名 SHALL 不同。
Validates: Requirements 1.5
Property 4: 存储策略一致性
For any 上传操作,返回的URL格式 SHALL 与当前配置的存储类型一致:
- 本地存储: URL以
/uploads/开头 - 腾讯云COS: URL以配置的 Domain 开头
Validates: Requirements 2.3, 3.3
Property 5: 上传成功返回有效URL
For any 成功的上传操作,返回的URL SHALL 是可访问的有效图片地址。
Validates: Requirements 1.1
Error Handling
| 错误场景 | 错误码 | 错误信息 |
|---|---|---|
| 文件为空 | 400 | 请选择要上传的文件 |
| 文件格式不支持 | 400 | 只支持 jpg、jpeg、png、gif、webp 格式的图片 |
| 文件大小超限 | 400 | 文件大小不能超过10MB |
| 存储配置无效 | 500 | 存储配置无效,请检查上传配置 |
| COS上传失败 | 500 | 上传到云存储失败: {具体错误} |
| 本地存储失败 | 500 | 保存文件失败: {具体错误} |
Testing Strategy
单元测试
-
UploadService 测试
- 测试文件格式验证
- 测试文件大小验证
- 测试文件名生成唯一性
- 测试存储提供者选择逻辑
-
LocalStorageProvider 测试
- 测试目录创建
- 测试文件保存
- 测试URL生成
-
TencentCosProvider 测试
- 测试配置读取
- 测试上传逻辑(使用Mock)
集成测试
- API 集成测试
- 测试上传接口完整流程
- 测试错误响应格式
前端测试
- ImageUpload 组件测试
- 测试文件选择
- 测试上传进度显示
- 测试v-model绑定