1

I'm sending a file frome a client to a server and receive it like this:

            //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();

Just like it's explained in this subject. It's working well. But the fact is, i don't really want to receive the file itself; i want a BLOB. I've read that a BLOB is just like a byte[].

In my database class (i use SQLite), i have the following table:

    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"
            + ");";

and the following function to insert a new "file":

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());
    }
}

As you can see, there's 4 columns, the file itself is in the 3rd column. In the table it's declared as a BLOB, but when i insert it i'm just doing a setBytes. I'm not sure if this is right, it's just what i've found on internet.

So, i'm receiving this file on my server, and i want to store it in my database. If possible, i would like to avoid creating the file on server side (the line FileOutputStream fis = new FileOutputStream("receivedTest"); in my first code). I would like to store it directly in the database, since i receive it as a byte array i think it'll be easier this way.

But i don't know how to do that. Probably because i don't really understand the link between Blob and byte[]. I mean, a byte array may be too little to hold an entire file; but a blob is ok. however, to insert the file in database, i insert a byte array. This make nonsens to me.

EDIT:

So, i've tried two things: first, adding the file in the DB as it's done here (and pretty much everywhere i looked, it's always done that way):

//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());
    }
}

Second, insert the file as a byte array (but that won't work for big files) as it's explained in 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();
    }
}

Then, when i print my DB, there is no file in it. Same if i try to open the DB with DB Browser. The console only say:

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

1   admin   admin

Files:
Ablia
  • 317
  • 1
  • 3
  • 14

4 Answers4

8

A byte[] and a BLOB are just two different ways of talking about an arbitrary set of binary data. In Java a byte[] is a collection of binary data. In a database BLOB stands for Binary Large Object and is the same thing. Just a collection of binary data.

So they are the same thing with different names depending on the frame of reference. So when you store your byte[] in a blob column you're just pushing those bytes from Java into the database. Then when you read them back they can be turned back into an object if you want because the DB didn't change them. It just stored the binary info directly.

You'd find if you wrote a blob from somewhere else you may not be able to turn it into an object unless you know the encoding and endianness of the stored binary data.

If you have a file too big to be stored in a single byte[] or you wanted to optimize how you're using memory for storage you could use a Stream to send the data to the db without holding it all in memory at the same time.

Finally, if you need to turn your FileInputStream into bytes you can use Apache Commons IO like this:

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

And then store your file.

Joe W
  • 2,773
  • 15
  • 35
  • thank you, it's exactly what i wanna do, but i have no idea how to send the data in the db with a stream. I've tried changing my "insertFile" function by saying file is now an InputStream and to insert the blob i just do rs.setBinaryStream, but then i have no idea how i get properly this particulary stream....i've tried using my dis as InputStream, but then when i try to print the DB, instead of a row with the different columns & values, i get a row with "null". So there's still something wrong here, and i don't know what. – Ablia Jul 29 '18 at 09:35
  • @Ablia check this answer for details: https://stackoverflow.com/questions/9476434/how-to-store-a-file-in-a-mysql-database-using-blob the second answer has what I think is a valid example. Or if your files aren't huge just read them into memory and save the bytes using what you already have. – Joe W Jul 29 '18 at 09:50
  • i've already tried it. It doesn't work. However, even for a little file as a byte array, it doesn't work better. Maybe the problem is somewhere else? The file is correctly received and saved on the server side. But the insertion in DB doesn't work. Is my SQL statemant somehow wrong? – Ablia Jul 29 '18 at 10:26
  • What error are you getting? Post a stack trace in your question – Joe W Jul 29 '18 at 10:45
  • i don't get any error. the file just isn't saved in DB. I've edited my post for more info. – Ablia Jul 29 '18 at 11:33
  • Since i didn't see the problem with file insertion, i've tried just suppress it. Which mean i suppressed the file column in the DB and changed the insert function so that it insert just the shorthahs, filename and owner. It didn't work better. So i've tried without the shorthash: i got an error because i don't close the socket. This made, it worked; i added the shorthash again; it worked; i added the file again.... Well, when i print it now it says "null", but when i open it with DBBrowser i got a row: 1, BLOB, test, BLOB, admin Which seems good, except that shorthash isn't a Blob... – Ablia Jul 29 '18 at 12:08
  • Now, i still have to: 1) Figure out why shorthash is marked as Blob and not byte[] (it's just a byte[2]) 2) make sure the file was correctly saved; which mean take it out of the DB and see if it's really the same 3) Find a way to save it into DB without creating the file on server side. ...But not today! :D Thanks a lot for your help! (i'll probably still need it later XD) – Ablia Jul 29 '18 at 12:15
1

Insert file as a hex string

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);
Steven Spungin
  • 27,002
  • 5
  • 88
  • 78
  • Hex doubles the size. BLOB is designed to store arbitrary bytes, therefore why would someone want to convert data to hex and then store it? – Robert Jul 29 '18 at 11:50
  • @Robert Our use case was such that we had binary string stored in JSON as hex string. Our database was on same server as REST endpoint. We benchmarked it and it was faster to send hex string into database than converting to byte array and then sending byte array/stream to database. This solution is just another example that shows that a byte array as hex string can translated into a BLOB. – Steven Spungin Jul 29 '18 at 12:12
1

I've managed to find out a way; it's still not optimal, but it's enough for me and it probably can be usefull for others.

To save the file directly in the DB, without creating the file on server side, you can insert it as a BinaryStream. However, the method setBinaryStream take 3 inputs: the parameter index (int), the input stream, and the length of the stream. So to do so, you have to know the length of your file.

Since the client is sending the file, i just ask him to send the length of the file before with:

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

then on server side my DataInputStream receive the length of the file, immediatly followed by the file:

//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);

Insert file method:

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());
    }
}
Ablia
  • 317
  • 1
  • 3
  • 14
0

Simple way to convert PDF to Blob is to first convert PDF into byte[] and then convert it to Blob:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.sql.rowset.serial.SerialBlob;

public static Blob pdfToBlob(File file) throws SQLException {

    byte[] bArray = new byte[1000];

    List<Byte> byteList = new ArrayList<>();

    try (FileInputStream fis = new FileInputStream(file)) {

        // Converting input file in list of bytes
        while (fis.read(bArray) > 0) {
            for (byte b : bArray)
                byteList.add(b);
        }

    } catch (IOException e) {
        e.printStackTrace();
    }

    // Converting list of bytes into array of bytes
    // as SerialBlob class takes array of bytes
    byte[] byteArray = new byte[byteList.size()];

    for (int i = 0; i < byteList.size(); i++) {
        byteArray[i] = (byte) byteList.get(i);
    }

    return new SerialBlob(byteArray);
}

Please improve above code if you can. Hope you will find it helpful.

Mayur Gite
  • 397
  • 4
  • 16