流和文件附件
流和文件附件
超越基本的 Document.Open/Document.Save 重载,Aspose.PDF FOSS for .NET 为您提供更精细的字节在文档中进出控制 —— 可增长的内存缓冲区用于非常大的文件,以及专用的 API 用于通过 FileSpecification 和 EmbeddedFileCollection 嵌入、检查和提取文件附件。
在内存缓冲区中增长至超过 2 GB
MemoryStream 将其数据存储在单个连续数组中,容量约为 2 GB 左右。OptimizedMemoryStream 则以固定大小的块存储数据,因此在完全在内存中保存或生成 PDF 时,它可以容纳更大的文档。
using Aspose.Pdf;
using var doc = Document.Open("large-input.pdf");
using var buffer = new OptimizedMemoryStream();
doc.Save(buffer);
buffer.Seek(0, SeekOrigin.Begin);
byte[] allBytes = buffer.ToArray();
Console.WriteLine($"Buffer length: {buffer.Length}");您也可以直接写入它,方式与对任何 Stream 相同:
using var buffer = new OptimizedMemoryStream();
byte[] chunk = System.Text.Encoding.UTF8.GetBytes("raw content chunk");
buffer.Write(chunk, 0, chunk.Length);
buffer.WriteByte(0x0A);
buffer.SetLength(buffer.Length);直接从流嵌入文件
FileSpecification 可以包装一个 Stream,而不必先将附件缓冲到 byte[] 中。这在附件来源本身是流时非常有用,例如打开的文件句柄。
using Aspose.Pdf;
using var doc = Document.Open("input.pdf");
using var attachmentStream = File.OpenRead("report.csv");
using var spec = new FileSpecification(attachmentStream, "report.csv", "Quarterly report data");
spec.MimeType = "text/csv";
spec.AFRelationship = AFRelationship.Data;
spec.Encoding = FileEncoding.Zip;
doc.EmbeddedFiles.Add(spec);
doc.Save("with-attachment.pdf");从原始字节嵌入文件
Document.AddEmbeddedFile 是一种便利方法,可直接从字节数组嵌入文件,而无需直接构造 FileSpecification。
using Aspose.Pdf;
using var doc = Document.Open("input.pdf");
byte[] fileData = File.ReadAllBytes("appendix.docx");
doc.AddEmbeddedFile(
"appendix.docx",
fileData,
"Supporting appendix",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
true,
DateTime.Now,
DateTime.Now);
doc.Save("with-appendix.pdf");枚举并提取嵌入的文件
Document.EmbeddedFiles 是一个可以枚举的 EmbeddedFileCollection。每个条目都是一个 FileSpecification,GetData() 返回附件的原始字节。
using Aspose.Pdf;
using var doc = Document.Open("with-attachment.pdf");
if (doc.HasEmbeddedFiles)
{
foreach (FileSpecification file in doc.EmbeddedFiles)
{
Console.WriteLine($"{file.Name} ({file.Size} bytes, {file.MimeType})");
byte[] contents = file.GetData();
File.WriteAllBytes(file.Name, contents);
}
}查找、检查和删除附件
使用 FindByName 查找单个附件,使用 Params 读取其元数据,使用 Delete/DeleteByKey 将其删除。
using Aspose.Pdf;
using var doc = Document.Open("with-attachment.pdf");
FileSpecification? found = doc.EmbeddedFiles.FindByName("report.csv");
if (found?.Params is FileParams info)
{
Console.WriteLine($"{found.Name}: {info.Size} bytes, checksum {info.CheckSum}, modified {info.ModDate}");
}
doc.EmbeddedFiles.Delete("report.csv");
Console.WriteLine($"Remaining attachments: {doc.EmbeddedFiles.Count}");
doc.Save("cleaned.pdf");提示与最佳实践
- 在内存文档可能超过 2 GB 时,优先使用
OptimizedMemoryStream而不是MemoryStream—— 当其单个后备数组达到该限制时,MemoryStream会抛出异常。 - 释放
FileSpecification实例(以及为它们手动打开的任何流)–FileSpecification实现了IDisposable。 - 在遍历
EmbeddedFiles之前检查Document.HasEmbeddedFiles,以跳过对没有附件的文档进行不必要的枚举。 - 在每个
FileSpecification上设置MimeType和AFRelationship,以便下游 PDF 消费者能够正确分类附件。 - 使用
EmbeddedFileCollection.FindByName进行单次命名查找,而不是编写手动循环。
常见问题
| 问题 | 原因 | 修复 |
|---|---|---|
OutOfMemoryException 将大型文档保存到流 | MemoryStream 限制为单个约 2 GB 的后备数组 | 改为保存到 OptimizedMemoryStream |
FileSpecification.GetData() 返回空数组 | 在创建 FileSpecification 之前,源流位置未被重置 | 在将流传递给 FileSpecification 之前,将流定位到位置 0(或重新打开它) |
EmbeddedFiles.FindByName 返回 null | 附件名称未完全匹配(查找基于名称) | 遍历 doc.EmbeddedFiles 并比较 Name 值,或检查 Keys |
| 删除附件会移除错误的条目 | 多个附件共享相同的名称 | 使用来自 Keys 的确切键的 DeleteByKey 进行明确的移除 |
FAQ
MemoryStream 和 OptimizedMemoryStream 有何区别?
MemoryStream 将其字节存储在一个接近 2 GB 上限的连续数组中。OptimizedMemoryStream 则以固定大小的块存储数据,因此可以超出该限制——这对于在内存中对非常大文件执行的 Document.Save 操作非常有用。
我可以在不先将文件加载到 byte[] 中的情况下嵌入文件吗?
可以。FileSpecification(stream, name, description)直接接受一个Stream,因此您可以从File.OpenRead(...)或任何其他流嵌入,而无需自行将其缓冲到内存中。
如何从文档中取出嵌入的文件?
遍历doc.EmbeddedFiles,并对每个FileSpecification调用GetData()以获取原始字节。
EmbeddedFileCollection.Delete(name) 会删除所有具有该名称的附件吗?
不会——如果多个附件共享相同名称,请使用DeleteByKey并从Keys获取特定键来精确定位单个附件。
FileSpecification 在多个文档之间重复使用是否安全?
不。FileSpecification 实现了IDisposable并封装了单个嵌入文件流;每个文档的每个附件都应创建一个新实例。
API Reference 摘要
| 类 / 方法 | 描述 |
|---|---|
OptimizedMemoryStream | 可增长的内存流,超过 MemoryStream 的 2GB 限制 |
OptimizedMemoryStream.Write | 将字节写入缓冲区 |
OptimizedMemoryStream.ToArray | 将缓冲的字节作为字节数组返回 |
FileSpecification | 嵌入式文件附件描述符,基于流或字节数据 |
FileSpecification.GetData | 检索嵌入文件的原始字节 |
FileSpecification.Params | 嵌入文件的元数据(大小、校验和、日期) |
EmbeddedFileCollection | Document 上的 FileSpecification 附件集合 |
EmbeddedFileCollection.FindByName | 按名称查找附件 |
EmbeddedFileCollection.Delete | 按名称删除附件 |
Document.AddEmbeddedFile | 用于从原始字节嵌入文件的便捷方法 |
Document.EmbeddedFiles | 文档嵌入文件集合 |
Document.HasEmbeddedFiles | 文档是否包含任何嵌入文件 |
FileParams | 为嵌入式文件流封装大小/校验和/日期元数据 |