流和文件附件

流和文件附件

超越基本的 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嵌入文件的元数据(大小、校验和、日期)
EmbeddedFileCollectionDocument 上的 FileSpecification 附件集合
EmbeddedFileCollection.FindByName按名称查找附件
EmbeddedFileCollection.Delete按名称删除附件
Document.AddEmbeddedFile用于从原始字节嵌入文件的便捷方法
Document.EmbeddedFiles文档嵌入文件集合
Document.HasEmbeddedFiles文档是否包含任何嵌入文件
FileParams为嵌入式文件流封装大小/校验和/日期元数据

另请参阅

 中文