У меня есть довольно большая база данных доступа .mdb, которую я хочу преобразовать в SQLite3, чтобы использовать ее под Linux.
Я не могу передать какие-либо из имеющихся у меня BLOB (в основном содержащих изображения).
Вот пример программы тестирования:
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class prova {
public static void main(String[] args) {
String url = "jdbc:ucanaccess://data/BookDB-201810.mdb";
try {
Connection c = DriverManager.getConnection(url);
PreparedStatement ps;
ResultSet rs;
String q = "SELECT * FROM PersonImage";
ps = c.prepareStatement(q);
rs = ps.executeQuery();
while (rs.next()) {
byte[] i = rs.getBytes("Image");
String fn = String.format("data/img/i%05d.%d.jpg", rs.getInt("PersonId"), rs.getInt("Index"));
try (FileOutputStream fos = new FileOutputStream(fn)) {
fos.write(i);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Программа работает без ошибок, но полученные файлы "странные" (конечно, не изображения):
$ ls -l i00072.1.jpg
-rw-r--r-- 1 mcon mcon 369 Nov 23 11:38 i00072.1.jpg
$ file i00072.1.jpg
i00072.1.jpg: Java serialization data, version 5
Глядя в них, я нахожу следующее:
....sr..net.ucanaccess.jdbc.BlobKey...........L.
columnNamet..Ljava/lang/String;L..keyt..Ljava/util/HashMap;L. tableNameq.~..xpt..Imagesr..java.util.HashMap......`....F.
loadFactorI. [email protected].⠤...8...I..valuexr..java.lang.Number...........xp...
Ht..Indexsr..java.lang.ShorthM7.4`.R...S..valuexq.~. ..xt..PersonImage
Что я делаю неправильно?
Обновлять: Поскольку моя цель - преобразовать базу данных книг (поддерживаемую в сети как .mdb с использованием устаревшей программы BookCAT по историческим причинам), я нашел AccessConverter, который, кажется, отвечает всем требованиям; К сожалению, здесь есть две проблемы:
switch (type) { case xxx: ... для преобразования различных типов MS-Access в более традиционные типы SQLite3; Установив точку останова на этикетке default:, я обнаружил (как правильно догадался @ErikvonAsmuth) существуют необработанные типы OLE. Я понятия не имею, как с этим справиться.В программе (BookCAT) эти поля содержат данные двух типов:
Последнее менее важно, потому что всегда есть дублирующаяся версия с открытым текстом (было бы неплохо получить также отформатированную версию, но ...).
Однако мне бы очень хотелось иметь возможность извлекать изображения.
В данных изображения есть сопутствующий столбец «ImageType», для которого всегда установлено значение «2», что (если я не ошибаюсь) означает, что это должны быть изображения .jpeg.
Что я могу сделать, чтобы получить данные OLE в удобном для использования формате?
Примечание:AccessConverter не использует ucanaccess, вместо этого он использует непосредственно базовую библиотеку com.healthmarketscience.jackcess.
Заметка 2: кажется, что BookCAT построен с использованием Delphi, если это уместно.
@ErikvonAsmuth: всего несколько минут назад я обнаружил, что Вы абсолютно правы. Я обновлю вопрос, чтобы отразить мои выводы и уточнить вопрос (краткое резюме: эти поля содержат либо изображения, либо «форматированный текст»).




Оказывается, в моем конкретном случае все поля «OLE» на самом деле являются большими двоичными объектами, нераспознаваемыми как объекты OLE2.
В этом случае jackcess возвращает тип Enum OleBlob.ContentType.UNKNOWN и в этом случае откажется от доступа к содержимому BLOB (OleBlob.content.getBytes() возвращает null).
Для доступа к хранимым данным необходимо напрямую использовать Column.getBytes(name) (полностью минуя подсистему OLE).
Почему в этом состоянии ucanaccess возвращает недопустимое значение вместо ошибки, я не понимаю (возможно, мне следует отправить отчет об ошибке; комментарии приветствуются).
Данные изображения представляют собой простой файл в формате jpeg, в то время как «форматированный текст» кажется некоторой настраиваемой сериализацией виджета Delphi TRichText, я не знаю, как разбирать, но это другая проблема.
Используя jackcessColumn.getBytes(name), я смог получить нужные мне данные.
С UCanAccess вам нужно использовать ResultSet#getBlob:
String q = "SELECT * FROM PersonImage";
ps = conn.prepareStatement(q);
rs = ps.executeQuery();
while (rs.next()) {
java.sql.Blob image = rs.getBlob("Image");
String fn = String.format("C:/Users/Gord/Pictures/i%05d.%d.jpg", rs.getInt("PersonId"), rs.getInt("Index"));
try (FileOutputStream fos = new FileOutputStream(fn)) {
fos.write(image.getBytes(1, (int) image.length()));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Мы смогли получить доступ к контенту, используя тип файла UCanaccess + accdb:
String columnLabel = "FIELD1";
Object obj = rs.getObject(2);
net.ucanaccess.complex.Attachment[] attachments = (net.ucanaccess.complex.Attachment[])obj;
net.ucanaccess.complex.Attachment att = attachments[0];
System.err.println("Attachment Name: " + att.getName());
byte[] bytea = att.getData();
Как видите, вложения представляют собой массив и могут содержать более одного вложения.
В Access нет такой вещи, как
VARBINARY. Существуют объекты OLE, но они бывают разных типов: некоторые просто содержат файл в виде двоичных данных, другие содержат определенные заголовки, а третьи сжимают файлы и помещают несколько файлов в одну строку. Если вы не укажете, что именно вы пытаетесь импортировать, мы, скорее всего, не сможем вам помочь.