- PR -

Struts Action クラスで PDFダウンロード キャンセル時にエラー

1
投稿者投稿内容
SKH
会議室デビュー日: 2006/11/10
投稿数: 3
投稿日時: 2006-11-10 11:40
はじめまして。JAVA初心者です。
サーバー上のPDFファイルをダウンロードするStruts Actionクラスを作成していますが
エラーが発生し、困っております。
長文で申し訳ありませんが、原因や対策をご存知の方がいらしたら、教えてください。
よろしくお願いします。

【環境】
------------------------------------------------------------------------------
サーバー
Tomcat5.5.17
Struts1.2.9
WindowsXP Professional 2002 SP2
ブラウザ
IE6
------------------------------------------------------------------------------

【現象】
------------------------------------------------------------------------------
[ファイルのダウンロード] ダイアログ ボックスが表示され
「開く(O)」「保存(S)」をクリックした場合は、正常動作。

[ファイルのダウンロード] ダイアログ ボックスが表示され
「キャンセル」「×」をクリックした場合は、エラー。
ただし、ファイルのサイズが小さい場合(35KBまで確認)は、正常動作。
------------------------------------------------------------------------------

【エラー内容】(ブラウザには表示されず、Tomcatコンソールで確認)
------------------------------------------------------------------------------
致命的: サーブレット action のServlet.service()が例外を投げました
java.lang.IllegalStateException: getOutputStream()はこのレスポンスに対して既に呼び出されています
------------------------------------------------------------------------------

【ソース】
------------------------------------------------------------------------------
final String TMP_OUTPUT_FILE = "/WEB-INF/aa.pdf";
String tmpOutputPath = this.getServlet().getServletContext().getRealPath(TMP_OUTPUT_FILE);
File outputFile = new File(tmpOutputPath);

response.setHeader("Cache-Control", "");
response.setHeader("Pragma", "");
response.setHeader("Content-Disposition","attachment;filename=aa.PDF");
response.setContentType("application/pdf" );

FileInputStream in = new FileInputStream(outputFile);
OutputStream os = response.getOutputStream();
byte[] buffer = new byte[8192];
int bytes;

while ((bytes = in.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytes);
}
in.close();
os.close();

return null;
------------------------------------------------------------------------------

実行状況をコンソールで確認したところ、以下のことがわかりました。

●ファイルサイズが小さい場合
ダイアログボックス表示と同時に、Actionクラスが最後まで実行されて正常終了。

●ファイルサイズが大きい場合
ダイアログボックス表示後、
「開く(O)」「保存(S)」「キャンセル」「×」のいずれかのボタンを
クリックするまで、Actionクラスが待ち状態。

「開く(O)」「保存(S)」をクリックすると、Actionクラスが最後まで実行されて正常終了。
「キャンセル」「×」をクリックすると、Actionクラス再開直後にエラー。


バッファのサイズは、最初2048だったものを8192に変更してみたのですが
現象は変わりませんでした。

また、通常のサーブレットとして同じソースを実行したところ
上記の現象は発生しませんでした。

正常動作となるファイルサイズの上限値は、調査中です。

よろしくお願いします。
あしゅ
ぬし
会議室デビュー日: 2005/08/05
投稿数: 613
投稿日時: 2006-11-10 13:43
引用:

SKHさんの書き込み (2006-11-10 11:40) より:
致命的: サーブレット action のServlet.service()が例外を投げました
java.lang.IllegalStateException: getOutputStream()はこのレスポンスに対して既に呼び出されています



ブラウザ側でキャンセルすると、送信途中のTCPセッションが切断され、
ServletOutputStreamへの書き込み時にIOException発生します。
メッセージはConnection reset by peerになるはずです。

続いてアプリケーションのエラー処理でエラーページにforwardすることに
なりますが、レスポンスの送信は始めているのでエラーとなります。
「このレスポンスに対して既に呼び出されています」は後者のエラーです。

普通のHTML程度のサイズならばRSTを受け取るまでに送信が終わるので
キャンセルされてもConnection reset by peerは発生しないのですが、
ファイルのダウンロード系では頻繁に起こってしまう現象です。

数KBのレスポンスでも発生する可能性はありえますし、
放っておいても実害はないと思いますよ。

どうしてもエラーページへのforwardをやめさせたいのであれば、
ServletResponse#isCommitted()で判断できるかもしれません。
SKH
会議室デビュー日: 2006/11/10
投稿数: 3
投稿日時: 2006-11-10 14:45
早速の回答、ありがとうございます。

引用:

普通のHTML程度のサイズならばRSTを受け取るまでに送信が終わるので
キャンセルされてもConnection reset by peerは発生しないのですが、
ファイルのダウンロード系では頻繁に起こってしまう現象です。

数KBのレスポンスでも発生する可能性はありえますし、
放っておいても実害はないと思いますよ。



発生し得る現象であり、実害もないとのことで安心しました。

引用:
どうしてもエラーページへのforwardをやめさせたいのであれば、
ServletResponse#isCommitted()で判断できるかもしれません。



catchの中で、ServletResponse#isCommitted()を取得することができました。
下記のように対応しようと思います。

コード:
try{

  byte[] buffer = new byte[8192];
  int bytes;

  while ((bytes = in.read(buffer, 0, 8192)) != -1) {
    os.write(buffer, 0, bytes);
  }

} catch (Exception e) {
	
  if (response.isCommitted() == true ){
     //キャンセルボタンが押されたときの処理
  }else {
     //例外処理
  }

}



今後のためにお聞きしたいのですが
サーブレットとStrutsActionで動作が異なるのは、仕様がが異なるからなのでしょうか。
それとも今回は、たまたまStrutsでは発生していないだけなのでしょうか。

よろしくお願いします。
nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2006-11-10 14:57
引用:

SKHさんの書き込み (2006-11-10 14:45) より:
今後のためにお聞きしたいのですが
サーブレットとStrutsActionで動作が異なるのは、仕様がが異なるからなのでしょうか。
それとも今回は、たまたまStrutsでは発生していないだけなのでしょうか。



なんか誤解されているように思います。
やや語弊がありますがStrutsもServletですよ。
SKH
会議室デビュー日: 2006/11/10
投稿数: 3
投稿日時: 2006-11-10 15:46
申し訳ありません。説明不足でした。

Strutsを使用しないサーブレット(下記1)のdoPost(またはdoGet)で実行したときと、
Strutsを使用したとき(下記2)では、動作が異なるのです。

(1)
コード:
 public class downloadPdf extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {

	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) 
			throws ServletException, IOException {



(2)
コード:
public class DownloadPdfAction extends Action {

    public ActionForward execute(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {



同じプログラムでも、今回の現象が発生するのは(2)だけです。
かなり大きなサイズのPDFファイルでも、(1)では発生しないのです。
1

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