微信小程序上传头像
This commit is contained in:
parent
0e9ba53b76
commit
2a851d1525
@ -600,3 +600,14 @@ public class MyRegistrationDto
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string ProjectNames { get; set; }
|
public string ProjectNames { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新头像输出
|
||||||
|
/// </summary>
|
||||||
|
public class UpdateAvatarOutput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 头像URL
|
||||||
|
/// </summary>
|
||||||
|
public string AvatarUrl { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using YD_Event.Application.Client.Dtos;
|
using YD_Event.Application.Client.Dtos;
|
||||||
|
using YD_Event.Application.Common;
|
||||||
using YD_Event.Core.Entities;
|
using YD_Event.Core.Entities;
|
||||||
using YD_Event.Core.Enum;
|
using YD_Event.Core.Enum;
|
||||||
using SqlSugar;
|
using SqlSugar;
|
||||||
@ -8,6 +9,7 @@ using Microsoft.AspNetCore.Authorization;
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Furion.FriendlyException;
|
using Furion.FriendlyException;
|
||||||
using EasyCaching.Core;
|
using EasyCaching.Core;
|
||||||
|
using SkiaSharp;
|
||||||
using ProjectEntity = YD_Event.Core.Entities.Project;
|
using ProjectEntity = YD_Event.Core.Entities.Project;
|
||||||
|
|
||||||
namespace YD_Event.Application.Client;
|
namespace YD_Event.Application.Client;
|
||||||
@ -31,6 +33,7 @@ public class MyController : IDynamicApiController
|
|||||||
private readonly ISqlSugarRepository<MerchantCertification> _merchantCertificationRepository;
|
private readonly ISqlSugarRepository<MerchantCertification> _merchantCertificationRepository;
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
private readonly IEasyCachingProvider _cachingProvider;
|
private readonly IEasyCachingProvider _cachingProvider;
|
||||||
|
private readonly FileService _fileService;
|
||||||
|
|
||||||
public MyController(
|
public MyController(
|
||||||
ISqlSugarRepository<YD_Event.Core.Entities.AppUser> appUserRepository,
|
ISqlSugarRepository<YD_Event.Core.Entities.AppUser> appUserRepository,
|
||||||
@ -44,7 +47,8 @@ public class MyController : IDynamicApiController
|
|||||||
ISqlSugarRepository<EventInfo> eventRepository,
|
ISqlSugarRepository<EventInfo> eventRepository,
|
||||||
ISqlSugarRepository<MerchantCertification> merchantCertificationRepository,
|
ISqlSugarRepository<MerchantCertification> merchantCertificationRepository,
|
||||||
IHttpContextAccessor httpContextAccessor,
|
IHttpContextAccessor httpContextAccessor,
|
||||||
IEasyCachingProvider cachingProvider)
|
IEasyCachingProvider cachingProvider,
|
||||||
|
FileService fileService)
|
||||||
{
|
{
|
||||||
_appUserRepository = appUserRepository;
|
_appUserRepository = appUserRepository;
|
||||||
_teamRepository = teamRepository;
|
_teamRepository = teamRepository;
|
||||||
@ -58,6 +62,7 @@ public class MyController : IDynamicApiController
|
|||||||
_merchantCertificationRepository = merchantCertificationRepository;
|
_merchantCertificationRepository = merchantCertificationRepository;
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
_cachingProvider = cachingProvider;
|
_cachingProvider = cachingProvider;
|
||||||
|
_fileService = fileService;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long GetCurrentUserId()
|
private long GetCurrentUserId()
|
||||||
@ -658,4 +663,157 @@ public class MyController : IDynamicApiController
|
|||||||
if (string.IsNullOrEmpty(mobile) || mobile.Length < 7) return mobile;
|
if (string.IsNullOrEmpty(mobile) || mobile.Length < 7) return mobile;
|
||||||
return mobile.Substring(0, 3) + "****" + mobile.Substring(7);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user