From 386280a4c49c6d8efe41aa00e1b79ad549b632d7 Mon Sep 17 00:00:00 2001 From: xiangning Date: Thu, 2 Jul 2026 17:58:52 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E5=AE=8C=E5=96=84=20.gitignore=20+=20?= =?UTF-8?q?=E6=B8=85=E7=90=86=20IDE/=E6=9E=84=E5=BB=BA=E4=BA=A7=E7=89=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - .gitignore 补充 .idea/.vscode/.vs/ 等 IDE 目录 - 从 git 移除已误提交的 .idea/ 目录 - 移除 bin/obj 缓存 --- .gitignore | 22 +- .idea/.idea.Admin/.idea/.name | 1 - .idea/.idea.Admin/.idea/workspace.xml | 272 ------------------ .idea/.idea.BuildFlow/.idea/.gitignore | 15 - .idea/.idea.BuildFlow/.idea/.name | 1 - .idea/.idea.BuildFlow/.idea/encodings.xml | 4 - .idea/.idea.BuildFlow/.idea/indexLayout.xml | 8 - .idea/.idea.BuildFlow/.idea/vcs.xml | 7 - .idea/.idea.build-flow/.idea/.gitignore | 15 - .idea/.idea.build-flow/.idea/.name | 1 - .idea/.idea.build-flow/.idea/encodings.xml | 4 - .idea/.idea.build-flow/.idea/indexLayout.xml | 8 - .idea/.idea.build-flow/.idea/vcs.xml | 6 - .../file/Files/DownloadFileEndpoint.cs | 30 +- src/BuildFlow.Host/Hubs/NotificationHub.cs | 6 +- .../Features/Files/DTOs/DownloadFileResult.cs | 8 +- .../Handlers/DownloadFileQueryHandler.cs | 16 +- 17 files changed, 65 insertions(+), 359 deletions(-) delete mode 100644 .idea/.idea.Admin/.idea/.name delete mode 100644 .idea/.idea.Admin/.idea/workspace.xml delete mode 100644 .idea/.idea.BuildFlow/.idea/.gitignore delete mode 100644 .idea/.idea.BuildFlow/.idea/.name delete mode 100644 .idea/.idea.BuildFlow/.idea/encodings.xml delete mode 100644 .idea/.idea.BuildFlow/.idea/indexLayout.xml delete mode 100644 .idea/.idea.BuildFlow/.idea/vcs.xml delete mode 100644 .idea/.idea.build-flow/.idea/.gitignore delete mode 100644 .idea/.idea.build-flow/.idea/.name delete mode 100644 .idea/.idea.build-flow/.idea/encodings.xml delete mode 100644 .idea/.idea.build-flow/.idea/indexLayout.xml delete mode 100644 .idea/.idea.build-flow/.idea/vcs.xml diff --git a/.gitignore b/.gitignore index c3628a0..632750f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,32 @@ +# ===== .NET 构建产物 ===== **/bin/ **/obj/ *.user +*.suo +*.userosscache +*.userprefs +*.lscache + +# ===== IDE / 编辑器 ===== +.idea/ +.vscode/ +.vs/ +*.swp +*.swo +*~ + +# ===== 敏感配置 ===== .env .env.* !.env.example **/appsettings.Development.json **/appsettings.Production.json -*.lscache +**/secrets.json + +# ===== 前端 ===== node_modules/ dist/ + +# ===== 系统 ===== .DS_Store +Thumbs.db diff --git a/.idea/.idea.Admin/.idea/.name b/.idea/.idea.Admin/.idea/.name deleted file mode 100644 index 9d1e34a..0000000 --- a/.idea/.idea.Admin/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -Admin \ No newline at end of file diff --git a/.idea/.idea.Admin/.idea/workspace.xml b/.idea/.idea.Admin/.idea/workspace.xml deleted file mode 100644 index 5d1e4b8..0000000 --- a/.idea/.idea.Admin/.idea/workspace.xml +++ /dev/null @@ -1,272 +0,0 @@ - - - - src/Admin/Admin.csproj - src/modules/file/Api/FileSystem.Api.csproj - src/modules/im/Api/IM.Api.csproj - src/modules/monitor/Monitor.Api/Monitor.Api.csproj - src/modules/order/Order.Api/Order.Api.csproj - src/modules/rag/Api/RAG.Api.csproj - src/modules/sso/Api/SSO.Api.csproj - src/modules/sso/Api/SSO.Api.csproj - src/modules/workflow/Api/Workflow.Api.csproj - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { - "associatedIndex": 1 -} - - - - { - "keyToString": { - "ModuleVcsDetector.initialDetectionPerformed": "true", - "RunOnceActivity.ShowReadmeOnStart": "true", - "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true", - "RunOnceActivity.cidr.known.project.marker": "true", - "RunOnceActivity.readMode.enableVisualFormatting": "true", - "RunOnceActivity.typescript.service.memoryLimit.init": "true", - "cidr.known.project.marker": "true", - "codeWithMe.voiceChat.enabledByDefault": "false", - "last_opened_file_path": "/Users/wen/project/rag/admin/Admin.slnx", - "vue.rearranger.settings.migration": "true" - } -} - - - - - - - - - - - - - - - - - - - - - - - - 1782566787625 - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/.idea.BuildFlow/.idea/.gitignore b/.idea/.idea.BuildFlow/.idea/.gitignore deleted file mode 100644 index d17c24d..0000000 --- a/.idea/.idea.BuildFlow/.idea/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -# 默认忽略的文件 -/shelf/ -/workspace.xml -# Rider 忽略的文件 -/projectSettingsUpdater.xml -/contentModel.xml -/modules.xml -/.idea.BuildFlow.iml -# 基于编辑器的 HTTP 客户端请求 -/httpRequests/ -# 已忽略包含查询文件的默认文件夹 -/queries/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/.idea.BuildFlow/.idea/.name b/.idea/.idea.BuildFlow/.idea/.name deleted file mode 100644 index 3cde0be..0000000 --- a/.idea/.idea.BuildFlow/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -BuildFlow \ No newline at end of file diff --git a/.idea/.idea.BuildFlow/.idea/encodings.xml b/.idea/.idea.BuildFlow/.idea/encodings.xml deleted file mode 100644 index df87cf9..0000000 --- a/.idea/.idea.BuildFlow/.idea/encodings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/.idea.BuildFlow/.idea/indexLayout.xml b/.idea/.idea.BuildFlow/.idea/indexLayout.xml deleted file mode 100644 index 7b08163..0000000 --- a/.idea/.idea.BuildFlow/.idea/indexLayout.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/.idea.BuildFlow/.idea/vcs.xml b/.idea/.idea.BuildFlow/.idea/vcs.xml deleted file mode 100644 index 288b36b..0000000 --- a/.idea/.idea.BuildFlow/.idea/vcs.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/.idea.build-flow/.idea/.gitignore b/.idea/.idea.build-flow/.idea/.gitignore deleted file mode 100644 index e59b6b7..0000000 --- a/.idea/.idea.build-flow/.idea/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -# 默认忽略的文件 -/shelf/ -/workspace.xml -# Rider 忽略的文件 -/contentModel.xml -/modules.xml -/.idea.build-flow.iml -/projectSettingsUpdater.xml -# 基于编辑器的 HTTP 客户端请求 -/httpRequests/ -# 已忽略包含查询文件的默认文件夹 -/queries/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/.idea.build-flow/.idea/.name b/.idea/.idea.build-flow/.idea/.name deleted file mode 100644 index b8109fc..0000000 --- a/.idea/.idea.build-flow/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -build-flow \ No newline at end of file diff --git a/.idea/.idea.build-flow/.idea/encodings.xml b/.idea/.idea.build-flow/.idea/encodings.xml deleted file mode 100644 index df87cf9..0000000 --- a/.idea/.idea.build-flow/.idea/encodings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/.idea.build-flow/.idea/indexLayout.xml b/.idea/.idea.build-flow/.idea/indexLayout.xml deleted file mode 100644 index 7b08163..0000000 --- a/.idea/.idea.build-flow/.idea/indexLayout.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/.idea.build-flow/.idea/vcs.xml b/.idea/.idea.build-flow/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/.idea.build-flow/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/BuildFlow.Host/Endpoints/file/Files/DownloadFileEndpoint.cs b/src/BuildFlow.Host/Endpoints/file/Files/DownloadFileEndpoint.cs index dd75937..27c43d2 100644 --- a/src/BuildFlow.Host/Endpoints/file/Files/DownloadFileEndpoint.cs +++ b/src/BuildFlow.Host/Endpoints/file/Files/DownloadFileEndpoint.cs @@ -1,18 +1,38 @@ -using File.Application.Features.Files.DTOs; +using System.Net.Mime; using File.Application.Features.Files.Queries; using MediatR; namespace BuildFlow.Host.Endpoints.file.Files; - -/// 下载文件端点。GET /api/files/download,按对象键返回流数据。 -public class DownloadFileEndpoint(IMediator mediator) : Endpoint +/// +/// 下载文件端点。GET /api/files/download,按对象键流式返回文件内容。 +/// 直接把 S3 响应流透传到 HTTP 响应体,整文件不进内存;Content-Disposition 触发浏览器下载。 +/// 该路径已在 ApiResponseMiddlewareOptions.PassthroughPrefixes 注册,绕过响应包裹中间件的缓冲。 +/// +[Authorize] +public class DownloadFileEndpoint(IMediator mediator) : Endpoint { public override void Configure() => Get("/files/download"); public override async Task HandleAsync(DownloadFileRequest req, CancellationToken ct) { var result = await mediator.Send(new DownloadFileQuery(req.BucketName, req.ObjectKey), ct); - await Send.OkAsync(result, ct); + + // 设置响应头:真实 ContentType + 触发下载的文件名 + HttpContext.Response.StatusCode = 200; + HttpContext.Response.ContentType = result.ContentType; + var disposition = new ContentDisposition { FileName = result.FileName, Inline = false }; + 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(); + } } } diff --git a/src/BuildFlow.Host/Hubs/NotificationHub.cs b/src/BuildFlow.Host/Hubs/NotificationHub.cs index 58df6f2..90c031c 100644 --- a/src/BuildFlow.Host/Hubs/NotificationHub.cs +++ b/src/BuildFlow.Host/Hubs/NotificationHub.cs @@ -24,11 +24,11 @@ public class NotificationHub : Hub public static string UserGroup(Guid userId) => $"user:{userId}"; /// 从 ClaimsPrincipal 解析用户 Id(sub claim)。 - internal static Guid? ResolveUserId(ClaimsPrincipal? user) + private static Guid? ResolveUserId(ClaimsPrincipal? user) { if (user?.Identity?.IsAuthenticated != true) return null; var sub = user.FindFirstValue(JwtRegisteredClaimNames.Sub) - ?? user.FindFirstValue(ClaimTypes.NameIdentifier); + ?? user.FindFirstValue(ClaimTypes.NameIdentifier); return Guid.TryParse(sub, out var id) ? id : null; } -} +} \ No newline at end of file diff --git a/src/modules/file/Application/Features/Files/DTOs/DownloadFileResult.cs b/src/modules/file/Application/Features/Files/DTOs/DownloadFileResult.cs index 8ed1b75..17f9170 100644 --- a/src/modules/file/Application/Features/Files/DTOs/DownloadFileResult.cs +++ b/src/modules/file/Application/Features/Files/DTOs/DownloadFileResult.cs @@ -2,5 +2,9 @@ namespace File.Application.Features.Files.DTOs; -/// 文件下载结果 DTO(字节流、ContentType、文件名)。 -public record DownloadFileResult(byte[] Data, string ContentType, string FileName); +/// +/// 文件下载结果 DTO(流、ContentType、文件名)。 +/// 持有 S3 返回的原始 Stream,由 Endpoint 直接透传到响应输出流,避免整文件进内存。 +/// 调用方负责在使用后 Dispose Stream。 +/// +public record DownloadFileResult(Stream Stream, string ContentType, string FileName); diff --git a/src/modules/file/Application/Features/Files/Handlers/DownloadFileQueryHandler.cs b/src/modules/file/Application/Features/Files/Handlers/DownloadFileQueryHandler.cs index ba63bb4..e6ab63b 100644 --- a/src/modules/file/Application/Features/Files/Handlers/DownloadFileQueryHandler.cs +++ b/src/modules/file/Application/Features/Files/Handlers/DownloadFileQueryHandler.cs @@ -5,15 +5,19 @@ using MediatR; namespace File.Application.Features.Files.Handlers; -/// 下载文件查询处理器。从 S3 拉流并拷贝到内存返回。 +/// +/// 下载文件查询处理器。从 S3 拉取响应流,原样透传给 Endpoint 写入 HTTP 响应体, +/// 不再拷贝到内存,大文件零内存压力。真实 ContentType 由 S3 HeadObject/GetObject 返回。 +/// public class DownloadFileQueryHandler(IS3StorageService s3) : IRequestHandler { public async Task Handle(DownloadFileQuery req, CancellationToken ct) { - var (stream, _) = await s3.DownloadFileAsync(req.BucketName, req.ObjectKey, ct); - using var ms = new MemoryStream(); - await stream.CopyToAsync(ms, ct); - await stream.DisposeAsync(); - return new DownloadFileResult(ms.ToArray(), "application/octet-stream", req.ObjectKey); + var (stream, contentType) = await s3.DownloadFileAsync(req.BucketName, req.ObjectKey, ct); + // 文件名取 ObjectKey 最后一段(S3 key 可能含目录前缀) + var fileName = req.ObjectKey.Contains('/') + ? req.ObjectKey[(req.ObjectKey.LastIndexOf('/') + 1)..] + : req.ObjectKey; + return new DownloadFileResult(stream, contentType, fileName); } }