如何用C# ASP.NET将Azure Blob存储的PDF合并后供用户下载
方案核心思路
核心原则为全程流式处理避免全量加载PDF到Web应用内存,尽可能降低服务器资源占用,合并过程无需落地本地磁盘。整体执行流程为:接收用户提交的PDF文件排序规则→按顺序从Azure Blob Storage拉取对应文件的只读流→调用PDF处理库逐页合并流→直接将合并后的输出流返回给前端供用户下载。
关键实现步骤
- 第一步:选择适配流式处理的PDF操作库,推荐选用MIT协议的
PdfSharp-MigraDoc,或者功能更完善的iText 7,两者均为.NET生态成熟的PDF处理工具,支持直接从流读取、合并文件,无需生成临时本地文件。 - 第二步:按用户指定顺序拉取Blob流,调用Azure Blob SDK的
OpenReadAsync方法获取只读流,不要使用全量加载到内存的API,参考代码如下:
// 初始化Blob容器客户端 var blobServiceClient = new BlobServiceClient("Azure Blob存储连接字符串"); var containerClient = blobServiceClient.GetBlobContainerClient("存储PDF的容器名称"); List<Stream> orderedBlobStreams = new List<Stream>(); // userOrderedBlobList为前端提交的、用户排序后的PDF对应的Blob名称列表 foreach (var blobName in userOrderedBlobList) { var blobClient = containerClient.GetBlobClient(blobName); // 直接获取流,不加载全量数据到内存 var blobStream = await blobClient.OpenReadAsync(); orderedBlobStreams.Add(blobStream); }
- 第三步:流式合并PDF,以iText 7为例的参考代码如下:
using var mergedPdfStream = new MemoryStream(); var writer = new PdfWriter(mergedPdfStream); var targetPdfDoc = new PdfDocument(writer); targetPdfDoc.SetDefaultPageSize(PageSize.A4); foreach (var blobStream in orderedBlobStreams) { using var sourcePdfDoc = new PdfDocument(new PdfReader(blobStream)); // 把源PDF的所有页拷贝到目标文档的末尾 sourcePdfDoc.CopyPagesTo(1, sourcePdfDoc.GetNumberOfPages(), targetPdfDoc); await blobStream.DisposeAsync(); } targetPdfDoc.Close(); // 复位流指针到起始位置,才能正常返回给前端 mergedPdfStream.Position = 0;
- 第四步:直接返回流供用户下载,ASP.NET Core中直接返回FileResult即可,无需额外存储文件:
return File(mergedPdfStream, "application/pdf", "合并后文件的自定义名称.pdf");
优化建议
- 如果业务场景中经常出现合并超过10个、单文件大于100M的PDF的需求,可以把合并逻辑拆分到Azure Function中异步处理,Web应用仅负责提交合并任务,任务完成后通过站内信、邮件通知用户,或者生成临时下载链接,避免Web请求超时。
- 接口层提前校验用户提交的Blob列表是否存在、是否为合法PDF格式,避免合并过程中报错。
- 合并完成后及时手动释放所有Blob流、PDF对象的占用,避免内存泄漏。
注意事项
- iText 7使用AGPL协议,商业项目如果不希望开源相关代码需要购买商用授权,中小项目优先选择MIT协议的PdfSharp更合适。
- 同步合并接口需要设置合理的超时时间,大体积PDF合并优先采用异步任务模式。
内容的提问来源于stack exchange,提问作者Wallstreetguy
相关产品推荐
相关产品推荐

