2014-10-28 3 views
1

Я подозреваю, что не понимаю, где jpegoptim пытается написать свои временные файлы.jpegoptim on ASP.Net - «Ошибка открытия временного файла»

У меня IIS 7.5 работает ASP.NET 4 AppDomain. В этом у меня есть процесс, который оптимизирует JPEGs с jpegoptim как так:

FileHelper.Copy(existingPath, optimizerPath); 
var jpegOptimResult = await ImageHelper.JpegOptim(optimizerPath, 30); 

Запуск локально я получаю оптимизированное изображение. Запуск на вышеуказанном сервере я получаю:

D: \ WWW \ hplusf.com \ Ь \ пк \ test.jpg 4096x2990 24bit N Adobe [OK] jpegoptim: Ошибка при открытии временного файла.

Я могу показать код FileHelper.Copy(), но это в основном просто File.Copy(), что переписывает, если файл уже существует.

Вот ImageHelper.JpegOptim:

public static async Task<string> JpegOptim(string path, int quality) 
{ 
    string jpegOptimPath = Path.GetDirectoryName(new Uri(Assembly 
      .GetExecutingAssembly().CodeBase).LocalPath) 
     + @"\Lib\jpegoptim.exe"; 

    var jpegOptimResult = await ProcessRunner.O.RunProcess(
     jpegOptimPath, 
     "-m" + quality + " -o -p --strip-all --all-normal \"" + path + "\"", 
     false, true 
    ); 

    return jpegOptimResult; 
} 

jpegOptimResult является то, что вы видите там, как сообщение об ошибке она производит. И вот ProcessRunner.RunProcess:

public async Task<string> RunProcess(string command, string args, 
    bool window, bool captureOutput) 
{ 
    var processInfo = new ProcessStartInfo(command, args); 

    if (!window) 
     makeWindowless(processInfo); 

    string output = null; 
    if (captureOutput) 
     output = await runAndCapture(processInfo); 
    else 
     runDontCapture(processInfo); 

    return output; 
} 

protected void makeWindowless(ProcessStartInfo processInfo) 
{ 
    processInfo.CreateNoWindow = true; 
    processInfo.WindowStyle = ProcessWindowStyle.Hidden; 
} 

protected async Task<string> runAndCapture(ProcessStartInfo processInfo) 
{ 
    processInfo.UseShellExecute = false; 
    processInfo.RedirectStandardOutput = true; 
    processInfo.RedirectStandardError = true; 

    var process = Process.Start(processInfo); 

    var output = process.StandardOutput; 
    var error = process.StandardError; 

    while (!process.HasExited) 
    { 
     await Task.Delay(100); 
    } 

    string s = output.ReadToEnd(); 
    s += '\n' + error.ReadToEnd(); 

    return s; 
} 

Итак:

  • jpegOptim работает должным образом на моей локальной машине, и оптимизирует файл, так что это не так, как я звоню jpegOptim.

  • Операция копирования успешно без исключений, так что это не проблема разрешения с чтением пользователя ASP.Net/записи из этого каталога

  • jpegOptim только оптимизирует и перезаписывает файл, поэтому, если это на самом деле бега под тем же пользователем ASP.Net, у него не должно возникнуть проблемы с записью этого файла, но ...

  • Непонятно, где jpegOptim пытается записать свой временный файл, поэтому, возможно, основная проблема заключается в том, где записывается этот временный файл ,

Однако, судя по источнику с Windows:

http://sourceforge.net/p/jpegoptim/code/HEAD/tree/jpegoptim-1.3.0/trunk/jpegoptim.c

«временный файл» jpegOptim, как представляется, просто файл назначения при использовании представленных выше опций. Соответствующие линии jpegOptim источника:

int dest = 0; 

