0

Java Web サービスに次のコードがあります。

public boolean makeFile(String fileName, String audio)
    {
        if (makeUserFolder())
        {
            File file = new File(getUserFolderPath() + fileName + amr);
            FileOutputStream fileOutputStream = null;
            try
            {

                file.createNewFile();
                fileOutputStream = new FileOutputStream(file);
                fileOutputStream.write(Base64.decode(audio));

                return true;

            }
            catch(FileNotFoundException ex)
            {
                return false;
            }
            catch(IOException ex)
            {
                return false;
            }
            finally{
                try {
                    fileOutputStream.close();
                    convertFile(fileName);
                } catch (IOException ex) {
                    Logger.getLogger(FileUtils.class.getName()).log(Level.SEVERE, null, ex);
            }
}

        }
        else
            return false;

    }

    public boolean convertFile(String fileName)
    {
        Process ffmpeg;
        String filePath = this.userFolderPath + fileName;
        try {
            ProcessBuilder pb = new ProcessBuilder("ffmpeg","-i",filePath + amr,filePath + mp3);
            pb.redirectErrorStream();
            ffmpeg = pb.start();
        } catch (IOException ex) {
            return false;
        }
        return true;
    }

以前は機能していましたが、何らかの理由で ffmpeg 変換を実行できなくなりました。ファイルに問題があると思いましたが、ターミナルからコマンドを実行した後、エラーは発生しませんでした。おそらく権限の問題だと思いましたが、ファイルを保存しているフォルダーにすべての権限が付与されています。プロセスの実行後に入力 BufferedReader ins が null に設定されていることに気付きました。何が起こっているのか分かりますか?

4

1 に答える 1

5

まず第一に、あなたのコードのちょっとした問題... を作成するときは、FileOutputStreamではなく文字列を使用してそれを作成します。.FileFileFileOutputStreamFile

もう 1 つの小さな問題は、オーディオ ファイルを書き出すときに、それをブロックで囲みtry、出力ストリームをブロックで閉じる必要があるという事実finallyです。プロジェクトに新しいライブラリを追加することが許可されている場合は、すべてのダーティ リソース管理を処理するmethod を持つGuavaを使用できます。Files.write(byte[],File)

明確なバグのように見える唯一のことは、ffmpeg のエラー ストリームを無視しているという事実です。ffmpeg の stdout で入力待ちをブロックしている場合は、機能しません。

このバグに対処する最も簡単な方法は、ProcessBuilder代わりに を使用することですRuntime

ProcessBuilder pb = new ProcessBuilder("ffmpeg","-i",filePath+amr,filePath+mp3);
pb.redirectErrorStream(); // This will make both stdout and stderr be redirected to process.getInputStream();
ffmpeg = pb.start();

このように開始すると、現在のコードは両方の入力ストリームを完全に読み取ることができます。stderr が、読み取っていないために表示できなかったエラーを隠していた可能性があります。

それが問題でない場合は、ffmpeg で絶対パスを使用することをお勧めします...つまり:

String lastdot = file.getName().lastIndexOf('.');
File mp3file = new File(file.getParentFile(),file.getName().substring(0,lastdot)+".mp3");
ProcessBuilder pb = new ProcessBuilder("ffmpeg","-i",file.getAbsolutePath(),mp3file.getAbsolutePath());
// ...

それがうまくいかない場合は、ffmpeg も絶対パスに変更します (パスの問題を除外するため)。

編集:さらなる提案。

必要な場所で使用できるように、私は個人的にコードの記述を独自のメソッドにリファクタリングします。言い換えれば:

public static boolean write(byte[] content, File to) {
    FileOutputStream fos = new FileOutputStream(to);
    try {
        fos.write(content);
    } catch (IOException io) {
        // logging code here
        return false;
    } finally {
        closeQuietly(fos);
    }
    return true;
}
public static void closeQuietly(Closeable toClose) {
    if ( toClose == null ) { return; }
    try {
        toClose.close();
    } catch (IOException e) {
        // logging code here
    } 
}

メソッドにした理由はcloseQuietly(Closeable)、そのように閉じないと、close()メソッドから例外がスローされる可能性があり、その例外によって、元々スローされた例外がわかりにくくなる可能性があるためです。これらをユーティリティ クラスに入れると (コードを見て、現在含まれているクラスの名前は FileUtils だと思います)、ファイル出力を処理する必要があるときはいつでも、アプリケーション全体でそれらを使用できます。

これにより、ブロックを次のように書き換えることができます。

File file = new File(getUserFolderPath() + fileName + amr);
file.createNewFile()
write(Base64.decode(audio),file);
convertFile(fileName);

これを行うべきかどうかはわかりませんが、ffmpeg プロセスが完了したことを確認したい場合は、完了しffmpeg.waitFor();たことを確認してください。その場合は、ffmpeg.exitValue();正常に完了したことを確認する必要があります。

もう 1 つの方法は、完了したら、出力内容をログ ファイルに書き込んで、何かが起こった場合に備えて何が起こったかを記録することです。

于 2011-02-17T23:46:52.853 に答える