From d089e6061d389cc21304b98ac61c8be86ff5e712 Mon Sep 17 00:00:00 2001 From: "code@server" Date: Thu, 15 Jan 2026 21:40:51 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=AD=98=E5=82=A8=E8=B7=AF=E5=BE=84=E5=92=8C?= =?UTF-8?q?URL=E5=89=8D=E7=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 FileStorageOptions 配置类 - 修改 FileUploadService 使用配置化路径 - 添加静态文件中间件支持文件访问 - 修复前端图片路径拼接问题 --- .../Configuration/FileStorageOptions.cs | 16 ++++++++++++++++ src/MilitaryTrainingManagement/Program.cs | 8 ++++++++ .../Implementations/FileUploadService.cs | 17 +++++++++++------ .../src/views/personnel/PersonnelDetail.vue | 4 ++-- .../src/views/personnel/PersonnelForm.vue | 2 +- 5 files changed, 38 insertions(+), 9 deletions(-) create mode 100644 src/MilitaryTrainingManagement/Configuration/FileStorageOptions.cs diff --git a/src/MilitaryTrainingManagement/Configuration/FileStorageOptions.cs b/src/MilitaryTrainingManagement/Configuration/FileStorageOptions.cs new file mode 100644 index 0000000..f863624 --- /dev/null +++ b/src/MilitaryTrainingManagement/Configuration/FileStorageOptions.cs @@ -0,0 +1,16 @@ +namespace MilitaryTrainingManagement.Configuration; + +public class FileStorageOptions +{ + public const string SectionName = "FileStorage"; + + /// + /// 文件存储基础路径(相对或绝对路径) + /// + public string BasePath { get; set; } = "./wwwroot/uploads"; + + /// + /// 访问文件的 URL 前缀 + /// + public string UrlPrefix { get; set; } = "/uploads"; +} diff --git a/src/MilitaryTrainingManagement/Program.cs b/src/MilitaryTrainingManagement/Program.cs index 494617f..787f3c3 100644 --- a/src/MilitaryTrainingManagement/Program.cs +++ b/src/MilitaryTrainingManagement/Program.cs @@ -7,6 +7,7 @@ using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; using MilitaryTrainingManagement.Authorization; +using MilitaryTrainingManagement.Configuration; using MilitaryTrainingManagement.Data; using MilitaryTrainingManagement.Models.Enums; using MilitaryTrainingManagement.Services.Implementations; @@ -14,6 +15,10 @@ using MilitaryTrainingManagement.Services.Interfaces; var builder = WebApplication.CreateBuilder(args); +// 配置文件存储选项 +builder.Services.Configure( + builder.Configuration.GetSection(FileStorageOptions.SectionName)); + // 添加HttpContextAccessor用于审计拦截器 builder.Services.AddHttpContextAccessor(); @@ -138,6 +143,9 @@ var app = builder.Build(); // 配置HTTP请求管道 +// 启用静态文件服务 +app.UseStaticFiles(); + app.UseSwagger(); app.UseSwaggerUI(); diff --git a/src/MilitaryTrainingManagement/Services/Implementations/FileUploadService.cs b/src/MilitaryTrainingManagement/Services/Implementations/FileUploadService.cs index 670f4a7..e146164 100644 --- a/src/MilitaryTrainingManagement/Services/Implementations/FileUploadService.cs +++ b/src/MilitaryTrainingManagement/Services/Implementations/FileUploadService.cs @@ -1,4 +1,6 @@ +using MilitaryTrainingManagement.Configuration; using MilitaryTrainingManagement.Services.Interfaces; +using Microsoft.Extensions.Options; using SixLabors.ImageSharp; namespace MilitaryTrainingManagement.Services.Implementations; @@ -8,8 +10,8 @@ namespace MilitaryTrainingManagement.Services.Implementations; /// public class FileUploadService : IFileUploadService { - private readonly IWebHostEnvironment _environment; private readonly ILogger _logger; + private readonly FileStorageOptions _storageOptions; // 照片尺寸限制(放宽限制以适应各种照片) private const int MinPhotoWidth = 100; @@ -22,9 +24,9 @@ public class FileUploadService : IFileUploadService private static readonly string[] AllowedPhotoExtensions = { ".jpg", ".jpeg", ".png" }; private static readonly string[] AllowedDocumentExtensions = { ".pdf", ".doc", ".docx", ".jpg", ".jpeg", ".png" }; - public FileUploadService(IWebHostEnvironment environment, ILogger logger) + public FileUploadService(IOptions storageOptions, ILogger logger) { - _environment = environment; + _storageOptions = storageOptions.Value; _logger = logger; } @@ -108,7 +110,7 @@ public class FileUploadService : IFileUploadService public string GetFullPath(string relativePath) { - return Path.Combine(_environment.WebRootPath ?? _environment.ContentRootPath, "uploads", relativePath); + return Path.Combine(_storageOptions.BasePath, relativePath); } private void ValidateDocument(IFormFile file) @@ -132,7 +134,7 @@ public class FileUploadService : IFileUploadService private async Task SaveFileAsync(IFormFile file, string subFolder) { - var uploadsPath = Path.Combine(_environment.WebRootPath ?? _environment.ContentRootPath, "uploads", subFolder); + var uploadsPath = Path.Combine(_storageOptions.BasePath, subFolder); Directory.CreateDirectory(uploadsPath); var fileName = $"{Guid.NewGuid()}{Path.GetExtension(file.FileName).ToLowerInvariant()}"; @@ -144,6 +146,9 @@ public class FileUploadService : IFileUploadService } _logger.LogInformation("文件已保存: {FilePath}", filePath); - return Path.Combine(subFolder, fileName); + + // 返回带 URL 前缀的完整访问路径 + var relativePath = Path.Combine(subFolder, fileName).Replace("\\", "/"); + return $"{_storageOptions.UrlPrefix.TrimEnd('/')}/{relativePath}"; } } diff --git a/src/frontend/src/views/personnel/PersonnelDetail.vue b/src/frontend/src/views/personnel/PersonnelDetail.vue index 0b9e0d2..eac469f 100644 --- a/src/frontend/src/views/personnel/PersonnelDetail.vue +++ b/src/frontend/src/views/personnel/PersonnelDetail.vue @@ -12,7 +12,7 @@
- +
暂无照片 @@ -64,7 +64,7 @@

佐证材料

- + 查看文件 diff --git a/src/frontend/src/views/personnel/PersonnelForm.vue b/src/frontend/src/views/personnel/PersonnelForm.vue index 20978a3..f5c45d9 100644 --- a/src/frontend/src/views/personnel/PersonnelForm.vue +++ b/src/frontend/src/views/personnel/PersonnelForm.vue @@ -312,7 +312,7 @@ async function loadPersonnel() { form.trainingParticipation = person.trainingParticipation || '' form.achievements = person.achievements || '' if (person.photoPath) { - photoPreview.value = `/api/files/${person.photoPath}` + photoPreview.value = person.photoPath } } catch { ElMessage.error('加载人才信息失败')