バイナリファイルでOutOfMemoryエラー

このQ&Aのポイント
  • バイナリファイルのアップロード時に発生するOutOfMemoryエラーの解決方法
  • バイナリファイルのアップロード時にJava heap spaceが不足する問題の対処法
  • バイナリファイルのアップロード時に50MB以上のファイルでOutOfMemoryエラーが発生する場合の対策
回答を見る
  • ベストアンサー

バイナリファイルでOutOfMemoryエラー

お世話になります。 ブラウザ画面からファイルをアップロードし、そのバイナリデータを返すメソッドを作成したのですが、約50MB以上のファイルを使用するとOutOfMemoryエラーとなりJava heap spaceが足りないといわれてしまいます。ヒープサイズをあげればエラーはしなくなるとはおもいますが、根本的な解決にはならないと思います。 public byte[] upload(FormFile ff) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); InputStream is = ff.getInputStream(); BufferedInputStream bis = new BufferedInputStream(is); byte[] byteData; try { int data = 0; byte[] buffer = new byte[1024]; while ((data = bis.read(buffer) != -1) { baos.write(buffer, 0, data); } byteData = baos.toByteArray(); } catch (IOException e) { throw e; } finally { if (null != bis) { bis.close(); } } return byteData; } 以上のようなメソッドなのですが、おかしい点はありますでしょうか。 指摘していただけると助かります。 よろしくお願いいたします。

  • rihm
  • お礼率76% (19/25)
  • Java
  • 回答数2
  • ありがとう数2

質問者が選んだベストアンサー

  • ベストアンサー
回答No.2

iBatisは、BLOBに対してデフォルトではbyte配列で対応するので、 ちょっと大きいデータを扱うと、この問題が発生するんですよね。 BLOBとStreamをマッピングする、カスタムTypeHandlerを作成して対応します。 iBatis側で用意されていないのは、DBによってBLOBの扱いが違うから。

その他の回答 (1)

  • salsberry
  • ベストアンサー率69% (495/711)
回答No.1

50MBのファイルなら50MB全部を読み出してからbaos.toByteArray()を実行していますよね。どうしてもファイル全体を読み込み終えてからでないと処理できないような内容ならば、ファイル全体を保持してもOutOfMemoryにならないだけの大きさのヒープを確保するしかありません。 upload()がreturnした配列はその後どのように使われるのですか? ファイルを最後まで読み出してから一気に処理するのではなく、少しずつ読み出しては処理する(処理し終えた部分はメモリ上から消す)のを繰り返すという形には変更できませんか? そのように書き換えられる種類の処理であればそれが根本的な解決策です。 そういう書き換えができない場合に少しでも限界を遠ざけるにはどうしたらいいか、ですが、ファイルの長さは読み出し前には分からないでしょうか? ByteArrayOutputStreamに一度溜め込んでからtoByteArray()で変換するのはメモリの無駄です。ByteArrayOutputStreamを使うのをやめて、ファイルの長さ分のbyte配列をnewで初めから確保し、bis.read()でそのbyte配列に直接読み込むようにすれば大幅な節約になるはずです。それでも、ファイル全体をメモリ上に読み込もうとする限り200MBのファイルを扱おうとしたらOutOfMemoryになるかもしれません。

rihm
質問者

お礼

回答ありがとうございます。 取得したbyte配列はエンティティに格納してDBに格納しようとしています。 バイナリデータをDBに格納するのが必要なためByteArrayOutputStreamを使っていました。 InputStreamをそのまま格納できれば一番早いのかと思ったのですがiBatisでそれができるのかわからなかったからです。 >ByteArrayOutputStreamを使うのをやめて、ファイルの長さ分のbyte配列をnewで初めから確保し、bis.read()でそのbyte配列に直接読み込むようにすれば大幅な節約になるはずです。 これをちょっと試してみようと思います。 ありがとうございます。

