36 lines
1.4 KiB
C#
36 lines
1.4 KiB
C#
using AgriculturalPlatform.Api.Services;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
namespace AgriculturalPlatform.Api.Controllers;
|
|
|
|
/// <summary>
|
|
/// 附件读取代理:OSS 模式(私有桶)下前端通过 /api/files/{key} 访问图片,
|
|
/// 后端实时从 OSS 拉取并回传,避免将 Bucket 设为公共读而泄露身份证等敏感信息。
|
|
/// 本地存储模式不经过此接口(前端直接走静态文件 /uploads)。
|
|
/// </summary>
|
|
[ApiController]
|
|
[Route("api/files")]
|
|
[AllowAnonymous]
|
|
public class FilesController(IFileStorage storage) : ControllerBase
|
|
{
|
|
/// <summary>
|
|
/// 读取附件。key 形如 uploads/avatar/202608/xxx.png(必须限定 uploads/ 前缀,防止越权读取)。
|
|
/// 通过 FileStreamResult 边读边传,减少大图内存占用。
|
|
/// </summary>
|
|
[HttpGet("{**key}")]
|
|
[ResponseCache(Duration = 300)] // 5 分钟缓存,减少重复拉取
|
|
public async Task<IActionResult> Get(string key)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(key) || !key.StartsWith("uploads/"))
|
|
return BadRequest(new { message = "无效的文件路径" });
|
|
|
|
var file = await storage.OpenReadAsync(key);
|
|
if (file is null)
|
|
return NotFound(new { message = "文件不存在或已被删除" });
|
|
|
|
var (stream, contentType) = file.Value;
|
|
return File(stream, contentType, enableRangeProcessing: true);
|
|
}
|
|
}
|