using System.Net.Mime; using File.Application.Features.Files.Queries; using MediatR; namespace BuildFlow.Host.Endpoints.File.Files; /// /// 文件预览端点。GET /api/files/preview,流式返回文件内容供内联展示(非下载)。 /// 后端从 S3 拉流透传,前端永远不接触 S3 URL 和凭据。 /// 鉴权:[Authorize] + JWT 中间件从 token query 参数读 JWT(img src / window.open 无法带 header)。 /// 该路径在 ApiResponseMiddlewareOptions.PassthroughPrefixes 注册,绕过响应包裹中间件的缓冲。 /// [Authorize] public class GetFilePreviewEndpoint(IMediator mediator) : Endpoint { public override void Configure() { Get("/files/preview"); Permissions("file:read"); } public override async Task HandleAsync(GetFilePreviewRequest req, CancellationToken ct) { // 复用下载查询(返回 Stream + ContentType + FileName) var result = await mediator.Send(new DownloadFileQuery(req.ObjectKey), ct); // inline = 浏览器内联展示(图片/PDF 直接渲染),而非触发下载 HttpContext.Response.ContentType = result.ContentType; var disposition = new ContentDisposition { FileName = result.FileName, Inline = true }; HttpContext.Response.Headers.ContentDisposition = disposition.ToString(); // 流式透传:S3 流 → HTTP 响应体 try { await result.Stream.CopyToAsync(HttpContext.Response.Body, ct); await HttpContext.Response.Body.FlushAsync(ct); } finally { await result.Stream.DisposeAsync(); } } }