Consolidated DTOs under `DocumentService.Application.Common.DTOs.Requests` to centralize and align them with the application layer. Introduced `PdfBase64RequestBase` to encapsulate shared properties, reducing redundancy across request DTOs. Updated controllers, clients, and tests to use the new DTO structure. Removed redundant DTOs and cleaned up unused namespaces and comments. Improved documentation and ensured consistent naming conventions across the codebase.
126 lines
4.7 KiB
C#
126 lines
4.7 KiB
C#
using DocumentService.Application.Common.DTOs;
|
|
using DocumentService.Client.Interfaces;
|
|
using DocumentService.Application.Common.DTOs.Requests;
|
|
using Microsoft.Extensions.Logging;
|
|
using System.IO.Compression;
|
|
using System.Net.Http;
|
|
|
|
namespace DocumentService.Client.Clients;
|
|
|
|
/// <summary>
|
|
/// Implementation of PDF attachment client.
|
|
/// </summary>
|
|
public class PdfAttachmentClient(HttpClient httpClient, ILogger<PdfAttachmentClient> logger)
|
|
: BaseDocumentClient(httpClient, logger), IPdfAttachmentClient
|
|
{
|
|
/// <inheritdoc />
|
|
public async Task<AttachmentCheckResult> CheckAttachmentsAsync(Stream pdfStream, CancellationToken cancellationToken = default)
|
|
{
|
|
Logger.LogDebug("Checking PDF attachments from stream (multipart)");
|
|
|
|
var result = await SendMultipartAsync<AttachmentCheckResult>(
|
|
"/api/pdf/attachments/check",
|
|
pdfStream,
|
|
"document.pdf",
|
|
cancellationToken);
|
|
|
|
return result ?? throw new InvalidOperationException("API returned null response");
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<AttachmentCheckResult> CheckAttachmentsAsync(byte[] pdfBytes, CancellationToken cancellationToken = default)
|
|
{
|
|
Logger.LogDebug("Checking PDF attachments from byte array (Base64 JSON)");
|
|
|
|
var request = new CheckPdfAttachmentsRequest { Base64Pdf = ToBase64(pdfBytes) };
|
|
|
|
var result = await SendJsonAsync<CheckPdfAttachmentsRequest, AttachmentCheckResult>(
|
|
"/api/pdf/attachments/check",
|
|
request,
|
|
cancellationToken);
|
|
|
|
return result ?? throw new InvalidOperationException("API returned null response");
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<Dictionary<string, Stream>> ExtractAttachmentsAsync(Stream pdfStream, CancellationToken cancellationToken = default)
|
|
{
|
|
Logger.LogDebug("Extracting PDF attachments from stream (multipart)");
|
|
|
|
var zipStream = await SendMultipartForStreamAsync(
|
|
"/api/pdf/attachments/extract",
|
|
pdfStream,
|
|
"document.pdf",
|
|
cancellationToken);
|
|
|
|
return UnzipToStreams(zipStream);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<Dictionary<string, Stream>> ExtractAttachmentsAsync(byte[] pdfBytes, CancellationToken cancellationToken = default)
|
|
{
|
|
Logger.LogDebug("Extracting PDF attachments from byte array (Base64 JSON)");
|
|
|
|
var request = new ExtractPdfAttachmentsRequest { Base64Pdf = ToBase64(pdfBytes) };
|
|
|
|
var zipStream = await SendJsonForStreamAsync(
|
|
"/api/pdf/attachments/extract",
|
|
request,
|
|
cancellationToken);
|
|
|
|
return UnzipToStreams(zipStream);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
[Obsolete("API endpoint not implemented yet")]
|
|
public Task<Stream> AddAttachmentsAsync(Stream pdfStream, List<AttachmentRequestDto> attachments, CancellationToken cancellationToken = default)
|
|
{
|
|
Logger.LogWarning("AddAttachments endpoint is not implemented yet in API");
|
|
throw new NotImplementedException("API endpoint /api/pdf/attachments/add is not implemented yet");
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
[Obsolete("API endpoint not implemented yet")]
|
|
public Task<Stream> AddAttachmentsAsync(byte[] pdfBytes, List<AttachmentRequestDto> attachments, CancellationToken cancellationToken = default)
|
|
{
|
|
Logger.LogWarning("AddAttachments endpoint is not implemented yet in API");
|
|
throw new NotImplementedException("API endpoint /api/pdf/attachments/add is not implemented yet");
|
|
}
|
|
|
|
// ?????????????????????????????????????????????????????????????????????????
|
|
// Private helpers
|
|
// ?????????????????????????????????????????????????????????????????????????
|
|
|
|
/// <summary>
|
|
/// Reads a ZIP stream and returns a dictionary mapping each entry's full name
|
|
/// to an in-memory <see cref="MemoryStream"/> containing the decompressed bytes.
|
|
/// The caller owns the returned streams and is responsible for disposing them.
|
|
/// </summary>
|
|
private static Dictionary<string, Stream> UnzipToStreams(Stream zipStream)
|
|
{
|
|
var result = new Dictionary<string, Stream>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
using var archive = new ZipArchive(zipStream, ZipArchiveMode.Read, leaveOpen: false);
|
|
|
|
foreach (var entry in archive.Entries)
|
|
{
|
|
// Skip directory entries (name ends with '/')
|
|
if (string.IsNullOrEmpty(entry.Name))
|
|
continue;
|
|
|
|
var ms = new MemoryStream((int)entry.Length);
|
|
|
|
using (var entryStream = entry.Open())
|
|
{
|
|
entryStream.CopyTo(ms);
|
|
}
|
|
|
|
ms.Position = 0;
|
|
result[entry.FullName] = ms;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|