Ablia
Ablia

Reputation: 327

convert file or byte[] into BLOB in JAVA

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:

Upvotes: 1

Views: 26456

Answers (4)

Mayur Gite
Mayur Gite

Reputation: 395

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.

Upvotes: 0

Ablia
Ablia

Reputation: 327

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

Upvotes: 1

Steven Spungin
Steven Spungin

Reputation: 29189

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

Upvotes: 1

Joe W
Joe W

Reputation: 2891

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.

Upvotes: 8

Related Questions