using FoodLabeling.Application.Contracts.Dtos.Picture; using FoodLabeling.Application.Contracts.IServices; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Hosting; using Volo.Abp; using Volo.Abp.Application.Services; using Volo.Abp.Guids; namespace FoodLabeling.Application.Services; public class PictureAppService : ApplicationService, IPictureAppService { private const long MaxSizeBytes = 5 * 1024 * 1024; private static readonly HashSet AllowedExtensions = new(StringComparer.OrdinalIgnoreCase) { ".jpg", ".jpeg", ".png", ".webp", ".gif" }; private readonly IGuidGenerator _guidGenerator; private readonly IHostEnvironment _hostEnvironment; public PictureAppService(IGuidGenerator guidGenerator, IHostEnvironment hostEnvironment) { _guidGenerator = guidGenerator; _hostEnvironment = hostEnvironment; } /// /// 上传类别图片(保存到 /www/wwwroot/FoodLabelingManagementUs/picture) /// /// 返回的 Url 可直接保存到 CategoryPhotoUrl。 [HttpPost] [Consumes("multipart/form-data")] [Route("/api/app/picture/category/upload")] public async Task UploadCategoryAsync([FromForm] PictureUploadInputVo input) { if (input.File is null || input.File.Length <= 0) { throw new UserFriendlyException("请选择要上传的图片文件"); } if (input.File.Length > MaxSizeBytes) { throw new UserFriendlyException("图片大小不能超过5MB"); } var ext = Path.GetExtension(input.File.FileName ?? string.Empty); if (string.IsNullOrWhiteSpace(ext) || !AllowedExtensions.Contains(ext)) { throw new UserFriendlyException("仅支持上传 jpg/jpeg/png/webp/gif 格式图片"); } var subDir = NormalizeSubDir(input.SubDir); var saveRoot = ResolvePictureRoot(); var saveDir = string.IsNullOrWhiteSpace(subDir) ? saveRoot : Path.Combine(saveRoot, subDir); Directory.CreateDirectory(saveDir); var fileName = $"{DateTime.Now:yyyyMMddHHmmss}_{_guidGenerator.Create():N}{ext.ToLowerInvariant()}"; var savePath = Path.Combine(saveDir, fileName); await using (var stream = new FileStream(savePath, FileMode.CreateNew, FileAccess.Write, FileShare.None)) { await input.File.CopyToAsync(stream); } var url = BuildPictureUrl(subDir, fileName); return new PictureUploadOutputDto { Url = url, FileName = fileName, Size = input.File.Length }; } private string ResolvePictureRoot() { var linuxPictureRoot = "/www/wwwroot/FoodLabelingManagementUs/picture"; var webRootPicture = Path.Combine(_hostEnvironment.ContentRootPath, "wwwroot", "FoodLabelingManagementUs", "picture"); return Directory.Exists(linuxPictureRoot) ? linuxPictureRoot : webRootPicture; } private static string NormalizeSubDir(string? subDir) { if (string.IsNullOrWhiteSpace(subDir)) { return string.Empty; } var s = subDir.Trim().Replace('\\', '/'); while (s.StartsWith('/')) { s = s[1..]; } if (s.Contains("..", StringComparison.Ordinal)) { throw new UserFriendlyException("subDir 不能包含 .."); } return s; } private static string BuildPictureUrl(string? subDir, string fileName) { var s = string.IsNullOrWhiteSpace(subDir) ? string.Empty : $"/{subDir.Trim().Replace('\\', '/').Trim('/')}"; return $"/picture{s}/{fileName}"; } }