using DocumentService.Client.Interfaces;
using DocumentService.Client.Models.Requests;
using Microsoft.Extensions.Logging;
using System.IO.Compression;
using System.Net.Http;
namespace DocumentService.Client.Clients;
///
/// Implementation of PDF attachment client.
///
public class PdfAttachmentClient(HttpClient httpClient, ILogger logger)
: BaseDocumentClient(httpClient, logger), IPdfAttachmentClient
{
///
public async Task CheckAttachmentsAsync(Stream pdfStream, CancellationToken cancellationToken = default)
{
Logger.LogDebug("Checking PDF attachments from stream (multipart)");
var result = await SendMultipartAsync(
"/api/pdf/attachments/check",
pdfStream,
"document.pdf",
cancellationToken);
return result ?? throw new InvalidOperationException("API returned null response");
}
///
public async Task 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(
"/api/pdf/attachments/check",
request,
cancellationToken);
return result ?? throw new InvalidOperationException("API returned null response");
}
///
public async Task> 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);
}
///
public async Task> 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);
}
///
[Obsolete("API endpoint not implemented yet")]
public Task AddAttachmentsAsync(Stream pdfStream, List 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");
}
///
[Obsolete("API endpoint not implemented yet")]
public Task AddAttachmentsAsync(byte[] pdfBytes, List 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
// ?????????????????????????????????????????????????????????????????????????
///
/// Reads a ZIP stream and returns a dictionary mapping each entry's full name
/// to an in-memory containing the decompressed bytes.
/// The caller owns the returned streams and is responsible for disposing them.
///
private static Dictionary UnzipToStreams(Stream zipStream)
{
var result = new Dictionary(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;
}
}