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

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

ただいまの
回答率

90.45%

  • Java

    16221questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

poiでwb.write(out)する際の処理が非常に重い

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 7,809

tera1

score 72

お世話になっております。

poiでエクセルに書き込み(wb.write(out))を行った際の処理が
非常に重く、30秒以上かかっております。

コードは以下になりますが、高速化させることはできませんでしょうか。

protected void doDownload(HttpServletResponse response) {


        String filePath = getServletContext().getRealPath("/WEB-INF/excel/donwload.xlsx");

        ZipSecureFile.setMinInflateRatio(0);

        try {
            wb = new XSSFWorkbook(filePath);
        } catch (IllegalStateException e) {
            System.out.println("テンプレートのエクセル読み込み時に例外が発生しました。エクセルファイル名が間違っている可能性があります。");
        } catch (IOException e) {
            e.printStackTrace();
        }
        sheet = wb.getSheet(getSheetName());

        response.setContentType("application/octet-stream");
        String fileName = null;
        try {
            fileName = new String(getExcelFileName().getBytes("Windows-31J"), "ISO-8859-1");
        } catch (UnsupportedEncodingException e1) {
            e1.printStackTrace();
        }
        response.setHeader("Content-Disposition", "attachments; filename=\"" + fileName + "\"");
        response.setHeader("Cache-Control", "public");
        response.setHeader("Pragma", "public");

        try {
            OutputStream out = new BufferedOutputStream(response.getOutputStream());
            wb.write(out);
        } catch (IOException e) {
                              e.printstactrace();
        } finally {
            try {
                wb.close();
            } catch (IOException e) {
                              e.printstactrace();
            }
        }
    }

追記

(1)ボトルネック部分ですが、上記コードのwb.write(out)の部分となります。それ以外は、時間はかかっていない状況となります。
なお、wb.write(out)はローカルにあるファイルをローカルにダイアログでダウンロードで返しています。

(2)ローカルでホストはMacゲストがWin7の64bit メモリ8Gで動作させていてます。

(3)jvmのヒープヒープサイズは以下に設定しております。
-Xmx2g
-Xms2048m
-Xmx4098m

(4)該当のエクセルファイルは3M程度のエクセルを読み込みこむ形となります。
そのファイルに対して、データを横10列のものもあれば、150列のものもあり、行数は、数千行~数万行を、書き込む形となります。
当該重いのは、行数は、数千行ですが、横が150列程度あります。
数式が多く設定されていまして、また細部にこだわる仕様のため、書式も多く設定されております。

追記2
CPU/memory監視状況を添付します。
メモリなど多めに割り当てているため、
マシンに負荷はかかっていないですが、
エクセルへの書き込みとそのダウンロードが非常に重い状況です。

CPU/memory監視状況

  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • eripong

    2016/06/15 16:48

    操作するコードがない様に見えますが、高速化の検討に必要と思うので、省略しているのであれば、できるかぎり提示してください。また、30秒はかなり長いですが、download.xlsxのファイルサイズはどのくらいですか?
    JVMのヒープサイズは?ハードのスペックはどのくらいでしょうか?
    ネットワーク的には離れていますか?あるいはローカルマシン上?

    キャンセル

回答 4

check解決した方法

+2

皆様

ご回答いただき誠にありがとうございます。

ボトルネック部分について、原因がわかりましたので共有させていただきます。

原因は、Virtualboxの共有フォルダが原因でした。
何年も前に同様の経験がありましたが、失念しておりました。

ふと、Eclipseを立ち上げた際、ワークスペースがホスト側のフォルダになっており気づくのが遅くなりました。。

ホスト側でなく、ゲストで動かしているのでゲスト側のワークスペースにしたところ、大幅に改善されました。

恐縮です。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/06/17 10:20

    改善されて良かったです。後学のために伺いたいのですが、poiの内部ではファイルアクセスを頻繁に行っていると言うことでしょうか?あるいはクラスロード周りでのファイルアクセスが起きていた?分かれば教えていただけませんか?

    キャンセル

  • 2016/06/17 10:29

    お世話になっております。
    poi内部の解析はしなかったのですが、起きた事象から推測ではありますが、
    データをExcelに貼り付けた後、書込(wb.write(out))に行った際のファイルアクセスが
    ゲストからホスト側のフォルダへの書込となっており、
    その場合、以下の記事等でも書かれているように、
    パフォーマンスが大幅に低下するようです。
    http://webradar.info/2014/03/06/slowly-rails-in-vagrant/
    今回は、それが原因でした。

    キャンセル

  • 2016/06/17 10:45

    返信ありがとうございます。

    wb.write(out)はレスポンスへの書き込みなので、ネットワークアクセスであって、ファイルに直接出力しているのではないという認識でした。どちらかというと、ブラウザがファイルに書き込んでいるのかと。ブラウザのファイル書き込みを待つとこういう見え方になるのかも?

    とはいえ、解析に時間をかけないと分からなそうなので、ひとまず了解です。

    キャンセル

  • 2016/06/17 12:08

    wb.write(out)の処理に必要なクラスのロード等でホスト側にアクセスしていたのかもしれませんね。

    キャンセル

  • 2016/06/17 12:13

    そうすると、2回目以降は早いのか?とか疑問が出てきますね。興味深い状況ではあります。

    キャンセル

