using HoneyBox.Core.Interfaces; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace HoneyBox.Admin.Controllers; /// /// 文件上传控制器 /// [ApiController] [Route("api/admin/upload")] [Authorize] public class UploadController : ControllerBase { private readonly IImageUploadService _uploadService; private readonly ILogger _logger; private static readonly HashSet AllowedImageExtensions = new(StringComparer.OrdinalIgnoreCase) { ".jpg", ".jpeg", ".png", ".gif", ".webp", ".avif" }; private const long MaxFileSize = 20 * 1024 * 1024; // 20MB public UploadController(IImageUploadService uploadService, ILogger logger) { _uploadService = uploadService; _logger = logger; } /// /// 获取预签名上传URL(当前不支持直传,返回标识让前端降级) /// [HttpPost("presigned-url")] public IActionResult GetPresignedUrl([FromBody] PresignedUrlRequest request) { // 当前不支持直传,返回标识让前端降级到服务端上传 return Ok(new { code = 0, message = "success", data = new { supportsDirectUpload = false } }); } /// /// 上传图片(服务端上传) /// [HttpPost("image")] [RequestSizeLimit(MaxFileSize)] public async Task UploadImage(IFormFile file) { if (file == null || file.Length == 0) { return Ok(new { code = 1, message = "请选择要上传的图片" }); } if (file.Length > MaxFileSize) { return Ok(new { code = 1, message = "图片大小不能超过20MB" }); } var extension = Path.GetExtension(file.FileName); if (!AllowedImageExtensions.Contains(extension)) { return Ok(new { code = 1, message = "不支持的图片格式,仅支持 jpg/jpeg/png/gif/webp/avif" }); } try { using var stream = file.OpenReadStream(); var url = await _uploadService.UploadStreamAsync(stream, file.FileName, file.ContentType, "uploads"); if (!string.IsNullOrEmpty(url)) { return Ok(new { code = 0, message = "上传成功", data = new { url, fileName = file.FileName, fileSize = file.Length } }); } else { return Ok(new { code = 1, message = "上传失败" }); } } catch (Exception ex) { _logger.LogError(ex, "图片上传异常: {FileName}", file.FileName); return Ok(new { code = 1, message = "上传失败,请稍后重试" }); } } /// /// 批量上传图片 /// [HttpPost("images")] [RequestSizeLimit(MaxFileSize * 10)] public async Task UploadImages(List files) { if (files == null || files.Count == 0) { return Ok(new { code = 1, message = "请选择要上传的图片" }); } var results = new List(); var errors = new List(); foreach (var file in files) { if (file.Length > MaxFileSize) { errors.Add($"{file.FileName}: 文件大小超过限制"); continue; } var extension = Path.GetExtension(file.FileName); if (!AllowedImageExtensions.Contains(extension)) { errors.Add($"{file.FileName}: 不支持的图片格式"); continue; } try { using var stream = file.OpenReadStream(); var url = await _uploadService.UploadStreamAsync(stream, file.FileName, file.ContentType, "uploads"); if (!string.IsNullOrEmpty(url)) { results.Add(new { url, fileName = file.FileName, fileSize = file.Length }); } else { errors.Add($"{file.FileName}: 上传失败"); } } catch (Exception ex) { _logger.LogError(ex, "图片上传异常: {FileName}", file.FileName); errors.Add($"{file.FileName}: 上传异常"); } } if (results.Count == 0) { return Ok(new { code = 1, message = string.Join("; ", errors) }); } return Ok(new { code = 0, message = errors.Count > 0 ? $"部分上传成功,失败: {string.Join("; ", errors)}" : "上传成功", data = results }); } } /// /// 预签名URL请求 /// public class PresignedUrlRequest { public string FileName { get; set; } = string.Empty; public string ContentType { get; set; } = string.Empty; public long FileSize { get; set; } }