在JAVA

时间:2018-07-28 10:08:45

标签: java arrays sqlite file blob

我正在从客户端向服务器发送文件,并像这样接收它:

            //Receive File:
            FileOutputStream fis = new FileOutputStream("receivedTest");
            DataInputStream dis = new DataInputStream(clientSocket.getInputStream());
            int count;
            byte[] buffer = new byte[4096];
            while ((count = dis.read(buffer)) > 0)
            {
              fis.write(buffer, 0, count);
            }
            fis.close();

就像this subject中所解释的那样。 运作良好。但是事实是,我真的不想要接收文件本身。我想要一个BLOB。我读过BLOB就像一个byte []。

在我的数据库类(我使用SQLite)中,有下表:

    String sqlFile = "CREATE TABLE IF NOT EXISTS files (\n"
            + " id integer PRIMARY KEY,\n"
            + " shorthash byte[],\n"
            + " filename text NOT NULL,\n"
            + " file blob,\n"
            + " owner text\n"
            + ");";

和以下用于插入新“文件”的函数:

public void insertFile(byte[] shorthash, String filename, byte[] file, String owner) {
    String sql = "INSERT INTO files(shorthash,filename, file, owner) VALUES(?,?,?,?)";

    try (Connection conn = DriverManager.getConnection(url);
            PreparedStatement pstmt = conn.prepareStatement(sql)) {
        pstmt.setBytes(1, shorthash);
        pstmt.setString(2, filename);
        pstmt.setBytes(3, file);
        pstmt.setString(4, owner);
        pstmt.executeUpdate();
    } catch (SQLException e) {
        System.out.println(e.getMessage());
    }
}

如您所见,有4列,文件本身位于第3列。在表中它被声明为BLOB,但是当我插入它时,我只是在做一个setBytes。我不确定这是否对,这就是我在互联网上找到的。

因此,我正在服务器上接收此文件,并且要将其存储在数据库中。如果可能,我想避免在服务器端创建文件(在我的第一个代码中,FileOutputStream fis = new FileOutputStream(“ receivedTest”);行)。我想将其直接存储在数据库中,因为我将其作为字节数组接收,因此我认为这样会更容易。

但是我不知道该怎么做。可能是因为我不太了解Blob和byte []之间的联系。我的意思是,字节数组可能太小而无法容纳整个文件。但是一滴没关系。但是,要在数据库中插入文件,我要插入一个字节数组。这使我无聊。

编辑:

因此,我尝试了两件事:首先,在完成操作后将文件添加到数据库中here(在我所看到的几乎所有地方,都总是这样做):

//Receive encrypted File:
            FileOutputStream fos = new FileOutputStream("receivedTest");
            DataInputStream dis = new DataInputStream(clientSocket.getInputStream());
            int count;
            byte[] buffer = new byte[4096];
            while ((count = dis.read(buffer)) > 0)
            {
              fos.write(buffer, 0, count);
            }
            fos.close();

            DB.insertFile(shorthash, "test", "receivedTest", user);

//Insert file in DB:
public void insertFile(byte[] shorthash, String filename, String filepath, String owner) throws FileNotFoundException {
    String sql = "INSERT INTO files(shorthash, filename, file, owner) VALUES(?, ?, ?, ?)";
    try (Connection conn = DriverManager.getConnection(url);
            PreparedStatement pstmt = conn.prepareStatement(sql)) {
        pstmt.setBytes(1, shorthash);
        pstmt.setString(2, filename);

        File file = new File(filepath);
        FileInputStream   fis = new FileInputStream(file);
        pstmt.setBinaryStream(3, fis, (int) file.length());
        pstmt.execute();
        pstmt.setString(4, owner);
        pstmt.executeUpdate();
        fis.close()
    } catch (SQLException e) {
        System.out.println(e.getMessage());
    }
}

第二,将文件作为字节数组插入(但是不适用于大文件),如SQLite Tutorial所述:

