Streams and File Attachments

Streams and File Attachments

Streams and File Attachments

Beyond the basic Document.Open/Document.Save overloads, Aspose.PDF FOSS for .NET gives you finer control over how bytes move in and out of a document – growable in-memory buffers for very large files, and a dedicated API for embedding, inspecting, and extracting file attachments through FileSpecification and EmbeddedFileCollection.


Growing in-memory buffers beyond 2 GB

MemoryStream stores its data in a single contiguous array, which caps out near 2 GB. OptimizedMemoryStream stores data in fixed-size chunks instead, so it can hold much larger documents when you save or build a PDF entirely in memory.

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}");

You can also write to it directly, the same way you would with any 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);

Embedding a file directly from a stream

FileSpecification can wrap a Stream instead of requiring you to buffer the attachment into a byte[] first. This is useful when the attachment source is itself a stream, such as an open file handle.

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");

Embedding a file from raw bytes

Document.AddEmbeddedFile is a convenience method for embedding a file straight from a byte array, without constructing a FileSpecification directly.

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");

Enumerating and extracting embedded files

Document.EmbeddedFiles is an EmbeddedFileCollection you can enumerate. Each entry is a FileSpecification, and GetData() returns the raw bytes of the attachment.

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);
    }
}

Finding, inspecting, and removing attachments

Use FindByName to look up a single attachment, Params to read its metadata, and Delete/DeleteByKey to remove it.

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");

Tips and Best Practices

  • Prefer OptimizedMemoryStream over MemoryStream whenever an in-memory document might exceed 2 GB – MemoryStream throws once its single backing array hits that limit.
  • Dispose FileSpecification instances (and any stream you opened manually for them) – FileSpecification implements IDisposable.
  • Check Document.HasEmbeddedFiles before iterating EmbeddedFiles to skip unnecessary enumeration on documents without attachments.
  • Set MimeType and AFRelationship on each FileSpecification so downstream PDF consumers can classify attachments correctly.
  • Use EmbeddedFileCollection.FindByName for a single named lookup instead of writing a manual loop.

Common Issues

IssueCauseFix
OutOfMemoryException saving a large document to a streamMemoryStream is limited to a single ~2 GB backing arraySave to OptimizedMemoryStream instead
FileSpecification.GetData() returns an empty arrayThe source stream position was not reset before the FileSpecification was createdSeek the stream to position 0 (or re-open it) before passing it to FileSpecification
EmbeddedFiles.FindByName returns nullThe attachment name does not match exactly (lookup is name-based)Iterate doc.EmbeddedFiles and compare Name values, or check Keys
Deleting an attachment removes the wrong entryMultiple attachments share the same nameUse DeleteByKey with the exact key from Keys for unambiguous removal

FAQ

What is the difference between MemoryStream and OptimizedMemoryStream?

MemoryStream stores its bytes in one contiguous array capped near 2 GB. OptimizedMemoryStream stores data in fixed-size chunks instead, so it can grow past that limit – useful for Document.Save operations performed entirely in memory on very large files.

Can I embed a file without loading it into a byte[] first?

Yes. FileSpecification(stream, name, description) accepts a Stream directly, so you can embed from File.OpenRead(...) or any other stream without buffering it into memory yourself.

How do I get an embedded file back out of a document?

Iterate doc.EmbeddedFiles and call GetData() on each FileSpecification to retrieve the raw bytes.

Does EmbeddedFileCollection.Delete(name) remove every attachment with that name?

No – target one attachment precisely with DeleteByKey and the specific key from Keys if multiple attachments share the same name.

Is FileSpecification safe to reuse across multiple documents?

No. FileSpecification implements IDisposable and wraps a single embedded-file stream; create a new instance per attachment per document.


API Reference Summary

Class / MethodDescription
OptimizedMemoryStreamGrowable in-memory stream that exceeds the 2 GB limit of MemoryStream
OptimizedMemoryStream.WriteWrite bytes into the buffer
OptimizedMemoryStream.ToArrayReturn the buffered bytes as a byte array
FileSpecificationEmbedded file attachment descriptor, backed by a stream or byte data
FileSpecification.GetDataRetrieve the raw bytes of an embedded file
FileSpecification.ParamsMetadata (size, checksum, dates) for the embedded file
EmbeddedFileCollectionCollection of FileSpecification attachments on a Document
EmbeddedFileCollection.FindByNameLook up an attachment by name
EmbeddedFileCollection.DeleteRemove an attachment by name
Document.AddEmbeddedFileConvenience method to embed a file from raw bytes
Document.EmbeddedFilesThe document embedded file collection
Document.HasEmbeddedFilesWhether the document contains any embedded files
FileParamsWraps size/checksum/date metadata for an embedded file stream

See Also