微信小程序上传头像

This commit is contained in:
马梓彭 2026-05-09 14:26:57 +08:00
parent 0e9ba53b76
commit 2a851d1525
2 changed files with 170 additions and 1 deletions

View File

@ -600,3 +600,14 @@ public class MyRegistrationDto
/// </summary>
public string ProjectNames { get; set; }
}
/// <summary>
/// 更新头像输出
/// </summary>
public class UpdateAvatarOutput
{
/// <summary>
/// 头像URL
/// </summary>
public string AvatarUrl { get; set; } = string.Empty;
}

View File

@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Mvc;
using YD_Event.Application.Client.Dtos;
using YD_Event.Application.Common;
using YD_Event.Core.Entities;
using YD_Event.Core.Enum;
using SqlSugar;
@ -8,6 +9,7 @@ using Microsoft.AspNetCore.Authorization;
using System.Security.Claims;
using Furion.FriendlyException;
using EasyCaching.Core;
using SkiaSharp;
using ProjectEntity = YD_Event.Core.Entities.Project;
namespace YD_Event.Application.Client;
@ -31,6 +33,7 @@ public class MyController : IDynamicApiController
private readonly ISqlSugarRepository<MerchantCertification> _merchantCertificationRepository;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IEasyCachingProvider _cachingProvider;
private readonly FileService _fileService;
public MyController(
ISqlSugarRepository<YD_Event.Core.Entities.AppUser> appUserRepository,
@ -44,7 +47,8 @@ public class MyController : IDynamicApiController
ISqlSugarRepository<EventInfo> eventRepository,
ISqlSugarRepository<MerchantCertification> merchantCertificationRepository,
IHttpContextAccessor httpContextAccessor,
IEasyCachingProvider cachingProvider)
IEasyCachingProvider cachingProvider,
FileService fileService)
{
_appUserRepository = appUserRepository;
_teamRepository = teamRepository;
@ -58,6 +62,7 @@ public class MyController : IDynamicApiController
_merchantCertificationRepository = merchantCertificationRepository;
_httpContextAccessor = httpContextAccessor;
_cachingProvider = cachingProvider;
_fileService = fileService;
}
private long GetCurrentUserId()
@ -658,4 +663,157 @@ public class MyController : IDynamicApiController
if (string.IsNullOrEmpty(mobile) || mobile.Length < 7) return mobile;
return mobile.Substring(0, 3) + "****" + mobile.Substring(7);
}
/// <summary>
/// 更新头像 - 上传图片文件
/// </summary>
/// <param name="file">头像图片文件</param>
/// <returns>更新后的头像信息</returns>
[HttpPost("avatar/upload")]
[DisplayName("上传头像")]
public async Task<UpdateAvatarOutput> UpdateAvatarByFile(IFormFile file)
{
if (file == null || file.Length == 0)
{
throw Oops.Bah("请上传头像图片");
}
// 验证文件类型
var allowedExtensions = new[] { ".jpg", ".jpeg", ".png", ".gif", ".bmp" };
var extension = Path.GetExtension(file.FileName).ToLower();
if (!allowedExtensions.Contains(extension))
{
throw Oops.Bah("仅支持 JPG、JPEG、PNG、GIF、BMP 格式的图片");
}
// 验证文件大小 (最大 5MB)
if (file.Length > 5 * 1024 * 1024)
{
throw Oops.Bah("图片大小不能超过 5MB");
}
// 处理图片
using var processedStream = await ProcessAvatarImage(file.OpenReadStream());
// 上传处理后的图片
var now = DateTime.Now;
var fileName = $"avatar/{now.Year}/{now.Month:D2}/{now.Day:D2}/{Yitter.IdGenerator.YitIdHelper.NextId()}.jpg";
var avatarUrl = await _fileService.UploadFileStreamAsync(fileName, processedStream);
// 更新用户头像
var userId = GetCurrentUserId();
var user = await _appUserRepository.GetFirstAsync(u => u.Id == userId);
if (user == null)
{
throw Oops.Bah("用户信息不存在");
}
user.Avatar = avatarUrl;
user.UpdatedTime = DateTime.Now;
await _appUserRepository.UpdateAsync(user);
return new UpdateAvatarOutput
{
AvatarUrl = avatarUrl
};
}
/// <summary>
/// 更新头像 - 使用图片URL
/// </summary>
/// <param name="avatarUrl">头像图片URL</param>
/// <returns>更新后的头像信息</returns>
[HttpPost("avatar/url")]
[DisplayName("更新头像URL")]
public async Task<UpdateAvatarOutput> UpdateAvatarByUrl([FromBody] string avatarUrl)
{
if (string.IsNullOrWhiteSpace(avatarUrl))
{
throw Oops.Bah("头像URL不能为空");
}
// 验证URL格式
if (!Uri.TryCreate(avatarUrl, UriKind.Absolute, out _))
{
throw Oops.Bah("头像URL格式不正确");
}
// 更新用户头像
var userId = GetCurrentUserId();
var user = await _appUserRepository.GetFirstAsync(u => u.Id == userId);
if (user == null)
{
throw Oops.Bah("用户信息不存在");
}
user.Avatar = avatarUrl;
user.UpdatedTime = DateTime.Now;
await _appUserRepository.UpdateAsync(user);
return new UpdateAvatarOutput
{
AvatarUrl = avatarUrl
};
}
/// <summary>
/// 处理头像图片 - 压缩和格式转换
/// </summary>
/// <param name="inputStream">原始图片流</param>
/// <returns>处理后的图片流</returns>
private async Task<Stream> ProcessAvatarImage(Stream inputStream)
{
// 将流复制到内存流,以便可以多次读取
var memoryStream = new MemoryStream();
await inputStream.CopyToAsync(memoryStream);
memoryStream.Position = 0;
// 使用 SkiaSharp 处理图片
using var originalBitmap = SKBitmap.Decode(memoryStream);
if (originalBitmap == null)
{
throw Oops.Bah("图片解码失败,请检查图片格式");
}
// 计算缩放后的尺寸(最大 200x200)
const int maxSize = 200;
int width = originalBitmap.Width;
int height = originalBitmap.Height;
if (width > maxSize || height > maxSize)
{
if (width > height)
{
height = (int)(height * (double)maxSize / width);
width = maxSize;
}
else
{
width = (int)(width * (double)maxSize / height);
height = maxSize;
}
}
// 创建缩放后的图片
using var scaledBitmap = originalBitmap.Resize(new SKImageInfo(width, height), SKFilterQuality.High);
// 裁剪为正方形(从中心裁剪)
int cropSize = Math.Min(width, height);
int cropX = (width - cropSize) / 2;
int cropY = (height - cropSize) / 2;
using var croppedBitmap = new SKBitmap(cropSize, cropSize);
using var canvas = new SKCanvas(croppedBitmap);
canvas.Clear(SKColors.Transparent);
canvas.DrawBitmap(scaledBitmap, new SKRect(cropX, cropY, cropX + cropSize, cropY + cropSize), new SKRect(0, 0, cropSize, cropSize));
// 转换为 JPG 格式并压缩
var outputStream = new MemoryStream();
using var image = SKImage.FromBitmap(croppedBitmap);
using var data = image.Encode(SKEncodedImageFormat.Jpeg, 80); // 80% 质量
data.SaveTo(outputStream);
outputStream.Position = 0;
return outputStream;
}
}