Проблемы с конвертацией PDF в SVG через Aspose.Pdf при параллельной обработке
опишу проблему:
Есть задача по конвертации pdf страниц в svg и png файлы.
Есть код, который успешно реализует данную задачу при последовательной обработке всех файлов. Проблема возникает, когда я пытаюсь распараллелить выполнение конвертации для нескольких файлов одновременно. Библиотека aspose.pdf не до конца конвертирует pdf в svg (рандомно обрезает страницу).Запускаю одни и те же документы, получаю рандомно некорректно сконвертированные файлы. Притом сам файл svg открывается без ошибок, просто часть линий или текста не переносятся в svg из pdf. При последовательной обработке файлов такой проблемы не возникает. У меня подозрение, что я неправильно реализую распараллеливание для асинхронных задач, код ниже.
public async Task<JobContract> ProcessingByContractParallel(JobContract jobContract)
{
try
{
foreach (var trmPackage in jobContract.TrmPackages)
{
var tempFolder = !string.IsNullOrWhiteSpace(jobContract.TempFolder)
? jobContract.TempFolder
: Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var outputDirectoryInfo = new DirectoryInfo(Path.Combine(tempFolder, jobContract.QueueItemId.ToString(), String.Concat(trmPackage.TrmName, "_", Path.GetRandomFileName())));
if (!outputDirectoryInfo.Exists) outputDirectoryInfo.Create();
trmPackage.TrmSplittedFilesPath = outputDirectoryInfo.FullName;
_logger.LogInformation($"Путь до временных файлов: {outputDirectoryInfo.FullName}");
var options = new ParallelOptions { MaxDegreeOfParallelism = 4};
//здесь пытаюсь добиться распараллеливания обработки файлов
//без проблем отрабатывает запуск foreach (var pdfFile in trmPackage.ArrayOfPdfFiles)
await Parallel.ForEachAsync(trmPackage.ArrayOfPdfFiles, options,async (pdfFile,defult) =>
{
var fullPathPdfFile = Path.Combine(trmPackage.TrmPath, pdfFile.FileName);
pdfFile.FileSize = new FileInfo(fullPathPdfFile).Length;
var splitResults = new List<FileOperationResult>();
try
{
splitResults = (await _pdfSplitterService.SplitAndSavePageAsPdf(outputDirectoryInfo.FullName, fullPathPdfFile))
.ToList();
}
catch (Exception ex)
{
var fileOperationResultErr = FileOperationResult.CreateFailure("Faulted", ex);
splitResults.Add(fileOperationResultErr);
}
foreach (var splitResult in splitResults)
{
if (splitResult.HasError)
{
pdfFile.PagesInfo.Add(new PageInfo
{
SplitPDFPageName = splitResult.FileName,
SplitSVGPageName = "",
SplitPNGPageName = "",
PageStatusError = splitResult.HasError,
PageExceptionMsg = splitResult.NonSuccessMessage,
SplitPageNumber = splitResults.IndexOf(splitResult) + 1
});
}
else
{
try
{
//конвертируем и устанавливаем ожидаемое время выполнения в 30 мин
//проблема возникает с методом ConvertOnePagesPdfToSvgWithCt
var convertSvgResult = await _pdfConverterService.ConvertOnePagesPdfToSvgWithCt(outputDirectoryInfo.FullName, splitResult.FullFileName);
var convertPngResult = await _pdfConverterService.ConvertOnePagesPdfToPngWithCt(outputDirectoryInfo.FullName, splitResult.FullFileName);
pdfFile.PagesInfo.Add(new PageInfo
{
SplitPDFPageName = splitResult.FileName,
SplitSVGPageName = convertSvgResult.FileName,
SplitPNGPageName = convertPngResult.FileName,
PageStatusError = splitResult.HasError || convertSvgResult.HasError || convertPngResult.HasError,
PageExceptionMsg = splitResult.NonSuccessMessage + convertSvgResult.NonSuccessMessage + convertPngResult.NonSuccessMessage,
SplitPageNumber = splitResults.IndexOf(splitResult) + 1
});
}
catch (Exception ex)
{
pdfFile.PagesInfo.Add(new PageInfo
{
SplitPDFPageName = splitResult.FileName,
SplitSVGPageName = "",
SplitPNGPageName = "",
PageStatusError = splitResult.HasError,
PageExceptionMsg = ex.Message,
SplitPageNumber = splitResults.IndexOf(splitResult) + 1
});
}
}
}
});
}
return jobContract;
}
catch (Exception ex)
{
_logger.LogError(ex, "ProcessingByContract");
return jobContract;
}
}
Сам проблемный метод:
public async Task<FileOperationResult> ConvertOnePagesPdfToSvgWithCt(string outputFolder, string? fullPdfFileName)
{
var outputFileName = $"{Path.GetFileNameWithoutExtension(fullPdfFileName)}.svg";
_logger.LogInformation($"Convert to Svg - {Path.GetFileName(fullPdfFileName)}");
try
{
using var cts = new CancellationTokenSource();
cts.CancelAfter(_convertSetting.Value.ConvertTimeoutMs);
var task = Task.Run(async() =>
{
try
{
var pdfPage = new Pdf(fullPdfFileName);
//сама конвертация
await pdfPage.SaveAsSvgAsync(Path.Combine(outputFolder, outputFileName), cts.Token);
}
catch
{
cts.Cancel();
}
}, cts.Token);
await task.WaitAsync(cts.Token);
if (task.IsCanceled)
{
_logger.LogWarning($"Операция конвертации прервана для файла:{Path.GetFileName(fullPdfFileName)}");
return FileOperationResult.CreateFailure(outputFileName, new TimeoutException("Превышено время ожидания задачи или ошибка ковертации"));
}
else
{
return FileOperationResult.CreateSuccessResult(outputFileName);
}
}
catch (Exception ex)
{
_logger.LogError(ex, message: $"ERROR Converting pdf '{fullPdfFileName}' as SVG");
return FileOperationResult.CreateFailure(outputFileName, ex);
}
}
Конвертация сторонней библиотекой
public async Task<string> SaveAsSvgAsync(string outputFilename, CancellationToken cts, bool removeWatermark = true)
{
try
{
try
{
var document = new Aspose.Pdf.Document(Filestream, false);
await document.SaveAsync(outputFilename, Aspose.Pdf.SaveFormat.Svg, cts);
if (removeWatermark) // remove watermark
{
var svgTextLines = await File.ReadAllTextAsync(outputFilename);
var svgClearText = Regex.Replace(svgTextLines, @"(<text)(.)+(Evaluation)(.)+(Aspose)(.)+(<\/text>)", "");
//var svgClearText = Regex.Replace(svgTextLines, @"[\n\r\t]+?((<text)(.)+[\n\r]+?(Evaluation)(.)+(Aspose)(.)+[\n\r]+?(.)+(<\/text>))[\n\r\t]*", "");
await File.WriteAllTextAsync(outputFilename, svgClearText);
}
}
catch
{
var document = new Spire.Pdf.PdfDocument(Filestream);
document.SaveToFile(outputFilename, Spire.Pdf.FileFormat.SVG);
if (removeWatermark) // remove watermark
{
var svgTextLines = await File.ReadAllTextAsync(outputFilename);
var svgClearText = Regex.Replace(svgTextLines, @"(<text)(.)+(Evaluation)(.)+(Aspose)(.)+(<\/text>)", "");
//var svgClearText = Regex.Replace(svgTextLines, @"[\n\r\t]+?((<text)(.)+[\n\r]+?(Evaluation)(.)+(Aspose)(.)+[\n\r]+?(.)+(<\/text>))[\n\r\t]*", "");
await File.WriteAllTextAsync(outputFilename, svgClearText);
}
}
}
catch
{
throw;
}
return outputFilename;
}