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
-
-
- 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);
}
}