使用SAS在Blob存储中跨文件夹复制文件(基于Azure.Storage.Blobs)
实现方案:Blob内模板处理+结果上传(无本地下载)
核心思路
因为需要在Blob环境下完成邮件合并+结果存储,无法直接用Blob间的异步复制(中间需要文档加工),所以采用「内存流中转」的方式:读取模板Blob到内存流→SyncFusion处理→将处理后的流上传到目标Blob,全程不落地本地文件。
步骤1:配置依赖与权限
- 安装NuGet包:
Azure.Storage.Blobs(Blob存储操作)Syncfusion.DocIO.Net.Core(邮件合并,若为.NET Framework项目可替换为Syncfusion.DocIO.WinForms)
- 确保SAS URI拥有模板Blob的读取权限和结果Blob的写入权限,推荐使用容器级SAS(覆盖Templates和Results文件夹)。
步骤2:代码实现
初始化容器客户端
用SAS URI创建容器客户端:
using Azure.Storage.Blobs; using Syncfusion.DocIO.DLS; using System.IO; // 替换为你的容器级SAS URI string containerSasUri = "https://yourstorageaccount.blob.core.windows.net/yourcontainer?sv=2023-11-03&ss=b&srt=c&sp=rwdlacupiytfx&se=2024-12-31T23:59:59Z&st=2024-01-01T00:00:00Z&spr=https&sig=xxx"; BlobContainerClient containerClient = new BlobContainerClient(new Uri(containerSasUri));
读取模板Blob到内存流
// 模板Blob路径,示例:Templates/invoice-template.docx string templateBlobPath = "Templates/invoice-template.docx"; BlobClient templateBlobClient = containerClient.GetBlobClient(templateBlobPath); MemoryStream templateStream = new MemoryStream(); await templateBlobClient.DownloadToAsync(templateStream); templateStream.Position = 0; // 重置流指针,供SyncFusion读取
SyncFusion邮件合并处理
复用你本地已实现的邮件合并逻辑,直接基于内存流操作:
using WordDocument document = new WordDocument(templateStream, FormatType.Docx); // 替换为你的实际邮件合并数据源 var mergeData = new List<Dictionary<string, string>>() { new Dictionary<string, string>() { {"CustomerName", "张三"}, {"InvoiceNumber", "INV-2024-001"}, {"Amount", "1500.00"} } }; // 执行邮件合并 document.MailMerge.Execute(mergeData); // 将处理后的文档写入新内存流 MemoryStream resultStream = new MemoryStream(); document.Save(resultStream, FormatType.Docx); resultStream.Position = 0; // 重置流指针,供Blob上传使用
上传处理后的结果到Results文件夹
// 结果Blob路径,示例:Results/invoice-2024-001.docx string resultBlobPath = $"Results/invoice-{mergeData[0]["InvoiceNumber"]}.docx"; BlobClient resultBlobClient = containerClient.GetBlobClient(resultBlobPath); // 上传内存流到Blob await resultBlobClient.UploadAsync(resultStream, overwrite: true);
批量处理模板示例
如果需要处理Templates文件夹下的所有模板:
public async Task ProcessAllTemplates(string containerSasUri) { BlobContainerClient containerClient = new BlobContainerClient(new Uri(containerSasUri)); // 列出Templates文件夹下的所有文件Blob(跳过文件夹本身) await foreach (BlobItem blobItem in containerClient.GetBlobsAsync(prefix: "Templates/")) { if (blobItem.Name.EndsWith("/")) continue; string templateName = Path.GetFileName(blobItem.Name); // 读取模板流 MemoryStream templateStream = new MemoryStream(); await containerClient.GetBlobClient(blobItem.Name).DownloadToAsync(templateStream); templateStream.Position = 0; // SyncFusion邮件合并(替换为你的业务逻辑) using WordDocument document = new WordDocument(templateStream, FormatType.Docx); var mergeData = GetMergeData(templateName); // 自定义方法获取对应模板的数据源 document.MailMerge.Execute(mergeData); // 生成结果流 MemoryStream resultStream = new MemoryStream(); document.Save(resultStream, FormatType.Docx); resultStream.Position = 0; // 上传结果 string resultBlobPath = $"Results/{Path.GetFileNameWithoutExtension(templateName)}-{Guid.NewGuid()}.docx"; await containerClient.GetBlobClient(resultBlobPath).UploadAsync(resultStream, overwrite: true); // 清理流资源 templateStream.Dispose(); resultStream.Dispose(); } } // 自定义方法:根据模板名称返回对应数据源 private List<Dictionary<string, string>> GetMergeData(string templateName) { // 替换为你的实际数据逻辑 return new List<Dictionary<string, string>>() { new Dictionary<string, string>() { {"Field1", "Value1"}, {"Field2", "Value2"} } }; }
关键注意事项
- 流指针重置:每次读取或写入流后,必须将
Position设为0,否则后续操作会从流的末尾开始,导致读取空内容。 - SAS权限:确保SAS的
sp参数包含r(读取模板)和w(写入结果),若需列出Blob需额外添加l。 - 内存优化:处理大文档时,可使用临时
FileStream替代MemoryStream,避免内存溢出;也可结合SyncFusion的分块处理API。 - SyncFusion许可证:确保已配置有效许可证,避免处理后的文档出现水印。
内容的提问来源于stack exchange,提问作者user3792804
相关产品推荐
相关产品推荐

