UCanAccess 似乎无法使用 getBytes() 读取 OLE 对象列

UCanAccess seems unable to read OLE Object column using getBytes()

我有一个相当大的 .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();
                }
        }
}

程序运行没有错误,但生成的文件是"strange"(当然不是图像):

$ 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.    thresholdxp?@......w.........t..PersonIDsr..java.lang.Integer.⠤...8...I..valuexr..java.lang.Number...........xp...
Ht..Indexsr..java.lang.ShorthM7.4`.R...S..valuexq.~.  ..xt..PersonImage

我做错了什么?

更新: 因为我的目标是转换一个书籍数据库(使用过时的程序 BookCAT for historical reasons) I found AccessConverter 在网上维护为 .mdb,这似乎符合要求;不幸的是,这基本上有两个问题:

在程序(BookCAT)中,这些字段包含两种数据:

后者不太重要,因为总是存在重复的 "plaintext" 版本(最好也检索格式化版本,但是...)。

不过,我真的很希望能够提取图像。

在图像数据中有一个伴随的 "ImageType" 列总是设置为“2”,这(如果我没记错的话)意味着它们应该是 .jpeg 个图像。

如何以可用格式检索 OLE 数据?

注: AccessConverter不使用ucanaccess,而是直接使用底层的com.healthmarketscience.jackcess库。

注意 2: 似乎 BookCAT 是使用 Delphi 构建的,如果相关的话。

事实证明,在我的特定情况下,所有 "OLE" 字段实际上都是 BLOB,无法识别为 OLE2 对象。

在这种情况下 jackcess returns a Enum OleBlob.ContentType.UNKNOWN 类型,在这种情况下,它将拒绝访问 BLOB 内容 (OleBlob.content.getBytes() returns null ).

为了访问存储的数据,必须直接使用 Column.getBytes(name)(完全绕过 OLE 子系统)。

为什么,在这种情况下,ucanaccess returns 一个无效值,而不是失败,我无法理解(可能我应该提交错误报告;欢迎评论)。

图像数据是纯 jpeg 格式的文件,而 "formatted text" 似乎是 Delphi TRichText 小部件的一些自定义序列化我不知道如何解析,但那是另一个问题。

使用 jackcess Column.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();

如您所见,附件是一个数组,它可以容纳 1 个以上的附件。