関連するQ&A

  • ByteArrayOutputStreamでの出力について

    以下のソース(抜粋)でこのreturnの箇所で「型の不一致」エラーが出てしまいます。最後にこのクラスのこのメソッドからServletへ値を戻したいのですが。 メソッドのデータ型はbyteですし、Servlet側の受容れる為の変数もbyte型にしています。 経験不足で原因が見つけられません。どなたかご教授をお願い致しますm(__)m Document document = DocumentBuilderFactory.newInstance() .newDocumentBuilder() .getDOMImplementation() .createDocument("", "rt", null); ~文字列からDOMを使ってxmlの生成処理~ Transformer transformer = TransformerFactory.newInstance() .newTransformer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); transformer.transform(new DOMSource(document),new StreamResult(baos)); byte[] buff = baos.toByteArray(); return buff;

    • ベストアンサー
    • Java
  • 固定長データのbyteスキップについて

    バイナリ入出力でご質問がございます。 下記の例のように各レコードにある 先頭5byte付与されたデータを スキップ(破棄)して 各レコード10byteずつ読み込みたいのですが なにかサンプルデータもしくは アドバイス頂けないでしょうか? どうぞよろしくお願い致します。 例 1レコード 10byte (先頭5byte付与) LLLLL1234567890 LLLLL1234567890 LLLLL1234567890 LLLLL1234567890 LLLLL1234567890 ↓ 1234567890 1234567890 1234567890 1234567890 1234567890 import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.nio.ByteBuffer; import java.io.ByteArrayOutputStream; public class dat{ public static void main(String[] args) { String inputFileName = ""; String outputFileName = ""; // ファイルオブジェクトの生成 File inputFile = new File(inputFileName); File outputFile = new File(outputFileName); try { FileInputStream fis = new FileInputStream(inputFile); BufferedInputStream bis = new BufferedInputStream(fis); FileOutputStream fos = new FileOutputStream(outputFile); BufferedOutputStream bos = new BufferedOutputStream(fos); byte[] buf = new byte[17]; int len = 0; while ((len = bis.read(buf, 0, 17)) == 17) { bos.write(buf, 0, 17); } bos.flush(); bos.close(); bis.close(); } catch(Exception e) { e.printStackTrace(); } }

    • ベストアンサー
    • Java
  • 文字のバイトサイズの取得

    25バイト以上ある文字配列を25バイトまでに切り取りたいのです。 全て半角英数ならstr.substring(0 ,25)ですみますが 日本語も入っているのでsubstringが使えません。 そこで,バイト配列を使おうと思ったのですがうまくいきません。どうかお願いします。 例) String E = "AAAAAAAAAAAAAあああああああ"; byte S[] = new byte[100]; S=E.getBytes(); ByteArrayOutputStream out = new ByteArrayOutputStream(); for(int n = 0; n < 25 ; ++n){ out.write(S[n]); } System.out.println(out.toByteArray()); 結果として"AAAAAAAAAAAAAあああ"が出力されてほしい

    • ベストアンサー
    • Java
  • レコード長からのbyteスキップ設定について

    QNo.8229324から あらたに新規でご質問させて頂きます。 1レコードごとに10byteあったとします。 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 レコード長からスキップ設定を35byteと設定したとします。 40byteごとに頭の付与された5byteをスキップさせたいのですが なにかサンプル及びアドバイス頂けますでしょうか? 仕様案 (1)まずはレコード先端の5byteをスキップ (2)次は35byteを超えた時点で5byteスキップを繰り返し。 どうぞ宜しくお願い致します。 import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.nio.ByteBuffer; import java.io.ByteArrayOutputStream; public class dat{ public static void main(String[] args) { String inputFileName = ""; String outputFileName = ""; // ファイルオブジェクトの生成 File inputFile = new File(inputFileName); File outputFile = new File(outputFileName); try { FileInputStream fis = new FileInputStream(inputFile); BufferedInputStream bis = new BufferedInputStream(fis); FileOutputStream fos = new FileOutputStream(outputFile); BufferedOutputStream bos = new BufferedOutputStream(fos); byte[] buf = new byte[17]; int len = 0; while ((len = bis.read(buf, 0, 17)) == 17) { bos.write(buf, 0, 17); } bos.flush(); bos.close(); bis.close(); } catch(Exception e) { e.printStackTrace(); } }

    • ベストアンサー
    • Java
  • java でソケット通信をするとき

    javaでソケット通信をするとき、 //データ受け取り byte[] buffer = new byte[2048]; try { int size = 0; while (size <= 0) { size = is.read(buffer); } receiveStr = new String(buffer, 0,size, "UTF8"); } catch (IOException e) { e.printStackTrace(); } のようにしていますが、これだと文字数にして一度におくれる容量はどのくらいでしょうか? また、それらを増やすにはどうしたらいいでしょうか?

    • ベストアンサー
    • Java
  • javaのsocket通信で20KB以上の文字列を

    javaのsocketについての質問です。サーバーとandroid端末で文字列のやりとりしています。 文字列は長いので、圧縮をかけ、android端末側で解凍を行っています。 そこで問題がおきました。文字データ量(20KB)が大きくなると、通信が完了せずに途中で止まってしまうのです。そこで文字列を分割(1KB毎)して送るなどを行い、しのいでおりましたが根本的の解決にはいたっていません。 InputStreamReader 、 BuffererdReader あたりを考えればいいと思うのですが、いまいち組み方がよくわからない状態です。 どなたか教えていただけないでしょうか? ============================= サーバー(PC側ソースコード)DataCompression は文字列を送って圧縮をかける独自のクラスです。 public class DataExchange extends Thread{ /* android端末とデータのやり取りをする */ private boolean DEBUG_FLAG = true; // private boolean DEBUG_FLAG = false; private InputStream is; private OutputStream os; private String sendStr; private String receiveStr; public DataExchange(InputStream is, OutputStream os, String sendStr) { this.is = is; this.os = os; this.sendStr = sendStr; } public void run() { //送信データ圧縮 DataCompression dc = new DataCompression(); byte[] sendByte = dc.getDataCompression(sendStr); //データ送信 try { os.write(sendByte); os.flush(); } catch (IOException e) { e.printStackTrace(); } if (DEBUG_FLAG) { System.out.println(os + " データ送信完了 = " + sendStr); } //データ受け取り byte[] buffer = new byte[1024]; try { int size = 0; while (size <= 0) { size = is.read(buffer); } receiveStr = new String(buffer, 0,size, "UTF8"); } catch (IOException e) { e.printStackTrace(); } if (DEBUG_FLAG) { System.out.println(is + " データ受信完了 = " + receiveStr); } } public String getReceiveStr() { return receiveStr; } } ========================================= android端末側ソースコード public class DataExchange extends Thread{ /* android端末とデータのやり取りをする */ private InputStream is; private OutputStream os; private String sendStr; private String receiveStr; public DataExchange(InputStream is, OutputStream os, String sendStr) { this.is = is; this.os = os; this.sendStr = sendStr; } public void run() { //データ受け取り byte[] buffer = new byte[4096]; try { int size = 0; while (size <= 0) { size = is.read(buffer); } receiveStr = new String(buffer, 0,size, "UTF8"); } catch (IOException e) { e.printStackTrace(); } /* 解凍 */ Inflater decompresser = new Inflater(); decompresser.setInput(buffer); int count; ByteArrayOutputStream decompos = new ByteArrayOutputStream(); while (!decompresser.finished()) { try { count = decompresser.inflate(buffer); decompos.write(buffer, 0, count); } catch (DataFormatException e) { e.printStackTrace(); } } receiveStr = decompos.toString(); System.out.println("データ受信完了 = " + receiveStr); //データ送信 try { byte[] sendByte = sendStr.getBytes("UTF8"); os.write(sendByte); os.flush(); } catch (IOException e) { e.printStackTrace(); } System.out.println("データ送信完了 = " + sendStr); } public String getReceiveStr() { return receiveStr; } }

    • ベストアンサー
    • Java
  • Oracle BLOB→Byte→.tif の変換

    こんばんわ。 タイトル通り、OracleのBLOB型の画像データをjavaのByte型に変換し、それをtiffファイルに変換したいのですが、Javaを始めたばかりで調べてもわかりませんでした。みなさんのお力をお借りしたいのですが・・・。 とりあえず、以下のソースでBLOB型のデータを取得し、byte型に変換まではできたのですが、これからこのバイトデータを.tifファイルへと変換するのがどうしてもわかりません。 ご回答お願いいたします。 ---------------------------------------------------------------- public class Image{ private byte[] imgData; public Blobconvert() { } public Blobconvert(byte[] image) { this.imgData = imgData; } public Blobconvert(InputStream imgData) throws IOException { byte[] buf = new byte[4096]; ByteArrayOutputStream bos = new ByteArrayOutputStream); while (true) {    int len = imgData.read(buf); if (len == -1) break; bos.write(buf, 0, len); } this.imgData = bos.toByteArray(); bos.close(); } public Blobconvert(Blob imgData) throws SQLException, IOException { this(imgData.getBinaryStream()); } } ---------------------------------------------------------------- Blob blbImage = rs.getBlob("SendFile");//SQL文の結果 Blobconvert listModel = null;          if (blbImage != null) listModel = new Blobconvert(blbImage); else listModel = new Blobconvert(); ----------------------------------------------------------------

  • ByteArrayInputStreamを配列に

    ByteArrayInputStreamから読み取ったバイトデータをbyte配列に格納したいのですが,よく分かりません. ByteArrayOutputStreamにはtoBytesメソッドがあるようなのですが・・. 分かる人がいましたらサンプルコードが参考サイトへのURLを教えていただけないでしょうか.

    • ベストアンサー
    • Java
  • strutsでファイルダウンロード(WinでOK,linuxでNG)

    strutsでWindowsのexeファイルのダウンロードを実装しましたが、Windows上のtomcatにdeployすると正常にダウンロードできるのに、linux(fedora-core3)上のtomcatにdeployすると、ダウンロードしたファイルがhtmlになってしまいます。 hoge.exeという名前のファイルはダウンロードできるのですが、それが実はテキストで、メモ帳で開くと、ダウンロード画面のhtmlが表示されます。 この状況は何によって生み出されているのでしょうか。 お分かりになる方、ヒントをお願いいたします。 以下にファイルダウンロード部のソース(抜粋)を掲載させていただきます。 ----- 以下ソース ----- protected void download(HttpServletResponse response, String fileType, String filename) { try { // exeファイルのダウンロード時 if (fileType.equals("application/octet-stream")) { response.setHeader("Content-Disposition", "attachment; filename=" + filename); } response.setContentType(fileType); BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filename)); BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream()); byte[] buf = new byte[128]; int size; while ((size = bis.read(buf, 0, buf.length)) != -1) { bos.write(buf, 0, size); } bos.close(); bis.close(); } catch (IOException e) { throw new thisSystemException("could not send file[" + filename + "]"); }

    • ベストアンサー
    • Java
  • Servletで画像を表示させたい時。

    例えばdocBaseがtestで、test直下にlogoフォルダがあって、その中に foobar.jpgと言う画像ファイルがあった場合、次のソースコードで出るはずなのですが、 /* イメージ画像の表示 */ package srd; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class dispLogo extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("image/jpeg"); BufferedInputStream bis = new BufferedInputStream(new FileInputStream("\\usr\\local\\test\\logo\\foobar.jpg")); ServletOutputStream out = res.getOutputStream(); int n; byte[] buf = new byte[512]; while ((n = bis.read(buf)) != -1) { out.write(buf, 0, n); } bis.close(); } } どうしても、画像のあるパスが分からないというエラーメッセージがでてしまいます。 画像はtmpフォルダに入れるべきなのでしょうか。それでも上手く表示されません。 どなたか分かる方がいましたら、教えてください。

専門家に質問してみよう