- PR -

画像のリサイズについて

投稿者投稿内容
どんたくお
ベテラン
会議室デビュー日: 2005/08/29
投稿数: 88
投稿日時: 2005-10-14 23:04
みなさん、こんにちは。


FedoreCore2
J2SDK 1.4.2_08
Tomcat5

にて、WEBアプリケーション開発を行っております。
その中にブラウザから画像をアップロードし、ディスク内に保存する機能があります。
処理的には、multipartで送られたリクエストをjakarta commons FileUploadで受け取り、ディスクに保存しております。

アップロードされるのは画像なので、画像を小さくリサイズしています。
今まではずっと、JAVAからシェルを呼び出しその中にImageMagickの処理を書き、画像を小さくしていたのですが、時々ですが画像が小さくなっていないといわれることがありました。
調査すると、アップロードされる画像と小さい画像2つあるはずなのですが、アップロードされた画像しかない場合が何度かありました。

シェルを呼び出しているので、JAVAからはシェルの中でエラーが発生した場合もキャッチすることができず、原因がわかりませんでした。

そこで、皆様にご教授いただきたいのですが、画像を小さくしたり大きくしたりするリサイズの機能は通常どのように実装するのでしょうか。
僕の場合、ImageMagickを使ってのシェル呼び出ししか思いつかなかったのですが、もう少しよいやり方がありましたら、ご教授いただけるとうれしいです。


よろしくお願いします。
ひら
ぬし
会議室デビュー日: 2005/03/04
投稿数: 260
投稿日時: 2005-10-14 23:48
引用:

どんたくおさんの書き込み (2005-10-14 23:04) より:
僕の場合、ImageMagickを使ってのシェル呼び出ししか思いつかなかったのですが、もう少しよいやり方がありましたら、ご教授いただけるとうれしいです。



私の場合、com.sun.image.codec.jpegパッケージのJPEGImageDecoder・JPEGImageEncoder
でJPEGファイルの入出力を行い、java.awt.image.AffineTransformOpで拡大縮小をすると
いうのをやったことがあります。ただし、この方法ですとJPEGしか対応できません。
javax.imageioという、もっといいパッケージも出ているようですので、それを使って
入出力をするといいかもしれませんね。

さて、一方、シェルのエラーの話が出ていますが、getErrorStreamメソッドがありまして、
エラーの発生を知る方法はあります。キャッチと書かれていますがtry catchとは
違いますね。あとはImageMagickがどのようなエラーを吐いているかが問題ですが・・・。
ちょま吉
大ベテラン
会議室デビュー日: 2004/08/04
投稿数: 112
投稿日時: 2005-10-15 10:23
Runtime#exec(String)等でコマンドを起動しているとおもいますが、
大抵、コマンドは正常終了なのか異常終了なのかを終了ステータスとして返します。Process#exitValue()にて終了ステータスが取得できそうで、この値がエラーの場合はgetInpuStreamやgetErrorStreamでエラーメッセージを取り出すなどの方法が一般的と思われます。
コマンドの終了ステータスはコマンドによって異なりますので調べておいた方が良いかと思います。


[ メッセージ編集済み 編集者: ちょま吉 編集日時 2005-10-15 10:24 ]
どんたくお
ベテラン
会議室デビュー日: 2005/08/29
投稿数: 88
投稿日時: 2005-10-17 10:17
ご返信が遅れてしまい、大変申し訳ございません。
昨日、インターネットができる環境におりませんでした。

ひらさん、ありがとうございます。
> 私の場合、com.sun.image.codec.jpegパッケージのJPEGImageDecoder・JPEGImageEncoder
> でJPEGファイルの入出力を行い、java.awt.image.AffineTransformOpで拡大縮小をすると
> いうのをやったことがあります。ただし、この方法ですとJPEGしか対応できません。
なるほど。Javaの標準ぱパッケージからもJPEGであれば、リサイズがかけれるのですね。
今回、アップロードされる画像がJPEGとGIFとPNG形式があります。

> キャッチと書かれていますがtry catchとは違いますね。
紛らわしくて申し訳ないです。
そうです。ひらさんのいわれるようにJavaでのtry catchではなく、Runtime.execで呼び出したシェルコマンドでエラーが発生した場合に、JAVA側で拾うことをさしていました。

> getErrorStreamメソッドがありまして、エラーの発生を知る方法はあります。
これは知りませんでした。一度試してみます。


ちょま吉さん、ありがとうございます。
> Runtime#exec(String)等でコマンドを起動しているとおもいますが
コードを乗せておらず、大変もうしわけございませんでした。ご推測のとおり、Runtime#execで起動しております。
> Process#exitValue()
なるほど。やっぱり全然調べきれておりませんでした・・・。
これで、エラーがおきているかは判断できそうです。


皆様、またおお助けいただきまして、ありがとうございます。
皆様よりお教えいただいたことを、実際にコードに埋め込んで様子をみさせていただこうと思います。
// しかし、リサイズがうまくいく場合とうまくいかない場合の再現性つかめておりません。

結果がわかりましたら、またご返信させていただきます。

ありがとうございました。

武澤
常連さん
会議室デビュー日: 2004/09/27
投稿数: 31
投稿日時: 2005-10-18 17:22
こんにちは、武澤です。

