質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.64%

ファイルダウンロード処理で例外「getWriter()はこのレスポンスに対して既に呼び出されています」

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 3,671

bluescat

score 16

いつもお世話になります。
初歩的なことかもしれませんが、行き詰ってしまったので質問させてください。

画面からボタンを押したときにajax通信でJava側の処理を呼び出して
ファイルをダウンロードする処理を実装しているのですが、
「resp_.getOutputStream();」を実行したときに下記例外が発生してしまいます。

java.lang.IllegalStateException: getWriter()はこのレスポンスに対して既に呼び出されています

関連の質問を確認したところ、

一回のコネクションでgetWriterかgetOutputStreamのどちらしか選択できない

と記載されていたので、リソースを解放してあげればよいのだと思うのですが、
例外発生行の直前で「res.getWriter().close();」を入れても、今度はその行で同様の例外が発生するようでした。
※res:HttpServletResponseの変数

リソースを解放する方法についてご教示いただけないでしょうか。

Java8、Tomcat7です。

【参考にした記事】
https://hiyokko-se.com/java_filedownload

【関連の質問】
https://teratail.com/questions/115888

$(document).ready(function(){
    $('#monthry_report_result_10051').on('click', function(){
        var ajaxUrl = '/<コンテキスト名>/xxx/ajax/MonthryReportServices/printMonthryReport';
        var param = { code : 10051, title : 'リスト' };
        var callbackFunc = function(html){
            if($(html).hasClass('error')){
                $('.ajaxError').html($(html));
            }else{
                isDisabled = false;
            }
        }
        ajaxFunc.ajax('post','html', ajaxUrl, param, callbackFunc);
    });
});
/*非同期通信用*/
var ajaxFunc = {
    ajax : function(type, dataType, url, data, successFunc, errorFunc, traditional, async){
    if(traditional=="undefined") traditional = false;
    if(async=="undefined") async = true;
        var xhr = $.ajax({
            type : type,
            url : url,
            data : data,
            dataType : dataType,
            cache : false,
            timeout : 60000,
            scriptCharset : "UTF-8",
            traditional : traditional,
            async : async,
            success : function(data){
                if(dataType=="json" && data!=null && eval(data).ajaxErrorCode!=null){
                    var setting = eval(data);
                    AjaxFunc.ajaxError(setting);
                }else if((dataType=="" || dataType=="html" || dataType=="text") && data.match(/ajaxErrorCode/)!=null){
                    var setting = eval("("+data+")");
                    AjaxFunc.ajaxError(setting);
                }else{
                    if (successFunc) {
                        if (typeof successFunc == 'string') {
                            eval(successFunc);
                        } else if (typeof successFunc == 'function') {
                            successFunc(data);
                        }
                    }
                }
            }
}
// ajaxから呼び出されるクラス
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import jp.co.xxx.wes.ajax.portal.MonthryReportServices;
import jp.co.xxx.wes.com.ConnectionPool;
import jp.co.xxx.wes.com.HtmlPage;
import jp.co.xxx.wes.sfa.document.DocumentInfoPage;
public class AjaxUrlMapping extends AjaxPage{

    public void mapping(){
    }

    /**
     * URLから実行するServicesを特定する
     * 
     * @param request HttpServletRequest
     * @return
     * @throws Exception 
     */
    public HtmlPage set(HttpServletRequest request) throws Exception{
        String requestUrl = request.getRequestURI();
        ajaxUrl = requestUrl.split("/ajax/")[1];
        serviceUrl = ajaxUrl.split("/")[0];

        if(serviceUrl.equals(MonthryReportServices.SERVICES_NAME)) {
            return new MonthryReportServices();
        }
    }
}
// Ajax Page
package jp.co.xxx.wes.ajax;

import jp.co.xxx.wes.com.HtmlPage;
import jp.co.xxx.wes.sfa.security.MaskingWriter;
public abstract class AjaxPage extends HtmlPage{

    /** Ajax使用時にログイン状態を判定する */
    public static final String PARAM_ISNOT_LOGIN_AJAX_ALLOW = "isNotLoginAjaxAllow";

    private Exception exception;
    private HtmlPage page_;

    protected String ajaxUrl;
    protected String serviceUrl;
    protected String methodUrl;

    public void print(MaskingWriter pw) throws Exception{
        if(exception != null){
            this.printError(pw);
        }else{
            if(page_ != null){
                page_.print(pw);
            }else{
                pw.println("success");
            }
        }
    }

    public abstract void mapping() throws Exception;

    private void printError(MaskingWriter pw){
        exception.printStackTrace();
        StackTraceElement[] stackTraceArray =  exception.getStackTrace();
        for(StackTraceElement stackTrace : stackTraceArray){
            pw.println(stackTrace);
        }
    }

    public void createUrl(){
        String requestUrl = req_.getRequestURI();

        ajaxUrl = requestUrl.split("/ajax/")[1];
        serviceUrl = ajaxUrl.split("/")[0];
        methodUrl = ajaxUrl.split("/")[1];
    }

    public void setException(Exception e){
        exception = e;
    }
    public void setPage(HtmlPage page){
        page_ = page;
    }

    public HtmlPage getPage(){
        return page_;
    }


}
public class MonthryReportServices extends AjaxUrlMapping {

    public static final String SERVICES_NAME = "MonthryReportServices";
    public static final String METHOD_PRINT_MONTHRY_REPORT = "printMonthryReport";


    private static String portletTitle = "";
    private static long portletCode = 0;

    private void init() {
        portletTitle = req_.getParameter("portletTitle");
        portletCode = Long.parseLong(req_.getParameter("portletCode"));
    }
    public void mapping() {
        try {
            init();
            if(this.methodUrl.equals(METHOD_PRINT_MONTHRY_REPORT)){
                try {
                    this.downloadReport();
                } catch (Exception e) {
                    this.setPage(printError(e, AjaxErrorPage.AJAX_ERROR_CODE_FAILED_GET));
                }
            }
        } catch (Exception e) {

        }
    }

    public void downloadReport() throws Exception {

        // ダウンロード対象ファイルの読み込み用オブジェクト
        FileInputStream fis = null;
        InputStreamReader isr = null;

        // ダウンロードファイルの書き出し用オブジェクト
        ServletOutputStream os = null;
        OutputStreamWriter osw = null;
        try {
            // ダウンロード対象ファイルのFileオブジェクトを生成
            File file = new File(downloadFilePath);

            if (!file.exists() || !file.isFile()) {
                // ファイルが存在しない場合のエラー処理
                return;
            }

            // レスポンスオブジェクトのヘッダー情報を設定
            res.setContentType("application/octet-stream");
            res.setHeader("Content-Disposition",
                            "attachment; filename=\"test.xlsx\"");

            // ダウンロード対象ファイルの読み込み用オブジェクトを生成
            fis = new FileInputStream(file);
            isr = new InputStreamReader(fis, "ISO-8859-1");

            // ダウンロードファイルの書き出し用オブジェクトを生成
            //↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
            os = res.getOutputStream();   //ここで例外が発生する
            //↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
            osw = new OutputStreamWriter(os, "ISO-8859-1");

            // IOストリームを用いてファイルダウンロードを行う
            int i;
            while ((i = isr.read()) != -1) {
                osw.write(i);
            }
        } catch (FileNotFoundException e) {
            // 例外発生時処理
        } catch (UnsupportedEncodingException e) {
            // 例外発生時処理
        } catch (IOException e) {
            // 例外発生時処理
        } finally {
            try {
                // 各オブジェクトを忘れずにクローズ
                if (osw != null) {
                    osw.close();
                }
                if (os != null) {
                    os.close();
                }
                if (isr != null) {
                    isr.close();
                }
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                // 例外発生時処理
            }
        }
    }
}
//Servlet側で実行しているメソッド
    private static MaskingWriter getWriter(HttpServletRequest req,
            HttpServletResponse resp,
            ResourceBundle res,
            String strPage)
    throws java.io.IOException {

        MaskingWriter pw;

        if (isUseHtmlCompress(req, res)) {

            String strHtmlCompressEncode = ResourceManager.getHtmlCompressEncode(null);

            OutputStream out = resp.getOutputStream();
            GZIPOutputStream gzipOut = new GZIPOutputStream(out);
            OutputStreamWriter writer = null;

            try {
                writer = new OutputStreamWriter(gzipOut,strHtmlCompressEncode);
            } catch (java.io.UnsupportedEncodingException e){
                e.printStackTrace();
                writer = new OutputStreamWriter(gzipOut, "SJIS");
            }

            PrintWriter tPW = new PrintWriter(writer,false);
            pw = new MaskingWriter(tPW);
            resp.setHeader("Content-Encoding","gzip");

        }else{
            PrintWriter tPW = resp.getWriter();
            pw = new MaskingWriter(tPW);
        }

        // HTML をファイル出力するときには、以下の行を有効にする
        /*
          pw = new jp.co.xxx.wes.driver.PrintWriterWrapper(pw, strPage,
          "htmllog.txt");
         */
        return pw;
    }
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • bluescat

    2019/02/25 10:15

    申し訳ありません、おっしゃる通り、非公開パッケージの一部です。
    社名は伏せていただけると助かります。

    キャンセル

  • jimbe

    2019/02/25 10:29

    でしたら, 外部のものには仕様が分かりかねます. 仕様書をご確認の上, 開発元にお問い合わせ頂くのが良いかと思います.

    キャンセル

  • bluescat

    2019/02/25 10:39

    お手数をお掛けしまして申し訳ありません。。
    内部の有識者に確認してみます。

    キャンセル

回答 1

checkベストアンサー

0

既に呼び出されているメソッドの結果をこのメソッドで使用すれば良いと思います.

res.getWriter 自体で 2度目以降の呼び出しをガードしているものと思います.
また, getWriter で取れたと致しましても, それを close することはクライアントとの通信を切断してしまい, 以降の通信は出来なくなると想像します.
(再度 getWriter を呼び出せば通信が復活する(ように見える様, closeはフラグ操作のみ)という実装も不可能では無いと思いますが, そのような実装は稀かと思います)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.64%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • トップ
  • Javaに関する質問
  • ファイルダウンロード処理で例外「getWriter()はこのレスポンスに対して既に呼び出されています」