c # — jpegoptim на ASP.Net — & quot; ошибка при открытии временного файла & quot;

Я подозреваю, что не понимаю, где jpegoptim пытается записать свои временные файлы.

У меня есть IIS 7.5 под управлением ASP.Net 4 AppDomain. В нем у меня есть процесс, который оптимизирует JPEG с помощью jpegoptim следующим образом:

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

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

D: \ www \ hplusf.com \ b \ pc \ test.jpg 4096×2990 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. Он затем попадает в ветвь if, и предложение else для 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», означающего, что он записывает двоичный файл, и возвращает нулевое значение, что означает, что он не открывается, и появляется сообщение об ошибке.

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

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

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

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

Идеи?

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

Использование jpegtran, jpegoptim или другой оптимизации / сжатия jpeg в C #

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

1

Решение

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

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

Вторая модификация выглядит так:

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 или, что еще лучше, на «чистый диск». Если у вас есть оперативная память для него, RAMdrive будет самым быстрым.

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

1

Другие решения


По вопросам рекламы [email protected]