我目前正在尝试使用java在MySQL 5.5数据库上存储大文件。我的主类叫做FileDatabaseTest。它有以下方法:
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
上传小文档时程序运行正常,但是当我上传20MB这样的文件时,上传过程非常慢。所以我尝试将FileInputStream放在BufferedInputStream中,代码如下:
stmt.setBinaryStream(4, new BufferedInputStream(new FileInputStream(file));
上传过程变得非常快。就像将文件复制到另一个目录一样。但是当我尝试上传超过400mb的文件时,我收到了以下错误:
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)
所以我尝试使用嵌入式Apache-Derby数据库而不是MySQL,我没有收到错误。我可以使用BufferedInputStream在Derby数据库中上传500MB到1.5G的文件。我还观察到当使用BufferedInputStream和MySQL服务器上传大文件时,JVM占用了大量内存,而当我在Derby数据库中使用它时,JVM的内存使用量保持在85MB到100MB左右。
我对MySQL比较新,我只是使用它的默认配置。我在配置中唯一改变的是“max_allowed_packet”大小,所以我可以将最多2GB的文件上传到数据库。所以我想知道错误来自哪里。这是MySQL或MySQL连接器/ J的错误吗?或者我的代码有问题吗?
我在这里想要实现的是能够使用java将大文件(最多2GB)上传到MySQL服务器,而不会增加java堆空间。
答案 0 :(得分:1)
如果您不想增加JVM堆大小,还有另一种解决方法:
首先,你的MySQL版本应该比5.0更新。
其次,Statement.getResultSetType()应为TYPE_FORWARD_ONLY,ResultSetConcurrency应为CONCUR_READ_ONLY(默认值)。
第三,包括以下一行: 1).statement.setFetchSize(Integer.MIN_VALUE的); 2)((com.mysql.jdbc.Statement)STAT).enableStreamingResults();
现在您将逐个获取结果行
答案 1 :(得分:0)
只是为了它,尝试增加你的JVM堆大小。
increase the java heap size permanently? http://javahowto.blogspot.com/2006/06/6-common-errors-in-setting-java-heap.html
答案 2 :(得分:0)
在运行java代码时提升JVM堆大小:
right click your java file
->run as->run configurations->arguments->VM arguments
答案 3 :(得分: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 KB,较大的缓冲区可能会显示不同的内存行为,可能会对问题有所了解。
关闭自己不应该伤害尝试。