//Insert file in DB:
public void insertFile(byte[] shorthash, String filename, String filepath, String owner) throws FileNotFoundException {
    String sql = "INSERT INTO files(shorthash, filename, file, owner) VALUES(?, ?, ?, ?)";
    try (Connection conn = DriverManager.getConnection(url);
            PreparedStatement pstmt = conn.prepareStatement(sql)) {
        pstmt.setBytes(1, shorthash);
        pstmt.setString(2, filename);

        File file = new File(filepath);
        FileInputStream   fis = new FileInputStream(file);    
        byte[] buffer = new byte[1024];
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        for (int len; (len = fis.read(buffer)) != -1;)
            bos.write(buffer, 0, len);
        fis.close()
        pstmt.setBytes(3, bos.toByteArray());
        pstmt.execute();
        pstmt.setString(4, owner);
        pstmt.executeUpdate();
    } catch (SQLException e) {
        System.out.println(e.getMessage());
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

然后,当我打印我的数据库时,其中没有文件。如果我尝试使用数据库浏览器打开数据库,则相同。控制台只说:

Connection to SQLite has been established. 
ouverture du server
Clients:

1   admin   admin

Files:

3 个答案:

答案 0 :(得分:5)

byte[]和BLOB只是讨论任意二进制数据集的两种不同方式。在Java中,byte[]是二进制数据的集合。在数据库中,BLOB代表Binary Large Object,并且是同一件事。只是二进制数据的集合。

因此,它们是同一事物,具有不同的名称,具体取决于参考框架。因此,当您将byte[]存储在blob列中时,您只是将这些字节从Java推送到数据库中。然后,当您读回它们时,如果需要,可以将它们变成一个对象,因为数据库没有更改它们。它只是直接存储了二进制信息。

您会发现,如果您是从其他地方写的blob,则除非您知道存储的二进制数据的编码和字节序,否则可能无法将其变成对象。

如果文件太大而无法存储在单个byte[]中,或者您想优化使用内存进行存储的方式,则可以使用Stream将数据发送到数据库而不将它们全部同时保存在内存中。

最后,如果您需要将FileInputStream转换为字节,可以使用Apache Commons IO,如下所示:

byte[] bytes = IOUtils.toByteArray(fis);

然后存储您的文件。

答案 1 :(得分:0)

将文件插入为十六进制字符串

public static String bytesToHex(byte[] bytes) {
    char[] hexChars = new char[bytes.length * 2];
    for ( int j = 0; j < bytes.length; j++ ) {
        int v = bytes[j] & 0xFF;
        hexChars[j * 2] = hexArray[v >>> 4];
        hexChars[j * 2 + 1] = hexArray[v & 0x0F];
    }
    return new String(hexChars);
}

String strFilte = "x" + "’" + bytesToHex(file) + "’"

pstmt.setString(3, strFile);

答案 2 :(得分:0)

我设法找到一种方法;它仍然不是最佳选择,但对我来说已经足够了,它可能对其他人也有用。

要直接在数据库中保存文件而不在服务器端创建文件,可以将其作为BinaryStream插入。但是,方法setBinaryStream接受3个输入:参数索引(int),输入流和流的长度。因此,您必须知道文件的长度。

由于客户端正在发送文件,所以我只要求他使用以下命令发送文件的长度:

dout.writeInt((int) file.length());

然后在服务器端,我的DataInputStream接收文件的长度,紧随其后的是文件:

//receive file size (needed to save it as inputStream in DB) and file:
            DataInputStream dis = new DataInputStream(clientSocket.getInputStream());
            int fileLength = dis.readInt();

            DB.insertFile(shorthash, filename, dis, fileLength, user);

插入文件方法:

public void insertFile(String filename, InputStream fis, int length, String owner){
    String sql = "INSERT INTO files(filename, file, owner) VALUES(?, ?, ?)";
    try (Connection conn = DriverManager.getConnection(url);
            PreparedStatement pstmt = conn.prepareStatement(sql)) {
        pstmt.setString(1, filename);
        pstmt.setBinaryStream(2, fis, length);
        pstmt.setString(3, owner);
        pstmt.executeUpdate();
    } catch (SQLException e) {
        System.out.println(e.getMessage());
    }
}