From 40997f7d7118e5034380053f940920343562ec62 Mon Sep 17 00:00:00 2001 From: zzh <1368735060@qq.com> Date: Fri, 11 Sep 2026 19:14:26 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20/file-storage/stre?= =?UTF-8?q?am=20=E4=BE=9B=E9=9D=A2=E5=8D=95=E9=A2=84=E8=A7=88=E9=89=B4?= =?UTF-8?q?=E6=9D=83=E8=AF=BB=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- .../Services/Storage/FileStorageService.cs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/Oms.Application/Services/Storage/FileStorageService.cs b/src/Oms.Application/Services/Storage/FileStorageService.cs index f2d91b0..016b3b2 100644 --- a/src/Oms.Application/Services/Storage/FileStorageService.cs +++ b/src/Oms.Application/Services/Storage/FileStorageService.cs @@ -108,6 +108,55 @@ public class FileStorageService : ApplicationService var key = FileStorage.ToStorageKey(input.PathOrUrl); return new NormalizeKeyOutput { Key = key }; } + + /// + /// 按 key / URL 流式读取文件(供面单预览等场景,避免前端跨域 iframe 空白)。 + /// + [HttpGet("/file-storage/stream")] + public async Task StreamAsync([FromQuery] string key, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(key)) + { + throw new BusinessException("key 不能为空"); + } + + string storageKey; + try + { + storageKey = FileStorage.ToStorageKey(key) ?? FileStoragePath.NormalizeKey(key); + } + catch (Exception) + { + throw new BusinessException("无法解析文件 key"); + } + + if (!await FileStorage.ExistsAsync(storageKey, cancellationToken)) + { + throw new BusinessException("文件不存在或已过期"); + } + + var stream = await FileStorage.OpenReadAsync(storageKey, cancellationToken); + return new FileStreamResult(stream, GuessStreamContentType(storageKey)) + { + EnableRangeProcessing = true, + }; + } + + private static string GuessStreamContentType(string storageKey) + { + if (storageKey.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase)) + { + return "application/pdf"; + } + + if (storageKey.EndsWith(".html", StringComparison.OrdinalIgnoreCase) + || storageKey.EndsWith(".htm", StringComparison.OrdinalIgnoreCase)) + { + return "text/html; charset=utf-8"; + } + + return "application/octet-stream"; + } } public class PresignUploadInput