Потоки и вложения файлов
Потоки и вложения файлов
Помимо базовых перегрузок Document.Open/Document.Save, Aspose.PDF FOSS для .NET предоставляет более тонкий контроль над тем, как байты перемещаются в документ и из него — расширяемые буферы в памяти для очень больших файлов и специализированный API для встраивания, инспекции и извлечения файловых вложений через FileSpecification и EmbeddedFileCollection.
Увеличение буферов в памяти более 2ГБ
MemoryStream хранит данные в едином непрерывном массиве, который ограничивается примерно 2ГБ. 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");Советы и лучшие практики
- Отдавайте предпочтение
OptimizedMemoryStreamвместоMemoryStream, когда в памяти документ может превышать 2ГБ —MemoryStreamвыбрасывает исключение, как только его единственный базовый массив достигает этого ограничения. - Освободите экземпляры
FileSpecification(и любой поток, который вы открыли вручную для них) –FileSpecificationреализуетIDisposable. - Проверьте
Document.HasEmbeddedFilesперед переборомEmbeddedFiles, чтобы пропустить ненужную итерацию по документам без вложений. - Установите
MimeTypeиAFRelationshipна каждыйFileSpecification, чтобы downstream PDF consumers могли правильно классифицировать вложения. - Используйте
EmbeddedFileCollection.FindByNameдля одиночного именованного поиска вместо написания ручного цикла.
Распространённые проблемы
| Проблема | Причина | Исправление |
|---|---|---|
OutOfMemoryException сохранение большого документа в поток | MemoryStream ограничен единым массивом размером ~2 GB | Сохраните в OptimizedMemoryStream вместо этого |
FileSpecification.GetData() возвращает пустой массив | Позиция исходного потока не была сброшена перед созданием FileSpecification | Переместите указатель потока в позицию 0 (или откройте его заново) перед передачей в FileSpecification |
EmbeddedFiles.FindByName возвращает null | Имя вложения не совпадает точно (поиск основан на имени) | Итерируйте doc.EmbeddedFiles и сравните значения Name, либо проверьте Keys |
| Удаление вложения удаляет неправильную запись | Несколько вложений имеют одинаковое имя | Используйте DeleteByKey с точным ключом из Keys для однозначного удаления |
FAQ
В чём разница между MemoryStream и OptimizedMemoryStream?
MemoryStream хранит свои байты в одном непрерывном массиве, ограниченном примерно 2ГБ. OptimizedMemoryStream хранит данные в блоках фиксированного размера, поэтому может превысить этот предел – полезно для Document.Save операций, выполняемых полностью в памяти над очень большими файлами.
Могу ли я вложить файл, не загружая его сначала в byte[]?
Да. FileSpecification(stream, name, description) принимает Stream напрямую, поэтому вы можете вложить из File.OpenRead(...) или любого другого потока, не буферизуя его в памяти самостоятельно.
Как извлечь вложенный файл из документа?
Итерируйте doc.EmbeddedFiles и вызывайте GetData() для каждого FileSpecification, чтобы получить необработанные байты.
Удаляет ли EmbeddedFileCollection.Delete(name) все вложения с этим именем?
Нет — точно нацеливайтесь на одно вложение с помощью DeleteByKey и конкретного ключа из Keys, если несколько вложений имеют одинаковое имя.
Можно ли безопасно переиспользовать FileSpecification в нескольких документах?
Нет. FileSpecification реализует IDisposable и оборачивает единый поток вложенного файла; создавайте новый экземпляр для каждого вложения в каждом документе.
API Reference Сводка
| Класс / Метод | Описание: |
|---|---|
OptimizedMemoryStream | Расширяемый поток в памяти, который превышает ограничение в 2ГБ MemoryStream |
OptimizedMemoryStream.Write | Записать байты в буфер |
OptimizedMemoryStream.ToArray | Вернуть буферизованные байты в виде массива байтов |
FileSpecification | Встроенный дескриптор вложения файла, основанный на потоке или байтовых данных |
FileSpecification.GetData | Получить сырые байты встроенного файла |
FileSpecification.Params | Метаданные (размер, контрольная сумма, даты) для встроенного файла |
EmbeddedFileCollection | Коллекция FileSpecification вложений на Document |
EmbeddedFileCollection.FindByName | Найти вложение по имени |
EmbeddedFileCollection.Delete | Удалить вложение по имени |
Document.AddEmbeddedFile | Удобный метод для встраивания файла из сырых байтов |
Document.EmbeddedFiles | Коллекция встроенных файлов документа |
Document.HasEmbeddedFiles | Содержит ли документ какие-либо встроенные файлы |
FileParams | Оборачивает метаданные размера/контрольной суммы/даты для встроенного файлового потока |