Ответ
Да, PostgreSQL предоставляет два основных способа хранения бинарных данных (BLOB): тип bytea и механизм Large Objects.
1. Использование типа bytea (рекомендуется для файлов до 1 ГБ)
Тип bytea хранит бинарные данные как массив байтов прямо в строке таблицы.
Создание таблицы:
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
file_name VARCHAR(255) NOT NULL,
mime_type VARCHAR(100),
content BYTEA NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Сохранение файла из Java (JDBC):
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.PreparedStatement;
public void savePdf(Connection conn, String filePath) throws Exception {
byte[] fileBytes = Files.readAllBytes(Paths.get(filePath));
String sql = "INSERT INTO documents (file_name, mime_type, content) VALUES (?, ?, ?)";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, "document.pdf");
stmt.setString(2, "application/pdf");
stmt.setBytes(3, fileBytes); // Ключевой метод setBytes() для bytea
stmt.executeUpdate();
}
}
Плюсы bytea: Простота использования, совместимость со стандартным JDBC, эффективно для файлов малого и среднего размера.
Минусы: Вся строка с данными загружается в память при выборке.
2. Использование Large Objects (LOB) (для файлов > 1 ГБ или потоковой обработки)
Large Objects хранят данные отдельно от таблицы, а в таблице сохраняется только OID (идентификатор объекта).
Сохранение с использованием Large Object API JDBC:
import org.postgresql.PGConnection;
import org.postgresql.largeobject.*;
import java.io.FileInputStream;
import java.sql.Connection;
public long savePdfAsLargeObject(Connection conn, String filePath) throws Exception {
// Приведение к PGConnection для доступа к LOB API
PGConnection pgConn = conn.unwrap(PGConnection.class);
LargeObjectManager lom = pgConn.getLargeObjectAPI();
// Создание нового Large Object в режиме записи
long oid = lom.createLO(LargeObjectManager.READ | LargeObjectManager.WRITE);
try (LargeObject lob = lom.open(oid, LargeObjectManager.WRITE);
FileInputStream fis = new FileInputStream(filePath)) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
lob.write(buffer, 0, bytesRead); // Потоковая запись
}
}
return oid; // Этот OID нужно сохранить в таблице
}
Плюсы Large Objects: Эффективная потоковая передача, не требует загрузки всего файла в память, лучше для очень больших файлов. Минусы: Более сложный API, требует специфичного для PostgreSQL кода.
Рекомендация: Для большинства случаев (файлы до нескольких сотен мегабайт) используйте bytea. Для хранения очень больших файлов или необходимости потоковой обработки выбирайте Large Objects.