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
OptimizedMemoryStreamoverMemoryStreamwhenever an in-memory document might exceed 2 GB –MemoryStreamthrows once its single backing array hits that limit. - Dispose
FileSpecificationinstances (and any stream you opened manually for them) –FileSpecificationimplementsIDisposable. - Check
Document.HasEmbeddedFilesbefore iteratingEmbeddedFilesto skip unnecessary enumeration on documents without attachments. - Set
MimeTypeandAFRelationshipon eachFileSpecificationso downstream PDF consumers can classify attachments correctly. - Use
EmbeddedFileCollection.FindByNamefor a single named lookup instead of writing a manual loop.
Common Issues
| Issue | Cause | Fix |
|---|---|---|
OutOfMemoryException saving a large document to a stream | MemoryStream is limited to a single ~2 GB backing array | Save to OptimizedMemoryStream instead |
FileSpecification.GetData() returns an empty array | The source stream position was not reset before the FileSpecification was created | Seek the stream to position 0 (or re-open it) before passing it to FileSpecification |
EmbeddedFiles.FindByName returns null | The 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 entry | Multiple attachments share the same name | Use 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 / Method | Description |
|---|---|
OptimizedMemoryStream | Growable in-memory stream that exceeds the 2 GB limit of MemoryStream |
OptimizedMemoryStream.Write | Write bytes into the buffer |
OptimizedMemoryStream.ToArray | Return the buffered bytes as a byte array |
FileSpecification | Embedded file attachment descriptor, backed by a stream or byte data |
FileSpecification.GetData | Retrieve the raw bytes of an embedded file |
FileSpecification.Params | Metadata (size, checksum, dates) for the embedded file |
EmbeddedFileCollection | Collection of FileSpecification attachments on a Document |
EmbeddedFileCollection.FindByName | Look up an attachment by name |
EmbeddedFileCollection.Delete | Remove an attachment by name |
Document.AddEmbeddedFile | Convenience method to embed a file from raw bytes |
Document.EmbeddedFiles | The document embedded file collection |
Document.HasEmbeddedFiles | Whether the document contains any embedded files |
FileParams | Wraps size/checksum/date metadata for an embedded file stream |