feat: 修复用户头像上传功能,支持Base64图片上传到腾讯云COS
- 修复 updateUserInfo 接口路由(update_userinfo -> updateUserInfo) - IUploadService 添加 UploadBase64ImageAsync 方法 - UploadService 实现 Base64 图片解析和 COS 上传 - UserController 注入 IUploadService 替代本地保存逻辑 - HoneyBox.Api 添加对 HoneyBox.Admin.Business 的项目引用
This commit is contained in:
parent
72145441ee
commit
a95a2852d4
|
|
@ -22,6 +22,14 @@ public interface IUploadService
|
||||||
/// <returns>上传响应列表</returns>
|
/// <returns>上传响应列表</returns>
|
||||||
Task<List<UploadResponse>> UploadImagesAsync(List<IFormFile> files);
|
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>
|
/// <summary>
|
||||||
/// 获取预签名上传URL(客户端直传)
|
/// 获取预签名上传URL(客户端直传)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,95 @@ public class UploadService : IUploadService
|
||||||
return results;
|
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>
|
||||||
/// 验证文件
|
/// 验证文件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
using HoneyBox.Admin.Business.Services.Interfaces;
|
||||||
using HoneyBox.Core.Interfaces;
|
using HoneyBox.Core.Interfaces;
|
||||||
using HoneyBox.Model.Base;
|
using HoneyBox.Model.Base;
|
||||||
using HoneyBox.Model.Models.Asset;
|
using HoneyBox.Model.Models.Asset;
|
||||||
|
|
@ -24,6 +25,7 @@ public class UserController : ControllerBase
|
||||||
private readonly IAssetService _assetService;
|
private readonly IAssetService _assetService;
|
||||||
private readonly IVipService _vipService;
|
private readonly IVipService _vipService;
|
||||||
private readonly IQuanYiService _quanYiService;
|
private readonly IQuanYiService _quanYiService;
|
||||||
|
private readonly IUploadService _uploadService;
|
||||||
private readonly ILogger<UserController> _logger;
|
private readonly ILogger<UserController> _logger;
|
||||||
|
|
||||||
public UserController(
|
public UserController(
|
||||||
|
|
@ -32,6 +34,7 @@ public class UserController : ControllerBase
|
||||||
IAssetService assetService,
|
IAssetService assetService,
|
||||||
IVipService vipService,
|
IVipService vipService,
|
||||||
IQuanYiService quanYiService,
|
IQuanYiService quanYiService,
|
||||||
|
IUploadService uploadService,
|
||||||
ILogger<UserController> logger)
|
ILogger<UserController> logger)
|
||||||
{
|
{
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
|
|
@ -39,6 +42,7 @@ public class UserController : ControllerBase
|
||||||
_assetService = assetService;
|
_assetService = assetService;
|
||||||
_vipService = vipService;
|
_vipService = vipService;
|
||||||
_quanYiService = quanYiService;
|
_quanYiService = quanYiService;
|
||||||
|
_uploadService = uploadService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,12 +170,17 @@ public class UserController : ControllerBase
|
||||||
if (!string.IsNullOrWhiteSpace(request.Imagebase))
|
if (!string.IsNullOrWhiteSpace(request.Imagebase))
|
||||||
{
|
{
|
||||||
// Base64图片上传到腾讯云COS
|
// Base64图片上传到腾讯云COS
|
||||||
var headimgUrl = await UploadBase64ImageAsync(request.Imagebase, userId.Value);
|
var headimgUrl = await _uploadService.UploadBase64ImageAsync(request.Imagebase, $"avatar_{userId}");
|
||||||
if (!string.IsNullOrWhiteSpace(headimgUrl))
|
if (!string.IsNullOrWhiteSpace(headimgUrl))
|
||||||
{
|
{
|
||||||
updateDto.Headimg = headimgUrl;
|
updateDto.Headimg = headimgUrl;
|
||||||
hasUpdate = true;
|
hasUpdate = true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogWarning("头像上传失败: UserId={UserId}", userId);
|
||||||
|
return ApiResponse.Fail("头像上传失败,请重试");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (!string.IsNullOrWhiteSpace(request.Headimg))
|
else if (!string.IsNullOrWhiteSpace(request.Headimg))
|
||||||
{
|
{
|
||||||
|
|
@ -524,51 +533,5 @@ public class UserController : ControllerBase
|
||||||
return userId;
|
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
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
<ProjectReference Include="..\HoneyBox.Model\HoneyBox.Model.csproj" />
|
<ProjectReference Include="..\HoneyBox.Model\HoneyBox.Model.csproj" />
|
||||||
<ProjectReference Include="..\HoneyBox.Core\HoneyBox.Core.csproj" />
|
<ProjectReference Include="..\HoneyBox.Core\HoneyBox.Core.csproj" />
|
||||||
<ProjectReference Include="..\HoneyBox.Infrastructure\HoneyBox.Infrastructure.csproj" />
|
<ProjectReference Include="..\HoneyBox.Infrastructure\HoneyBox.Infrastructure.csproj" />
|
||||||
|
<ProjectReference Include="..\HoneyBox.Admin.Business\HoneyBox.Admin.Business.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user