feat: Implement DevExpressPdfProcessor.ExtractAttachmentsAsync with ZIP packaging
This commit is contained in:
@@ -234,6 +234,68 @@ public class DevExpressPdfProcessor : IPdfProcessor
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts all embedded files from a PDF document and returns them as a ZIP archive.
|
||||
/// Uses DevExpress PdfDocument.FileAttachments to retrieve attachment data.
|
||||
/// </summary>
|
||||
/// <param name="pdfStream">PDF content as stream (caller is responsible for disposal)</param>
|
||||
/// <returns>ZIP archive byte array containing all extracted attachments</returns>
|
||||
/// <exception cref="BadRequestException">Thrown when stream is empty</exception>
|
||||
/// <exception cref="NotFoundException">Thrown when PDF contains no attachments</exception>
|
||||
public async Task<byte[]> ExtractAttachmentsAsync(Stream pdfStream)
|
||||
{
|
||||
// 1. Input Validation
|
||||
ArgumentNullException.ThrowIfNull(pdfStream, nameof(pdfStream));
|
||||
|
||||
if (pdfStream.Length == 0)
|
||||
{
|
||||
throw new BadRequestException("PDF stream cannot be empty");
|
||||
}
|
||||
|
||||
// Defensive validation: Seekable streams must be at Position = 0
|
||||
if (pdfStream.CanSeek && pdfStream.Position != 0)
|
||||
{
|
||||
throw new BadRequestException("PDF stream must be positioned at the beginning (Position = 0).");
|
||||
}
|
||||
|
||||
// 2. Load PDF with DevExpress Document API
|
||||
using var processor = new PdfDocumentProcessor();
|
||||
processor.LoadDocument(pdfStream);
|
||||
|
||||
var document = processor.Document;
|
||||
|
||||
// 3. Extract attachment data using DevExpress FileAttachments collection
|
||||
var fileAttachments = document.FileAttachments;
|
||||
|
||||
// 4. No attachments case
|
||||
if (fileAttachments == null || !fileAttachments.Any())
|
||||
{
|
||||
throw new NotFoundException("PDF does not contain any attachments");
|
||||
}
|
||||
|
||||
// 5. Create ZIP archive in memory
|
||||
using var zipStream = new MemoryStream();
|
||||
using (var zipArchive = new System.IO.Compression.ZipArchive(zipStream, System.IO.Compression.ZipArchiveMode.Create, leaveOpen: true))
|
||||
{
|
||||
foreach (var attachment in fileAttachments)
|
||||
{
|
||||
// Get attachment metadata
|
||||
string fileName = attachment.FileName ?? "unnamed";
|
||||
byte[] fileData = attachment.Data;
|
||||
|
||||
// Create entry in ZIP
|
||||
var entry = zipArchive.CreateEntry(fileName, System.IO.Compression.CompressionLevel.Optimal);
|
||||
|
||||
// Write attachment data to ZIP entry
|
||||
using var entryStream = entry.Open();
|
||||
await entryStream.WriteAsync(fileData, 0, fileData.Length);
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Return ZIP byte array
|
||||
return zipStream.ToArray();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Helpers
|
||||
|
||||
Reference in New Issue
Block a user