我正在从客户端向服务器发送文件,并像这样接收它:
//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:
答案 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());
}
}