0

我目前正在尝试使用 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 的文件。我还观察到,在 MySQL 服务器上使用 BufferedInputStream 上传大文件时,JVM 正在消耗大量内存,而当我在 Derby 数据库中使用它时,JVM 的内存使用量保持在 85MB 到 100MB 左右。

我对 MySQL 比较陌生,我只是使用它的默认配置。我在其配置中唯一更改的是“max_allowed_pa​​cket”大小,因此我最多可以将 2GB 文件上传到数据库。所以我想知道错误来自哪里。它是 MySQL 的错误还是 MySQL 连接器/J 的错误?还是我的代码有问题?

我在这里想要实现的是能够使用 java 将大文件(最大 2GB)上传到 MySQL 服务器,而不会增加 java 堆空间。

4

4 回答 4

1

如果您不想增加 JVM 堆大小,还有另一种解决方法:

首先,您的 MySQL 版本应该高于 5.0。

其次,Statement.getResultSetType() 应该是 TYPE_FORWARD_ONLY 并且 ResultSetConcurrency 应该是 CONCUR_READ_ONLY(default)。

第三,包括这些行之一: 1).statement.setFetchSize(Integer.MIN_VALUE); 2).((com.mysql.jdbc.Statement)stat).enableStreamingResults();

现在您将一一获取结果行

于 2013-05-16T10:06:28.723 回答
0

只是为了它,尝试增加你的 JVM 堆大小。

永久增加java堆大小? http://javahowto.blogspot.com/2006/06/6-common-errors-in-setting-java-heap.html

于 2012-01-24T03:42:14.467 回答
0

运行 java 代码时增加 JVM 堆大小:

right click your java file
    ->run as->run configurations->arguments->VM arguments
于 2012-03-07T10:06:27.630 回答
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,更大的缓冲区可能会显示不同的内存行为,也许可以解决问题。

关闭自己不应该伤害尝试。

于 2013-05-16T10:18:15.830 回答