int main(int argc, char **argv) 
{ 
    ... 

Там некоторый код здесь ищет -d аргумент, который устанавливает Dest = 1 - смысл здесь Dest остается 0. Затем поражает, если ветвь, и положение еще, для Dest = = 0, делает это:

if (!splitdir(argv[i],tmpdir,sizeof(tmpdir))) 
    fatal("splitdir() failed!"); 
strncpy(newname,argv[i],sizeof(newname)); 

Вот скопировать имя каталога часть файла входного изображения переменной tmpdir - так как C: \ Blah \ 18.jpg назначит tmpdir="C:\Blah\". Затем он сбрасывает все имя входного изображения на newname, а это означает, что он просто перезапишет его на месте.

На данный момент в коде переменные это с помощью должно быть:

dest=0 
argv[i]=D:\www\hplusf.com\b\pc\test.jpg 
tmpdir=D:\www\hplusf.com\b\pc\ 
newname=D:\www\hplusf.com\b\pc\test.jpg 

Это то на самом деле открывает файл, и есть возможность ошибки там, предлагая jpegoptim успешно открыть файл. Он также распаковывает файл, подтверждающий его успешное открытие.

Конкретное сообщение об ошибке я вижу происходит в этих строках - я признаюсь, что я не знаю, если MKSTEMPS установлен или нет для сборки по умолчанию (который я использую):

snprintf(tmpfilename,sizeof(tmpfilename), 
     "%sjpegoptim-%d-%d.XXXXXX.tmp", tmpdir, (int)getuid(), (int)getpid()); 
#ifdef HAVE_MKSTEMPS 
    if ((tmpfd = mkstemps(tmpfilename,4)) < 0) 
     fatal("error creating temp file: mkstemps() failed"); 
    if ((outfile=fdopen(tmpfd,"wb"))==NULL) 
#else 
    tmpfd=0; 
    if ((outfile=fopen(tmpfilename,"wb"))==NULL) 
#endif 
     fatal("error opening temporary file"); 

Так snprintf, как C# String.Format(), который должен производить путь:

D: \ WWW \ hplusf.com \ б \ пк \ jpegoptim-1-2.XXXXXX.tmp

Судя по тому, что я могу найти, скорее всего, MKSTEMPS не определен. Значение fopen называется с «wb» означает, что он записывает двоичный файл, и он возвращает значение null, которое не открывается, и появляется сообщение об ошибке.

Так что - возможные причины:

  • Плохой путь в TmpDir Вполне возможно, я после C++ плохо (вероятно), но, от взглядов этого он должен быть идентичен исходному пути изображения. Но, возможно, это искалечено для tmpdir, jpegoptim? Входной путь явно чист, потому что jpegoptim фактически испускает его чисто в сообщении об ошибке.

  • Разрешения выпуск Похоже, маловероятно. Пользователь ASP.Net, в котором он работает, может четко читать и писать, потому что он копирует в каталог перед запуском jpegoptim, и единственным пользователем на компьютере с любыми разрешениями для этого каталога является этот пользователь, поэтому jpegoptim должен был сбить до этого момента если это были разрешения. Это может быть попытка получить доступ к другому директору, но это будет действительно сценарий Bad tmpdir.

  • Что-то еще, о чем я не думал.

Идеи?

Примечание: Этот вопрос похож:

Using jpegtran, jpegoptim, or other jpeg optimization/compression in C#

Однако этот вопрос с просьбой о совместном окр на GoDaddy, вызывая ответы на спираль вокруг вероятности, он не может раскрутить процессов. У нас есть полный контроль над нашим сервером, и, как должно быть ясно из вышеизложенного, процесс jpegoptim определенно начинается успешно, поэтому это другой сценарий.

ответ

1

Как оказалось, мое чтение jpegoptim было неверным. Используемый tmpdir используется там, где указывает рабочий каталог исполняемого файла, а не где входные изображения, а не где находится исполняемый файл.Таким образом, решение было 2 раза:

  1. Дайте разрешения исполняемые на запись своего собственного каталога * (но отрицать его доступ к видоизменяться)
  2. Изменить ProcessRunner для запуска процессов в месте - Установка рабочего Каталог, в котором находится exe.

Вторая модификация выглядит следующим образом:

var processInfo = new ProcessStartInfo(command, args); 

// Ensure the exe runs in the path where it sits, rather than somewhere 
// less safe like the website root 
processInfo.WorkingDirectory = (new FileInfo(command)).DirectoryName; 

* Примечание: Я посчастливилось иметь jpegoptim.exe изолированных на сервере своей собственной директории, чтобы ограничить риск. Если у вас было что-то более глобальное, например Program Files, вы определенно не должны этого делать - вместо этого установите рабочий каталог, как указано выше, но где-то изолированное/безопасное, например, tmp dir или даже лучше скребковый диск. Если у вас есть RAM, RAMdrive будет быстрее.

** Второе примечание: из-за того, как работают жесткие диски и jpegoptim, если местоположение tmp не совпадает с конечным местом вывода, существует потенциальное, частичное условие гонки, введенное между jpegoptim и другим кодом, который может быть это зависит от его результатов. В частности, если вы используете тот же диск, когда выполняется jpegoptim, выходной JPEG завершен - ОС изменяет запись в своей файловой таблице, но данные для изображения на жестком диске уже написаны до завершения. Когда tmp и destination являются отдельными дисками, jpegoptim заканчивается, сообщая операционной системе, чтобы перейти от tmpdir к выходному директору. Это перемещение данных, которое заканчивается через некоторое время после запуска jpegoptim. Если ваш ожидающий код достаточно быстр, он начнет работу с неполного JPEG.

Смежные вопросы