Я пишу API, используя ImageMagick. NET (отсюда: https://www.nuget.org/packages/Magick.NET-Q8-AnyCPU/) и GhostScript (отсюда: https://www.ghostscript.com/download.html), чтобы превратить PDF в миниатюра изображения:
var thumbnailStream = new MemoryStream();
using (var images = new MagickImageCollection())
{
// Read the frames into the collection
resource.ResourceStream.Seek(0, SeekOrigin.Begin);
var settings = new MagickReadSettings() { FrameIndex = 0, FrameCount = 1 };
images.Read(resource.ResourceStream, settings); // WARNING - NOT THREAD SAFE!!
// Take the first page and make a thumbnail out of it
var image = images[0];
// Scale thumbnail to proper width
var geometry = new MagickGeometry(){ Width = ThumbnailWidth };
image.Scale(geometry);
// Write the thumbnail to a MemoryStream
image.Write(thumbnailStream);
}
Просто выполнение кода как есть с перерывами in:
HResult=0x80131500
Message=FailedToExecuteCommand `...gswin64c.exe" -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pngalpha" -dTextAlphaBits=4 -dGraphicsAlphaBits=4 "-r300x300" -dFirstPage=1 -dLastPage=1 "-sOutputFile=...
(The system cannot find the file specified.) @ error/delegate.c/ExternalDelegateCommand/475
Это та же ошибка, что и в случае, если GhostScript isn ' т установлен вообще. Похоже, что исполняемый файл ghostscript (gswin64 c .exe) периодически недоступен . Однако, если я заверну вызов «Чтение» в блокировку следующим образом:
private static readonly object ImageMagickReadLock = new object();
// LOCK THE THREAD-UNSAFE CODE!!
lock (ImageMagickReadLock)
{
images.Read(resource.ResourceStream, settings);
}
... все работает, как и ожидалось! Однако это не масштабируемое решение (тестирование производительности нагрузки продемонстрировало нехватку ресурсов, которая может привести к тому, что этот процесс в среднем за 1,5 с достигнет более 20 с !!), поэтому я ищу поточно-безопасный способ использования GhostScript через ImageMagick ( . NET).
Здесь есть 7-летний поток здесь , который указывает, что можно получить исходный код для GhostScript и скомпилировать его самостоятельно с помощью специального флага. (Не знаю, почему безопасность потоков не используется по умолчанию, но это отдельное обсуждение). Однако я остался скептическим из-за возраста потока и, поскольку решение никогда не принималось - фактически, спрашивающий сообщил о проблемах с решением. Кроме того, я не верю, что пользователь использовал ImageMagick, поэтому я даже не уверен, что если бы я был , чтобы заставить его работать, даже если бы он мне даже помог.
Итак, я Задайте еще раз здесь: Существует ли потокобезопасный способ использования GhostScript через ImageMagick (. NET)?
Обновление:
После некоторого тестирования и помощи от dlemstra Я все еще вижу проблему, хотя она иная, чем прежде (что, я надеюсь, означает, что я ближе!).
Одной из моих проблем было то, что я использовал ghostscript .dll для обработки многопоточных запросов в неблокирующем случае, и когда поток, использующий .dll, занят, ImageMagick ищет .exe для заполнения с любыми последующими параллельными потоками. Это объясняет ошибку «файл не найден», которую я описал выше - даже если файл .dll присутствовал, необходимы как .dll, так и .exe ghostscipts. Поэтому я добавил .exe.
Тем не менее, я все еще получаю FailedToExecuteCommand
, но на этот раз он может найти файл и подробности сказать error/ghostscript-private.h/InvokeGhostscriptDelegate/143
.
Полное сообщение об ошибке :
ImageMagick.MagickDelegateErrorException
HResult=0x80131500
Message=FailedToExecuteCommand `".../GhostScript/gswin64c.exe" -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pngalpha" -dTextAlphaBits=4 -dGraphicsAlphaBits=4 "-r300x300" -dFirstPage=1 -dLastPage=1 "-sOutputFile=.../temp/magick-10252ZvAOzMMOsCXz%d" "-f...temp/magick-10252gRfuzynqR0Pe" "-f...temp/magick-10252Bp3xSsSeS1Ol"' (1) @ error/ghostscript-private.h/InvokeGhostscriptDelegate/143
Source=Magick.NET-Q8-AnyCPU
StackTrace:
at ImageMagick.MagickImageCollection.NativeMagickImageCollection.ReadBlob(MagickSettings settings, Byte[] data, Int32 offset, Int32 length)
at ImageMagick.MagickImageCollection.AddImages(Byte[] data, Int32 offset, Int32 count, MagickReadSettings readSettings, Boolean ping)
at ImageMagick.MagickImageCollection.AddImages(Stream stream, MagickReadSettings readSettings, Boolean ping)