From 34e38f19e5e1f74de4798578f9d66be8c5290071 Mon Sep 17 00:00:00 2001 From: TekH Date: Tue, 21 Jul 2026 09:26:34 +0200 Subject: [PATCH] feat: Add PdfAttachmentController extract endpoints (multipart + Base64) --- .../Controllers/PdfAttachmentController.cs | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/DocumentOperator.API/Controllers/PdfAttachmentController.cs b/DocumentOperator.API/Controllers/PdfAttachmentController.cs index a6d31af..0b03274 100644 --- a/DocumentOperator.API/Controllers/PdfAttachmentController.cs +++ b/DocumentOperator.API/Controllers/PdfAttachmentController.cs @@ -1,5 +1,6 @@ using DocumentOperator.Application.CheckPdfAttachments.Queries; using DocumentOperator.Application.Common.DTOs; +using DocumentOperator.Application.ExtractPdfAttachments; using DocumentOperator.Domain.Common.Exceptions; using MediatR; using Microsoft.AspNetCore.Http; @@ -82,6 +83,80 @@ public class PdfAttachmentController(IMediator mediator) : ControllerBase return Ok(result); } + + /// + /// Extracts all embedded files from a PDF and returns them as a ZIP archive. + /// Supports multipart/form-data file upload. + /// + /// The PDF file to extract attachments from + /// Cancellation token + /// ZIP archive containing all extracted attachments + /// Attachments extracted successfully - returns ZIP file + /// Invalid input (file missing, not a PDF, or corrupted) + /// PDF contains no attachments + /// Internal server error during PDF processing + [HttpPost("extract")] + [Consumes("multipart/form-data")] + [ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)] + public async Task ExtractAttachmentsFromFile( + IFormFile file, + CancellationToken cancellationToken) + { + // Use IFormFile stream directly (no intermediate byte[] conversion) + using var pdfStream = file.OpenReadStream(); + + // Send command to MediatR + var command = new ExtractPdfAttachmentsCommand { PdfStream = pdfStream }; + byte[] zipBytes = await mediator.Send(command, cancellationToken); + + // Return ZIP file + return File(zipBytes, "application/zip", "attachments.zip"); + } + + /// + /// Extracts all embedded files from a PDF and returns them as a ZIP archive. + /// Supports Base64-encoded PDF via JSON payload. + /// + /// Request containing Base64-encoded PDF + /// Cancellation token + /// ZIP archive containing all extracted attachments + /// Attachments extracted successfully - returns ZIP file + /// Invalid input (Base64 format error, not a PDF, or corrupted) + /// PDF contains no attachments + /// Internal server error during PDF processing + [HttpPost("extract")] + [Consumes("application/json")] + [ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)] + public async Task ExtractAttachmentsFromBase64( + [FromBody] ExtractPdfAttachmentsRequest request, + CancellationToken cancellationToken) + { + // Convert Base64 to stream (wrap in try-catch to throw BadRequestException) + byte[] pdfBytes; + try + { + pdfBytes = Convert.FromBase64String(request.Base64Pdf); + } + catch (FormatException ex) + { + throw new BadRequestException("Invalid Base64 format: " + ex.Message); + } + + using var pdfStream = new MemoryStream(pdfBytes); + + // Send command to MediatR + var command = new ExtractPdfAttachmentsCommand { PdfStream = pdfStream }; + byte[] zipBytes = await mediator.Send(command, cancellationToken); + + // Return ZIP file + return File(zipBytes, "application/zip", "attachments.zip"); + } } /// @@ -95,3 +170,15 @@ public record CheckPdfAttachmentsRequest /// JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2c... public string Base64Pdf { get; init; } = string.Empty; } + +/// +/// Request DTO for Base64-encoded PDF attachment extraction +/// +public record ExtractPdfAttachmentsRequest +{ + /// + /// PDF document encoded as Base64 string + /// + /// JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2c... + public required string Base64Pdf { get; init; } +}