Runtime#exec(String)でハマった口なのでアドバイスです。
Unix+アプリケーションサーバな環境の時はちょっと、注意が必要です。
このAPIではUnixでforkとexecが内部的に呼び出されているため、一瞬ですがアプリケ
ーションサーバと同じサイズのメモリが確保されます。

もし、Tomcatの利用メモリサイズが1Gbyteだとして同時に3つのRuntime#exec(String)を
実行すると3Gbyte増えます。
Windowsではこういった仕組みでないのでWindowsで開発していて本番稼動(私はSunでし
たが)した時にメモリ不足でVMがクラッシュする現象の原因解明するまでに苦労しました。
また、Runtime#exec(String)でgetInpuStreamやgetErrorStreamをスレッド処理で非同期
にバッファをクリアしないと外部プロセスが停止するので(Windowsだけかな?)これ
も気をつけるべき点です。

以上、余計なおせっかいでした。
どんたくお
ベテラン
会議室デビュー日: 2005/08/29
投稿数: 88
投稿日時: 2005-10-19 09:43
武澤さん、ご返信いただきまして、ありがとうございます。


> Unix+アプリケーションサーバな環境の時はちょっと、注意が必要です。
> このAPIではUnixでforkとexecが内部的に呼び出されているため、一瞬ですがアプリケ
> ーションサーバと同じサイズのメモリが確保されます。
このことは全く知らなかったです。
> アプリケーションサーバと同じサイズのメモリ
ということですが、Tomcat起動時にパラメータとして渡す
-Xmx512m
オプションと同じメモリサイズが確保されるということでしょうか。

当方は、現在FedoreCore2での動作を考えております。
メモリは1GBで、
-Xmx512m
で、起動させております。
この場合、Tomcatから同じタイミングで二度execが呼び出されると、VMがメモリ不足になるということですよね。


> また、Runtime#exec(String)でgetInpuStreamやgetErrorStreamをスレッド処理で非同期にバッファをクリアしないと外部プロセスが停止
こちらも全く知りませんでした。
// すみません。知らないことが多すぎます・・・。
大変申し訳ございません。
自分の技術不足で、武澤さんからご教授いただきました
> スレッド処理で非同期にバッファをクリアしないと
という部分ですが、ストリームを取得してバッファ処理し、そしてクリアするという認識でよろしかったでしょうか。

あんまり理解しておらず、的を外しているかもしれませんが、よろしくお願いします。

しろくま
常連さん
会議室デビュー日: 2004/10/15
投稿数: 35
投稿日時: 2005-10-19 21:12
ども、しろくまです。
以前、私も同様にFileUploadで受け取った画像を縮小して保存したのですが、
シェルで別PGを呼ばず、直接縮小保存してました。
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=19714&forum=12

リサイズは別PGに任せたほうが良いのかなぁ...
武澤
常連さん
会議室デビュー日: 2004/09/27
投稿数: 31
投稿日時: 2005-10-19 21:36
まずは簡単にUNIX系のforkとexecの動きですが、
fork -> 自分と同じプロセスをコピーを作成(Tomcatですね)
exec -> 実際に実行するプロセスのヒープサイズに変更
という動きだと思ってください。(我ながら、突っ込みどころが満載な表現だ・・・)
このforkが複数重なるとTomcatと同等メモリサイズのコピープロセスが複数存在すること
になります。
どんたくおさん が危惧している-Xmx512mはOSからJVM(Tomcat)が最大で512mのメモリを
利用するという事なので、MAX時の想定をしているのであれば、想定している通りです。

※私はC言語で少量サイズの外部コマンド起動サービスを作って、JavaからSocket通信
で外部プログラムを間接的に起動させる方法を取りました。(久々のCで悪戦苦闘しましたが)


次に、外部プログラムが止まってしまう件は、Windowsだけかもしれません(未確認)
しかし、Windowsでも動作させることを考えるのであれば、考慮しておいた方が良いかもしれません。
実際に停止する原因は外部プログラムが標準出力、エラー出力に何かを出力するような場
合に、出力バッファが不足して、出力待ちになります。(ここでブロックされてしまう。)
これを解消するにはProcessクラスからgetInputStream(), getErrorStream()を別スレッド
で読み込みをすれば、バッファが溢れずにすみます。

外部コマンドの出力バッファを考慮した例:
コード:
 
import java.io.*;

public class HOGE {

StringBuffer stdBuffer = null;
StringBuffer errBuffer = null;

// try-catchが面倒なのでthrows Throwableにしたけど、実際はちゃんとtry-catchして
// エラーの後処理もやりましょう。
public int execute(String command) throws Throwable {
  Process proc = Runtime.getRuntime().exec(command);
  final InputStream std = proc.getInputStream();
  final InputStream err = proc.getErrorStream();
  Thread stdThread = new Thread() {
    public void run() {
        try {
          BufferedReader stdBr = 
                     new BufferedReader(new InputStreamReader(std));
          stdBuffer = new StringBuffer();
          String line = null;
          while((line=stdBr.readLine())!= null )
            stdBuffer.append(line).append("\n");
        } catch ( IOException ex) {
           // IO例外処理
        }
    }
  };
  stdThread.start();
  
  // 標準エラーも同じようにやる
  // ここは省略


  stdThread.join(); // 標準出力の読み込みスレッドを待つ
  errThread.join(); // エラー出力の読み込みスレッドを待つ
  return proc.waitFor();
}

}


※コンパイルしていないので、間違いは置き換えて読んでください。

スキルアップ/キャリアアップ(JOB@IT)