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,这似乎符合要求;不幸的是,这基本上有两个问题:
- 在我的数据库中,一些列被命名为 "Index",这是 SQLite 的保留字(和其他列,但显然不是 MS-Access 的保留字);这是很简单的固定插入过滤器 "Index" -> "Idx".
- 有一个翻译
switch (type) { case xxx: ...
可以将各种 MS-Access 类型转换为更传统的 SQLite3 类型;在我发现的 default:
标签上设置断点(@ErikvonAsmuth 猜对了)有未处理的 OLE
类型。我不知道如何处理这些。
在程序(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 个以上的附件。
我有一个相当大的 .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,这似乎符合要求;不幸的是,这基本上有两个问题:
- 在我的数据库中,一些列被命名为 "Index",这是 SQLite 的保留字(和其他列,但显然不是 MS-Access 的保留字);这是很简单的固定插入过滤器 "Index" -> "Idx".
- 有一个翻译
switch (type) { case xxx: ...
可以将各种 MS-Access 类型转换为更传统的 SQLite3 类型;在我发现的default:
标签上设置断点(@ErikvonAsmuth 猜对了)有未处理的OLE
类型。我不知道如何处理这些。
在程序(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 个以上的附件。