1

jpegoptim が一時ファイルを書き込もうとする場所を理解できていないのではないかと思います。

IIS 7.5 で ASP.Net 4 AppDomain を実行しています。その中に、次のように jpegoptim で JPEG を最適化するプロセスがあります。

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

ローカルで実行すると、最適化された画像が得られます。上記のサーバーで実行すると、次のようになります。

D:\www\hplusf.com\b\pc\test.jpg 4096x2990 24bit N Adob​​e [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) 
{
    ...

dest=1 を設定する -d 引数を探しているコードがいくつかあります。つまり、ここでは dest は 0 のままです。次に if 分岐にヒットし、dest == 0 の else 句がこれを行います。

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\b\pc\jpegoptim-1-2.XXXXXX.tmp

私が見つけたものから判断すると、MKSTEMPS が定義されていない可能性がありますfopen。つまり、「wb」で呼び出されているという意味であり、バイナリ ファイルを書き込んでおり、null を返しているという意味で、開くことができず、エラー メッセージが表示されます。

だから - 考えられる原因:

  • tmpdirのパスが正しくありません C++ の手順をよく踏襲していない可能性があります (おそらく) が、見た目からすると、イメージのソース パスと同じである必要があります。しかし、おそらくそれは tmpdir のために jpegoptim によって破壊されたのでしょうか? 入力パスは、jpegoptim が実際にエラー メッセージでクリーンに出力するため、明らかにクリーンです。

  • アクセス許可の問題かなりありそうにありません。これが実行されているASP.Netユーザーは、jpegoptimが起動する前にディレクトリにコピーされるため、明確に読み書きできます。このディレクトリへのアクセス許可を持つマシン上の唯一のユーザーはそのユーザーであるため、jpegoptimはこの時点より前に失敗するはずですパーミッションの場合。別のディレクトリにアクセスしようとしている可能性がありますが、それは実際には悪い tmpdir シナリオです。

  • 私が考えていない他の何か。

アイデア?

注:この質問は似ています:

C# で jpegtran、jpegoptim、またはその他の jpeg 最適化/圧縮を使用する

ただし、その質問は GoDaddy の共有環境について尋ねているため、プロセスをスピンアップできない可能性について回答が渦巻いています。私たちはサーバーを完全に制御しており、上記から明らかなように、jpegoptim プロセスは間違いなく正常に開始されているため、別のシナリオです。

4

1 に答える 1

2

jpegoptim の読み方が間違っていたことが判明しました。使用する tmpdir は、実行可能ファイルの作業ディレクトリが指す場所であり、入力画像がある場所ではなく、実行可能ファイルが置かれている場所でもありません。したがって、解決策は2倍でした:

  1. exeに独自のディレクトリへの書き込み権限を与えます*(ただし、それ自体を変更するためのアクセスは拒否します)
  2. プロセスをインプレースで実行するように ProcessRunner を変更します - 作業ディレクトリを exe が存在する場所に設定します。

2 番目の変更は次のようになります。

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 ディレクトリのように分離された/安全な場所に設定するか、さらに良いスクラッチ ディスクに設定します。そのための RAM がある場合は、RAM ドライブが最速です。

**2 番目の注意: tmp の場所が出力の最終的な宛先と同じディスクでない場合、ハード ドライブと jpegoptim がどのように機能するかにより、jpegoptim と使用している可能性のある他のコードとの間で部分的な競合状態が発生する可能性があります。その出力。特に、同じディスクを使用する場合、jpegoptim が完了すると、出力 JPEG が完成します。OS はファイル テーブルのエントリを変更しますが、ハード ドライブ上の画像のデータは既に完全に書き込まれています。tmp と宛先が別のディスクの場合、jpegoptim は OS に tmpdir から出力ディレクトリに移動するように指示して終了します。これは、jpegoptim の実行が終了した後に終了するデータ移動です。待機中のコードが十分に高速な場合、不完全な JPEG で作業を開始します。

于 2014-10-29T04:55:11.700 に答える