0

提示されたソースを見る限り、改善の余地はないと思われます。
POIのソースを入手し、ボトルネックを調べるか、Excelファイル自体を最適化するかでしょうか。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

0

ダメ元ですが、
アプリサーバーのJVMのメモリーに余裕があるなら、
BufferedOutputStreamのバッファーサイズを増やしてみるとか。
おそらくExcelの書き出し側で遅いので改善は見込めないと思いますが。

OutputStream out = new BufferedOutputStream(response.getOutputStream(), 65536); // デフォルトは8192

...

本格的に調査するなら、プロファイリングを行って、
特に遅い箇所(ボトルネック)を突き止める必要があります。
JDKに Java VisualVM (jvisualvm)というプロファイラーが標準搭載されていますので、
それを使ってみるのも手です。

ただプロファイリングは難易度が高いですし、時間もかかります。

...

あとは代替策を取るという手もあります。

Excelを作るのとダウンロードを分けるとか。
これはこれで仕組みを作るのはちょっと手間ですけどね。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/06/15 22:43

    ご回答ありがとうございます。
    jvisualvm というのがあるのですね。勉強になります。
    こちらを使って確認したところ、ひっ迫した状況ではありませんでした。
    また、バッファサイズの変更で変化があらわれませんでした。
    Poiのwriteするときがどうしても重いようです。





    キャンセル

  • 2016/06/16 19:22

    質問追記内容を見ました。GC以外でCPU50%使っているようなので、純粋にExcelファイル生成などの計算で時間がかかっているようですね。argiusさんの言うとおり、プロファイルしてボトルネック特定して改善するか、Excelを作るのとダウンロードを分けるののどちらかかなと思います。

    キャンセル

  • 2016/06/16 21:45

    プロファイルするなら、一時的にでもTomcatのJavaのバージョンを上げた方が良いかも知れません。Visual VMでプロファイラタブが表示されるはずです。

    キャンセル

  • 2016/06/16 21:51

    keetonさん

    Excelデータの詳細とVisualVMの画像を追記していただきありがとうございます。
    サーブレットのダウンロードに関係なく、単純にExcelデータが大きいので処理に時間がかかってしまっているような気がしますね。
    POIでは限界があるのかも知れません。


    eripongさん

    コメントありがとうございます。
    やはりそうなりますかね。

    キャンセル

0

いくつか気になる点がありましたので、それも合わせて。

ZipSecureFile.setMinInflateRatio(0); 
デフォルト値は1.0ですが、0にした理由はありますでしょうか。

POIのバージョンも合わせて記載しておきましょう。
というのもPOIのバージョンによって推奨されない実装方法があります。

以下はpoi-ooxml-3.14版を使ったサーブレット+エクセルの読み込み例です。

String filePath = getServletContext().getRealPath("/WEB-INF/download.xlsx");

InputStream inp = new FileInputStream(filePath);
Workbook wb;
try {
    wb = WorkbookFactory.create(inp);
} catch (EncryptedDocumentException e) {
    throw new ServletException(e);
} catch (InvalidFormatException e) {
    throw new ServletException(e);
}

記載してくださったサーブレットのソースでは、POIを使って読み込み→そのままサーブレットから出力していますので、30分もかかる場合は以下の懸念があります。

  • 単純にファイルサイズが巨大(数百メガ~)なため、ファイルの読み込みに時間がかかる
  • セルに記載しているデータが非常に多い

ファイルサイズが大きいものの場合は、もし可能ならシートを分けて処理するなどの方法がありますが、要件次第でしょうか。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/06/15 23:09

    ご回答ありがとうございます。
    poiのバージョンは3.14です。

    ```
    ZipSecureFile.setMinInflateRatio(0);
    ```
    上記は、以下エラーメッセージが出まして、「zipbomが見つかった時に拡張が云々」ということで、あまりよくわかりませんないようでしたが、ZipSecureFile.setMinInflateRatioでlimitを調整できるということで、0にしてみましたら、エラー発生せず動作した為いれています。

    Caused by: java.io.IOException: Zip bomb detected! The file would exceed the max. ratio of compressed file size to the size of the expanded data. This may indicate that the file is used to inflate memory usage and thus could pose a security risk. You can adjust this limit via ZipSecureFile.setMinInflateRatio() if you need to work with files which exceed this limit. Counter: 827392, cis.counter: 8192, ratio: 0.009900990099009901Limits: MIN_INFLATE_RATIO: 0.01

    ちなみに、出力時間についてですが、30分ではなく、30秒となります。
    その程度は仕方ない気がしてきました。


    キャンセル

  • 2016/06/16 00:28

    なるほど詳細にありがとうございます。時間については失礼しました。
    ファイルサイズによっては多少時間はかかりますね。特にセルに書かれているデータ量が多い場合や、罫線が多い場合も読み込みに時間がかかります。

    キャンセル

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

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

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

  • Java

    16221questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。