feat: 支持配置文件存储路径和URL前缀
- 新增 FileStorageOptions 配置类 - 修改 FileUploadService 使用配置化路径 - 添加静态文件中间件支持文件访问 - 修复前端图片路径拼接问题
This commit is contained in:
parent
29cfc4d700
commit
d089e6061d
|
|
@ -0,0 +1,16 @@
|
|||
namespace MilitaryTrainingManagement.Configuration;
|
||||
|
||||
public class FileStorageOptions
|
||||
{
|
||||
public const string SectionName = "FileStorage";
|
||||
|
||||
/// <summary>
|
||||
/// 文件存储基础路径(相对或绝对路径)
|
||||
/// </summary>
|
||||
public string BasePath { get; set; } = "./wwwroot/uploads";
|
||||
|
||||
/// <summary>
|
||||
/// 访问文件的 URL 前缀
|
||||
/// </summary>
|
||||
public string UrlPrefix { get; set; } = "/uploads";
|
||||
}
|
||||
|
|
@ -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<FileStorageOptions>(
|
||||
builder.Configuration.GetSection(FileStorageOptions.SectionName));
|
||||
|
||||
// 添加HttpContextAccessor用于审计拦截器
|
||||
builder.Services.AddHttpContextAccessor();
|
||||
|
||||
|
|
@ -138,6 +143,9 @@ var app = builder.Build();
|
|||
|
||||
// 配置HTTP请求管道
|
||||
|
||||
// 启用静态文件服务
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
|||
/// </summary>
|
||||
public class FileUploadService : IFileUploadService
|
||||
{
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
private readonly ILogger<FileUploadService> _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<FileUploadService> logger)
|
||||
public FileUploadService(IOptions<FileStorageOptions> storageOptions, ILogger<FileUploadService> 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<string> 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}";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<el-row :gutter="40">
|
||||
<el-col :span="6">
|
||||
<div class="photo-section">
|
||||
<img v-if="person.photoPath" :src="`/api/files/${person.photoPath}`" class="person-photo" />
|
||||
<img v-if="person.photoPath" :src="person.photoPath" class="person-photo" />
|
||||
<div v-else class="no-photo">
|
||||
<el-icon :size="48"><User /></el-icon>
|
||||
<span>暂无照片</span>
|
||||
|
|
@ -64,7 +64,7 @@
|
|||
|
||||
<div v-if="person.supportingDocuments" class="documents-section">
|
||||
<h4>佐证材料</h4>
|
||||
<el-link type="primary" :href="`/api/files/${person.supportingDocuments}`" target="_blank">
|
||||
<el-link type="primary" :href="person.supportingDocuments" target="_blank">
|
||||
<el-icon><Document /></el-icon>
|
||||
查看文件
|
||||
</el-link>
|
||||
|
|
|
|||
|
|
@ -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('加载人才信息失败')
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user