2012-01-23 7 views
0

現在、javaを使用して大きなファイルをMySQL 5.5データベースに保存しようとしています。私のメインクラスはFileDatabaseTestです。これは、次の方法があります。JavaのBufferedInputStreamを使用して大きなファイルをMySQLデータベースに保存するときにjava.lang.outOfMemoryErrorを取得する

import java.sql.*; 
import java.io.*; 

... 

public class FileDatabaseTest { 

... 

private void uploadToDatabase(File file, String description) { 
     try { 
      PreparedStatement stmt = connection.prepareStatement(
       "INSERT INTO FILES (FILENAME, FILESIZE, FILEDESCRIPTION, FILEDATA) " + 
        "VALUES (?, ?, ?, ?)"); 
      stmt.setString(1, file.getName()); 
      stmt.setLong(2, file.length()); 
      stmt.setString(3, description); 
      stmt.setBinaryStream(4, new FileInputStream(file)); 
      stmt.executeUpdate(); 
      updateFileList(); 
      stmt.close(); 
     } catch(SQLException e) { 
      e.printStackTrace(); 
     } catch(FileNotFoundException e) {//thrown by FileInputStream constructor 
      e.printStackTrace(); 
     } catch(SecurityException e) { //thrown by FileInputStream constructor 
      e.printStackTrace(); 
     } 
    } 

... 

} 

データベースが一つだけ表がある - 「FILES」の表を、そしてそれは、次の列があります。

ID - AUTOINCREMENT, PRIMARY KEY 

FILENAME - VARCHAR(100) 

FILESIZE - BIGINT 

FILEDESCRIPTION - VARCHAR(500) 

FILEDATA - LONGBLOB 

小さなドキュメントをアップロードすると、プログラムが正常に動作しているが、私は20メガバイトのようなファイルをアップロードすると、アップロード処理が非常に遅いです。だから私は、次のコードでBufferedInputStreamを内部でのFileInputStreamを入れてみました:

stmt.setBinaryStream(4, new BufferedInputStream(new FileInputStream(file)); 

アップロードプロセスは非常に高速になりました。そのファイルを別のディレクトリにコピーするのが好きです。私はより多くの400メガバイト以上のファイルをアップロードしようとしたときしかし、私は次のエラーを得た:

Exception in thread "Thread-5" java.lang.OutOfMemoryError: Java heap space 
    at com.mysql.jdbc.Buffer.ensureCapacity(Buffer.java:156) 
    at com.mysql.jdbc.Buffer.writeBytesNoNull(Buffer.java:514) 
    at com.mysql.jdbc.PreparedStatement.escapeblockFast(PreparedStatement.java:1169) 
    at com.mysql.jdbc.PreparedStatement.streamToBytes(PreparedStatement.java:5064) 
    at com.mysql.jdbc.PreparedStatement.fillSendPacket(PreparedStatement.java:2560) 
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2401) 
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2345) 
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2330) 
    at FileDatabaseTest$2.run(FileDatabaseTest.java:312) 
    at java.lang.Thread.run(Thread.java:662) 

は、だから私は、代わりのMySQLの組み込みのApache-Derbyデータベースを使用してみました、と私はエラーを取得できませんでした。 BufferedInputStreamを使用してDerbyデータベースに500MBから1.5Gファイルをアップロードできました。私はまた、大きなファイルをアップロードする際にMySQLサーバーでBufferedInputStreamを使用すると、JVMが大量のメモリを使用しているのに対し、Derbyデータベースで使用した場合、JVMのメモリ使用量は約85MB〜100MBになります。

私は比較的新しいMySQLです。デフォルトの設定を使用しています。私が設定を変更したのは "max_allowed_pa​​cket"サイズだけなので、データベースに最大2GBのファイルをアップロードできます。だから私はエラーがどこから来たのだろうか。それはMySQLかMySQL connector/Jのバグですか?または私のコードに何か問題がありますか?

私がここで達成しようとしているのは、Javaヒープスペースを増やさずに、大きなファイル(最大2GB)をMySQLサーバーにアップロードできることです。

+0

Mysql jdbcドライバには、maxAllowedPacketというオプションがあります。このオプションは、デフォルトでmax_allowed_pa​​cketの値と一致します。したがって、ドライバがパケット全体に十分なバッファスペースを内部的に割り当てるように思えます。 – Vadim

+0

"maxAllowedPacket"プロパティを変更しようとしましたが、小さい値に変更すると、 "maxAllowedProperty"の値よりも大きいファイルを送信するとエラーが発生します。大きな値を変更すると、大きなファイルをアップロードするときにjava.lang.outOfMemoryエラーが発生します。私が見たところでは、MySQLではSQL文を実行する前にjavaがファイル全体の内容をメモリに読み込んでおり、大きなファイルをアップロードするときにoutOfMemoryエラーが発生すると考えられます。 MySQLがメモリにファイル全体をロードしてからMySQLに送信するのを防ぐ方法はありますか? – Esjee

+0

問題を解決しましたか? – Rafaesp

答えて

0

right click your java file 
    ->run as->run configurations->arguments->VM arguments 
1

あなたはJVMのヒープサイズを増額したくない場合は、別の解決方法は、あります

まず、MySQLのバージョンが5.0より新しいはずです。

第2に、Statement.getResultSetType()はTYPE_FORWARD_ONLYで、ResultSetConcurrencyはCONCUR_READ_ONLY(デフォルト)である必要があります。

第3に、これらの行のうちの1つを含めます。 1).statement.setFetchSize(Integer.MIN_VALUE); 2)((com.mysql.jdbc.Statement)stat).enableStreamingResults();

今は結果行を一

0

ずつフェッチされますMySQLのJDBCの問題であることをより多くのようです。もちろん、あなたはGZip + Piped I/Oを検討しています。

私も部品インサートをやって、ひどい解決策を見つけた:

UPDATE FILES SET FILEDATA = CONCAT(FILEDATA, ?) 

私たちは、大きなファイルのために、ディスク上に保存するために優れていることを、結論付けることができます。それでも

final int SIZE = 1024*128; 
InputStream in = new BufferedInputStream(new FileInputStream(file), SIZE); 
stmt.setBinaryStream(4, in); 
stmt.executeUpdate(); 
updateFileList(); 
stmt.close(); 
in.close(); //? 

デフォルトのバッファサイズは、私は、大きなバッファは多分問題にいくつかの光を流し、異なるメモリ挙動を示すかもしれないと思う8キロバイトです。

自分自身をクローズすると、迷ってはいけません。

関連する問題