feat: 修复用户头像上传功能,支持Base64图片上传到腾讯云COS

- 修复 updateUserInfo 接口路由(update_userinfo -> updateUserInfo)
- IUploadService 添加 UploadBase64ImageAsync 方法
- UploadService 实现 Base64 图片解析和 COS 上传
- UserController 注入 IUploadService 替代本地保存逻辑
- HoneyBox.Api 添加对 HoneyBox.Admin.Business 的项目引用
This commit is contained in:
zpc 2026-01-28 14:33:27 +08:00
parent 72145441ee
commit a95a2852d4
4 changed files with 108 additions and 47 deletions

View File

@ -22,6 +22,14 @@ public interface IUploadService
/// <returns>上传响应列表</returns>
Task<List<UploadResponse>> UploadImagesAsync(List<IFormFile> files);
/// <summary>
/// 上传Base64编码的图片
/// </summary>
/// <param name="base64Image">Base64编码的图片数据可包含data:image/xxx;base64,前缀)</param>
/// <param name="fileNamePrefix">文件名前缀,如 "avatar"</param>
/// <returns>上传后的图片URL失败返回null</returns>
Task<string?> UploadBase64ImageAsync(string base64Image, string fileNamePrefix = "image");
/// <summary>
/// 获取预签名上传URL客户端直传
/// </summary>

View File

@ -101,6 +101,95 @@ public class UploadService : IUploadService
return results;
}
/// <inheritdoc />
public async Task<string?> UploadBase64ImageAsync(string base64Image, string fileNamePrefix = "image")
{
try
{
if (string.IsNullOrWhiteSpace(base64Image))
{
_logger.LogWarning("Base64图片数据为空");
return null;
}
// 解析Base64数据和MIME类型
string base64Data;
string contentType = "image/png";
string extension = ".png";
if (base64Image.Contains(','))
{
// 格式: data:image/png;base64,xxxxx
var parts = base64Image.Split(',');
base64Data = parts[1];
// 解析MIME类型
var header = parts[0]; // data:image/png;base64
if (header.Contains(':') && header.Contains(';'))
{
var mimeType = header.Split(':')[1].Split(';')[0];
contentType = mimeType;
extension = mimeType switch
{
"image/jpeg" => ".jpg",
"image/jpg" => ".jpg",
"image/png" => ".png",
"image/gif" => ".gif",
"image/webp" => ".webp",
_ => ".png"
};
}
}
else
{
base64Data = base64Image;
}
// 解码Base64数据
byte[] imageBytes;
try
{
imageBytes = Convert.FromBase64String(base64Data);
}
catch (FormatException ex)
{
_logger.LogWarning(ex, "Base64解码失败");
return null;
}
// 验证文件大小 (10MB)
if (imageBytes.Length > 10 * 1024 * 1024)
{
_logger.LogWarning("Base64图片大小超过限制: {Size} bytes", imageBytes.Length);
return null;
}
// 生成文件名
var fileName = $"{fileNamePrefix}_{DateTime.UtcNow:yyyyMMddHHmmss}{extension}";
// 获取存储提供者并上传
var provider = await GetStorageProviderAsync();
using var stream = new MemoryStream(imageBytes);
var result = await provider.UploadAsync(stream, fileName, contentType);
if (result.Success)
{
_logger.LogInformation("Base64图片上传成功: {Url}", result.Url);
return result.Url;
}
else
{
_logger.LogWarning("Base64图片上传失败: {Error}", result.ErrorMessage);
return null;
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Base64图片上传异常");
return null;
}
}
/// <summary>
/// 验证文件
/// </summary>

View File

@ -1,4 +1,5 @@
using System.Security.Claims;
using HoneyBox.Admin.Business.Services.Interfaces;
using HoneyBox.Core.Interfaces;
using HoneyBox.Model.Base;
using HoneyBox.Model.Models.Asset;
@ -24,6 +25,7 @@ public class UserController : ControllerBase
private readonly IAssetService _assetService;
private readonly IVipService _vipService;
private readonly IQuanYiService _quanYiService;
private readonly IUploadService _uploadService;
private readonly ILogger<UserController> _logger;
public UserController(
@ -32,6 +34,7 @@ public class UserController : ControllerBase
IAssetService assetService,
IVipService vipService,
IQuanYiService quanYiService,
IUploadService uploadService,
ILogger<UserController> logger)
{
_userService = userService;
@ -39,6 +42,7 @@ public class UserController : ControllerBase
_assetService = assetService;
_vipService = vipService;
_quanYiService = quanYiService;
_uploadService = uploadService;
_logger = logger;
}
@ -166,12 +170,17 @@ public class UserController : ControllerBase
if (!string.IsNullOrWhiteSpace(request.Imagebase))
{
// Base64图片上传到腾讯云COS
var headimgUrl = await UploadBase64ImageAsync(request.Imagebase, userId.Value);
var headimgUrl = await _uploadService.UploadBase64ImageAsync(request.Imagebase, $"avatar_{userId}");
if (!string.IsNullOrWhiteSpace(headimgUrl))
{
updateDto.Headimg = headimgUrl;
hasUpdate = true;
}
else
{
_logger.LogWarning("头像上传失败: UserId={UserId}", userId);
return ApiResponse.Fail("头像上传失败,请重试");
}
}
else if (!string.IsNullOrWhiteSpace(request.Headimg))
{
@ -524,51 +533,5 @@ public class UserController : ControllerBase
return userId;
}
/// <summary>
/// 上传Base64图片到腾讯云COS
/// </summary>
/// <param name="base64Image">Base64编码的图片数据</param>
/// <param name="userId">用户ID</param>
/// <returns>上传后的图片URL</returns>
private async Task<string?> UploadBase64ImageAsync(string base64Image, int userId)
{
try
{
// 移除Base64前缀如果有
var base64Data = base64Image;
if (base64Image.Contains(","))
{
base64Data = base64Image.Split(',')[1];
}
// 解码Base64数据
var imageBytes = Convert.FromBase64String(base64Data);
// TODO: 实现腾讯云COS上传
// 目前返回一个占位URL实际应该上传到COS并返回真实URL
// 可以在后续添加ICosUploadService接口和实现
// 临时方案:将图片保存到本地并返回相对路径
var fileName = $"avatar_{userId}_{DateTime.UtcNow:yyyyMMddHHmmss}.png";
var uploadPath = Path.Combine("wwwroot", "uploads", "avatars");
if (!Directory.Exists(uploadPath))
{
Directory.CreateDirectory(uploadPath);
}
var filePath = Path.Combine(uploadPath, fileName);
await System.IO.File.WriteAllBytesAsync(filePath, imageBytes);
// 返回相对URL
return $"/uploads/avatars/{fileName}";
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to upload base64 image for user: {UserId}", userId);
return null;
}
}
#endregion
}

View File

@ -28,6 +28,7 @@
<ProjectReference Include="..\HoneyBox.Model\HoneyBox.Model.csproj" />
<ProjectReference Include="..\HoneyBox.Core\HoneyBox.Core.csproj" />
<ProjectReference Include="..\HoneyBox.Infrastructure\HoneyBox.Infrastructure.csproj" />
<ProjectReference Include="..\HoneyBox.Admin.Business\HoneyBox.Admin.Business.csproj" />
</ItemGroup>
<ItemGroup>