Storing and Retrieving Images and Files with JDBC
Databases can store more than numbers and short text. A BLOB (Binary Large Object) column holds binary data such as an image, a PDF or an audio clip. A CLOB (Character Large Object) column holds large text, such as a long document. In JDBC, you write to these columns with a PreparedStatement by giving it a stream, and read them back from the ResultSet as a stream. Using streams means a large file never has to be loaded into memory all at once. In this lesson, you will learn how to store and retrieve an image and a text file, and when it is better to keep files outside the database.
BLOB vs CLOB
| Point | BLOB | CLOB |
|---|---|---|
| Holds | Raw bytes | Characters (text) |
| Used for | Images, PDFs, audio, video, zip files | Long documents, XML, JSON, logs |
| Java stream type | InputStream | Reader |
| Store with | setBinaryStream, setBytes, setBlob | setCharacterStream, setString, setClob |
| Read with | getBinaryStream, getBytes, getBlob | getCharacterStream, getString, getClob |
| MySQL type names | BLOB, MEDIUMBLOB, LONGBLOB | TEXT, MEDIUMTEXT, LONGTEXT |
| PostgreSQL type names | BYTEA | TEXT |
Methods
| Method | Description |
|---|---|
ps.setBinaryStream(i, inputStream, length) | It is used to send binary data from a stream. |
ps.setBytes(i, byteArray) | It sends binary data already held in memory; fine for small items. |
ps.setCharacterStream(i, reader) | It sends text from a Reader. |
rs.getBinaryStream(column) | It returns a stream for reading a BLOB. |
rs.getBytes(column) | It returns a BLOB as a byte array. |
rs.getCharacterStream(column), rs.getString(column) | They read a CLOB as a stream or as a String. |
Example: Storing and Retrieving a Photo and an Essay
The program first creates two small files so it has something to store: a 2,048-byte file standing in for a photo, and a short text file.
import java.io.InputStream;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Arrays;
public class Lobs {
public static void main(String[] args) throws Exception {
// Two small files to store: a fake "photo" and a text document.
byte[] photoBytes = new byte[2048];
for (int i = 0; i < photoBytes.length; i++) {
photoBytes[i] = (byte) (i * 7);
}
Path photo = Files.write(Path.of("photo.png"), photoBytes);
Path essay = Files.writeString(Path.of("essay.txt"),
"My favorite subject is Java.\nI like collections the most.\n");
try (Connection con =
DriverManager.getConnection("jdbc:h2:mem:school")) {
try (Statement st = con.createStatement()) {
st.execute("CREATE TABLE document (id INT PRIMARY KEY, "
+ "student VARCHAR(50), photo BLOB, essay CLOB)");
}
// Store: stream the files straight into the database.
String insert = "INSERT INTO document VALUES (?, ?, ?, ?)";
try (PreparedStatement ps = con.prepareStatement(insert);
InputStream in = Files.newInputStream(photo);
Reader reader = Files.newBufferedReader(essay)) {
ps.setInt(1, 1);
ps.setString(2, "Navika");
ps.setBinaryStream(3, in, Files.size(photo));
ps.setCharacterStream(4, reader);
System.out.println(ps.executeUpdate() + " row stored");
}
// Retrieve: stream them back out to new files.
String query = "SELECT photo, essay FROM document WHERE id = ?";
try (PreparedStatement ps = con.prepareStatement(query)) {
ps.setInt(1, 1);
try (ResultSet rs = ps.executeQuery()) {
rs.next();
Path photoCopy = Path.of("photo-copy.png");
try (InputStream in = rs.getBinaryStream("photo")) {
Files.copy(in, photoCopy);
}
String text = rs.getString("essay");
System.out.println("Photo bytes back: "
+ Files.size(photoCopy));
System.out.println("Identical to the original? "
+ Arrays.equals(photoBytes,
Files.readAllBytes(photoCopy)));
System.out.println("Essay characters: " + text.length());
System.out.println("First line: "
+ text.lines().findFirst().orElse(""));
Files.delete(photoCopy);
}
}
} finally {
Files.delete(photo);
Files.delete(essay);
}
}
}1 row stored
Photo bytes back: 2048
Identical to the original? true
Essay characters: 58
First line: My favorite subject is Java.Explanation:
- The table has a
BLOBcolumn for the photo and aCLOBcolumn for the essay. - Storing:
setBinaryStream(3, in, size)hands the driver anInputStreamon the photo file;setCharacterStream(4, reader)hands it aReaderon the text file. The driver reads from the streams while executing, so the file's contents flow to the database without being held in a Java array. - Retrieving:
getBinaryStream("photo")gives a stream of the stored bytes, andFiles.copywrites it to a new file. The essay is small, sogetStringis the easy way to read it. - The check compares the retrieved file with the original, byte for byte: identical.
- The streams are opened in try-with-resources, so the files are closed even if the insert fails.
Should Files Go in the Database?
| Store in the database when | Store on disk or in object storage when |
|---|---|
| Files are small (thumbnails, signatures, short documents) | Files are large (video, big images, backups) |
| The file must be saved or rolled back together with its row | Files are served directly to browsers or a CDN |
| You want one backup to contain everything | You want the database to stay small and fast |
The common design for larger files is to keep the file in a file system or an object store such as Amazon S3, and store only its path or key, size and type in the database.
Common Mistakes
Loading a huge file with getBytes. The whole file must fit in memory. Use streams for anything large.
Using a CLOB for binary data, or a BLOB for text. Character conversion corrupts binary data; text in a BLOB loses its character set.
Closing the source stream before executing the statement. The driver reads it during executeUpdate.
SELECT * on a table with large columns. Every query drags the big data along. Select those columns only when needed.
Practice
- Easy: Store a real image from your computer and read it back into a second file. Open both to compare.
- Harder: Add
file_name,content_typeandsize_bytescolumns, fill them when storing, and write a method that lists the stored documents without reading the BLOB.
Key Takeaways
Was this tutorial helpful?