阿里云OSS图片上传方法说明.md
13.1 KB
阿里云OSS图片上传方法说明
文档日期:2025年1月
文件位置:netcore/src/Modularity/System/NCC.System/Service/Common/FileService.cs
一、核心上传方法
1.1 标准文件上传方法
方法名:Uploader
位置:FileService.cs 第60-95行
接口路径:POST /api/File/Uploader/{type}
功能:
- 上传文件/图片到服务器或OSS
- 支持多种存储类型(本地、MinIO、阿里云OSS、腾讯云COS)
annexpic类型强制使用阿里云OSS存储
参数:
type:文件类型(如:annexpic、avatar、temporary等)file:上传的文件(IFormFile)
返回值:
{
"name": "原始文件名.jpg",
"fileId": "20250123_123456789.jpg",
"url": "https://oss.example.com/2025/01/23/20250123_123456789.jpg?签名参数"
}
关键代码:
[HttpPost("Uploader/{type}")]
[AllowAnonymous]
public async Task<dynamic> Uploader(string type, IFormFile file)
{
// 1. 验证文件类型
var fileType = Path.GetExtension(file.FileName).Replace(".", "");
if (!this.AllowFileType(fileType, type))
throw NCCException.Oh(ErrorCode.D1800);
// 2. 生成文件路径和文件名
var _filePath = GetPathByType(type);
var now = DateTime.Now;
var _fileName = now.ToString("yyyyMMdd") + "_" + YitIdHelper.NextId().ToString() + Path.GetExtension(file.FileName);
// 3. annexpic 类型强制使用阿里云OSS存储
string forceStoreType = type == "annexpic" ? "aliyun-oss" : null;
string uploadFilePath = _filePath;
if (type == "annexpic")
{
// 按天生成文件夹:yyyy/MM/dd
var dateFolder = now.ToString("yyyy/MM/dd");
uploadFilePath = dateFolder;
}
// 4. 上传文件
await UploadFileByType(file, uploadFilePath, _fileName, forceStoreType);
// 5. 获取访问URL
string fileUrl;
if (type == "annexpic")
{
fileUrl = await GetOSSAccessUrl(uploadFilePath, _fileName);
}
else
{
fileUrl = string.Format("/api/File/Image/{0}/{1}", type, _fileName);
}
return new { name = file.FileName, fileId = _fileName, url = fileUrl };
}
1.2 Base64图片上传方法
方法名:UploadBase64Image
位置:FileService.cs 第696-775行
接口路径:POST /api/File/UploadBase64Image
功能:
- 上传Base64格式的图片到阿里云OSS
- 自动解析Base64数据并提取图片格式
- 所有类型都上传到阿里云OSS存储
参数(Base64ImageUploadInput):
{
"base64Data": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
"fileName": "图片名称(可选)",
"imageType": "annexpic(可选,默认为temporary)"
}
返回值:
{
"name": "图片名称.jpg",
"fileId": "20250123_123456789.jpg",
"url": "https://oss.example.com/2025/01/23/20250123_123456789.jpg?签名参数",
"fileSize": 12345,
"imageFormat": "JPEG",
"imageType": "annexpic"
}
关键代码:
[HttpPost("UploadBase64Image")]
[AllowAnonymous]
public async Task<dynamic> UploadBase64Image([FromBody] Base64ImageUploadInput input)
{
// 1. 解析Base64数据
var imageData = ParseBase64Data(input.Base64Data, out string imageFormat);
// 2. 验证图片格式
if (!IsValidImageFormat(imageFormat))
throw NCCException.Oh($"不支持的图片格式: {imageFormat}");
// 3. 生成文件路径和文件名
var imageType = string.IsNullOrEmpty(input.ImageType) ? "temporary" : input.ImageType;
var now = DateTime.Now;
string uploadFilePath;
string fileName;
if (imageType == "annexpic")
{
fileName = now.ToString("yyyyMMdd") + "_" + YitIdHelper.NextId().ToString() + "." + imageFormat;
var dateFolder = now.ToString("yyyy/MM/dd");
uploadFilePath = dateFolder;
}
else
{
fileName = GenerateImageFileName(input.FileName, imageFormat);
var originalPath = GetPathByType(imageType).TrimEnd('/').TrimEnd('\\');
var dateFolder = now.ToString("yyyy/MM/dd");
uploadFilePath = $"{originalPath}/{dateFolder}";
}
// 4. 上传到OSS
var bucketName = KeyVariable.BucketName;
var ossPath = $"{uploadFilePath.TrimEnd('/').TrimEnd('\\')}/{fileName}";
using (var stream = new MemoryStream(imageData))
{
await _oSSServiceFactory.Create("aliyun").PutObjectAsync(bucketName, ossPath, stream);
}
// 5. 获取OSS访问URL
string accessUrl = await GetOSSAccessUrl(uploadFilePath, fileName);
return new
{
name = originalFileName,
fileId = fileName,
url = accessUrl,
fileSize = imageData.Length,
imageFormat = imageFormat.ToUpper(),
imageType = imageType,
};
}
二、核心上传逻辑
2.1 UploadFileByType 方法
位置:FileService.cs 第301-344行
功能:根据存储类型上传文件
关键代码:
[NonAction]
public async Task UploadFileByType(IFormFile file, string filePath, string fileName, string forceStoreType = null)
{
var bucketName = KeyVariable.BucketName;
var fileStoreType = !string.IsNullOrEmpty(forceStoreType) ? forceStoreType : KeyVariable.FileStoreType;
// OSS路径使用正斜杠,不使用Path.Combine
var uploadPath = fileStoreType == "aliyun-oss" || fileStoreType == "tencent-cos" || fileStoreType == "minio"
? $"{filePath.TrimEnd('/').TrimEnd('\\')}/{fileName}"
: Path.Combine(filePath, fileName);
var stream = file.OpenReadStream();
switch (fileStoreType)
{
case "minio":
await _oSSServiceFactory.Create().PutObjectAsync(bucketName, uploadPath, stream);
break;
case "aliyun-oss":
// ✅ 阿里云OSS上传
await _oSSServiceFactory.Create("aliyun").PutObjectAsync(bucketName, uploadPath, stream);
break;
case "tencent-cos":
await _oSSServiceFactory.Create("qcloud").PutObjectAsync(bucketName, uploadPath, stream);
break;
default:
// 本地存储
if (!Directory.Exists(filePath))
Directory.CreateDirectory(filePath);
using (var stream4 = File.Create(uploadPath))
{
await file.CopyToAsync(stream4);
}
break;
}
}
关键点:
- ✅ 使用
_oSSServiceFactory.Create("aliyun")创建阿里云OSS服务 - ✅ 使用
PutObjectAsync(bucketName, uploadPath, stream)上传文件 - ✅ OSS路径使用正斜杠
/,不使用Path.Combine
2.2 GetOSSAccessUrl 方法
位置:FileService.cs 第391-476行
功能:获取阿里云OSS文件的访问URL(带签名的临时访问URL)
关键代码:
[NonAction]
private async Task<string> GetOSSAccessUrl(string filePath, string fileName)
{
var bucketName = KeyVariable.BucketName;
var uploadPath = $"{filePath.TrimEnd('/').TrimEnd('\\')}/{fileName}";
// 使用OSS服务生成带签名的临时访问URL(有效期24小时)
var ossService = _oSSServiceFactory.Create("aliyun");
var presignedUrl = await ossService.PresignedGetObjectAsync(bucketName, uploadPath, 86400);
// 获取带签名的URL字符串
string urlString = string.Empty;
if (presignedUrl != null)
{
var urlType = presignedUrl.GetType();
var absoluteUriProp = urlType.GetProperty("AbsoluteUri");
if (absoluteUriProp != null)
{
urlString = absoluteUriProp.GetValue(presignedUrl)?.ToString() ?? string.Empty;
}
else
{
urlString = presignedUrl.ToString() ?? string.Empty;
}
}
// 如果配置了自定义域名,替换为自定义域名
var customDomain = _configuration["NCC_App:AliyunOSS:CustomDomain"]
?? _configuration["NCC_APP:AliyunOSS:CustomDomain"];
if (!string.IsNullOrEmpty(customDomain))
{
// 替换域名逻辑...
}
return urlString;
}
关键点:
- ✅ 使用
PresignedGetObjectAsync生成带签名的临时访问URL - ✅ 有效期:86400秒(24小时)
- ✅ 支持自定义域名配置
三、OSS服务配置
3.1 服务注册
位置:Startup.cs 第109-137行
配置代码:
#region 阿里云OSS
var aliyunOSSEndpoint = App.Configuration["NCC_App:AliyunOSS:Endpoint"];
var aliyunOSSAccessKey = App.Configuration["NCC_App:AliyunOSS:AccessKeyId"];
var aliyunOSSSecretKey = App.Configuration["NCC_App:AliyunOSS:AccessKeySecret"];
var aliyunOSSRegion = App.Configuration["NCC_App:AliyunOSS:Region"];
var bucketName = App.Configuration["NCC_App:BucketName"];
if (!string.IsNullOrEmpty(aliyunOSSEndpoint) && !string.IsNullOrEmpty(aliyunOSSAccessKey) && !string.IsNullOrEmpty(aliyunOSSSecretKey))
{
services.AddOSSService("aliyun", option =>
{
option.Provider = OSSProvider.Aliyun;
option.Endpoint = aliyunOSSEndpoint; // 格式:oss-{region}.aliyuncs.com
option.AccessKey = aliyunOSSAccessKey;
option.SecretKey = aliyunOSSSecretKey;
option.IsEnableHttps = true;
option.IsEnableCache = true;
if (!string.IsNullOrEmpty(aliyunOSSRegion))
{
option.Region = aliyunOSSRegion; // 如:cn-chengdu
}
});
}
#endregion
3.2 配置文件
位置:appsettings.json
配置项:
{
"NCC_App": {
"AliyunOSS": {
"Endpoint": "oss-cn-chengdu.aliyuncs.com",
"AccessKeyId": "your-access-key-id",
"AccessKeySecret": "your-access-key-secret",
"Region": "cn-chengdu",
"CustomDomain": "https://cdn.example.com" // 可选,自定义域名
},
"BucketName": "your-bucket-name",
"FileStoreType": "aliyun-oss" // 默认存储类型
}
}
四、使用示例
4.1 标准文件上传
前端调用:
// 使用 FormData
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/File/Uploader/annexpic', {
method: 'POST',
body: formData
});
const result = await response.json();
// result: { name: "原始文件名.jpg", fileId: "20250123_123456789.jpg", url: "https://..." }
curl 示例:
curl -X POST "http://localhost:2011/api/File/Uploader/annexpic" \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "file=@/path/to/image.jpg"
4.2 Base64图片上传
前端调用:
const base64Data = "data:image/jpeg;base64,/9j/4AAQSkZJRg...";
const response = await fetch('/api/File/UploadBase64Image', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
base64Data: base64Data,
fileName: "图片名称",
imageType: "annexpic"
})
});
const result = await response.json();
// result: { name: "图片名称.jpg", fileId: "20250123_123456789.jpg", url: "https://...", fileSize: 12345, imageFormat: "JPEG", imageType: "annexpic" }
curl 示例:
curl -X POST "http://localhost:2011/api/File/UploadBase64Image" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"base64Data": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
"fileName": "图片名称",
"imageType": "annexpic"
}'
五、文件路径规则
5.1 annexpic 类型
- 文件夹结构:
yyyy/MM/dd(如:2025/01/23) - 文件名格式:
yyyyMMdd_{ID}.{ext}(如:20250123_123456789.jpg) - 完整路径:
2025/01/23/20250123_123456789.jpg - 存储类型:强制使用阿里云OSS
5.2 其他类型
- 文件夹结构:
{原始路径}/yyyy/MM/dd - 文件名格式:根据类型生成
- 存储类型:根据配置决定(
KeyVariable.FileStoreType)
六、依赖服务
6.1 IOSSServiceFactory
接口:IOSSServiceFactory
实现:OnceMi.AspNetCore.OSS 库
使用方式:
// 创建阿里云OSS服务
var ossService = _oSSServiceFactory.Create("aliyun");
// 上传文件
await ossService.PutObjectAsync(bucketName, uploadPath, stream);
// 生成预签名URL
var presignedUrl = await ossService.PresignedGetObjectAsync(bucketName, uploadPath, 86400);
七、注意事项
7.1 路径格式
- ✅ OSS路径使用正斜杠
/,不使用Path.Combine - ✅ 路径格式:
{filePath}/{fileName}
7.2 文件命名
- ✅ 文件名格式:
yyyyMMdd_{ID}.{ext} - ✅ 使用
YitIdHelper.NextId()生成唯一ID
7.3 访问URL
- ✅ 返回带签名的临时访问URL(有效期24小时)
- ✅ 支持自定义域名配置
- ✅ 如果生成失败,返回相对路径作为降级方案
7.4 错误处理
- ✅ 上传失败时抛出异常,包含详细错误信息
- ✅ URL生成失败时返回相对路径
八、相关文件
- 主服务文件:
netcore/src/Modularity/System/NCC.System/Service/Common/FileService.cs - 服务注册:
netcore/src/Application/NCC.API.Core/Startup.cs - 配置文件:
netcore/src/Application/NCC.API/appsettings.json - 依赖库:
OnceMi.AspNetCore.OSS
文档完成时间:2025年1月
文档状态:✅ 已完成