我正在编写一个Java(好的,Groovy,但这并不重要)迁移脚本将BLOB字段从Oracle10g数据库复制到另一个。数据由MS Access应用程序创建。文件似乎有不正确的编码,我猜测MS Access或ODBC驱动程序以某种方式操作文件。
使用查询SELECT * FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER = 'NLS_CHARACTERSET';
我发现源数据库有字符集WE8MSWIN1252
。
源表定义为:
CREATE TABLE CTR_DOCUMENTS (
CTR_ID NUMBER(11) NOT NULL,
CTR_A_ID NUMBER(11),
CTR_FILENAME VARCHAR2(260 Char) NOT NULL,
CTR_COMMENT VARCHAR2(255 Char),
CTR_DATE DATE,
CTR_DATA BLOB
)
我像这样访问blob:
def blob = sourceDB.firstRow("SELECT CTR_DATA FROM CTR_DOCUMENTS WHERE CTR_ID = ?",
[id]).CTR_DATA
def blobSize = blob.length()
def blobStream = blob.getBinaryStream()
byte[] byteArray = new byte[blobSize]
blobStream.read(byteArray)
我将一些blob保存为文件,编码看起来很奇怪,文件无法通过程序打开。第二个字节始终为00:
0000000: 2500 5000 4400 4600 2d00 3100 2e00 3500 %.P.D.F.-.1...5.
我也观察到使用SQL客户端访问BLOBS的相同行为(SQL Workbench / J,SQLDeveloper,TOAD)。
对我来说,看起来我必须将文件从Windows-1252转换为UTF8,但这不起作用。 我在这里错过了什么吗?
答案 0 :(得分:2)
在我开始的地方,出于诊断目的,如果没有别的,将从输入BLOB的样本扫描字节数组,以查看每个第二个字节实际上是0x00
,并且每隔一个字节写入(非零)字节到bytesOut
字节数组。如果成功,我会将bytesOut
数组写入文件,看看它们现在是否是有效的PDF文档。例如:
public static void main(String[] args) {
try {
String connectionUrl = "";
connectionUrl =
"jdbc:sqlserver://localhost;" +
"instanceName=SQLEXPRESS;" +
"databaseName=myDb;" +
"integratedSecurity=true";
Connection con = DriverManager.getConnection(connectionUrl);
String SQL =
"SELECT CTR_ID, CTR_FILENAME, CTR_DATA " +
"FROM CTR_DOCUMENTS " +
"WHERE CTR_ID BETWEEN 1 AND 5";
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(SQL);
while (rs.next()) {
boolean writeFile = true;
byte[] bytesIn = rs.getBytes("CTR_DATA");
//scan input byte array and copy every second byte to output byte array
byte[] bytesOut = new byte[bytesIn.length / 2];
for (int i = 0; i < bytesIn.length; i++) {
if ((i % 2) == 1) {
if (bytesIn[i] != 0x00) {
System.out.println(String.format("Darn. bytesIn value at offset %d is not 0x00. Skipping...", i));
writeFile = false;
break;
}
}
else {
bytesOut[i / 2] = bytesIn[i];
}
}
if (writeFile) {
String outFile =
"C:\\__tmp\\pdfTest\\" + rs.getString("CTR_FILENAME");
FileOutputStream fos = new FileOutputStream(outFile);
fos.write(bytesOut);
fos.close();
System.out.println(String.format("\"%s\" created.", outFile));
}
}
rs.close();
con.close();
} catch(Exception e) {
System.out.println(e.getMessage());
System.exit(0);
}
}
理由是,如果在某个地方,某个过程采用了它认为的一个&#34;字符串&#34;单字节字符(例如,Windows-1252)并转换为Unicode(例如,UCS-2LE),只需在每个字符后面插入0x00
(否则不会破坏实际数据字节),那么最简单的解决方案就是只需再取出0x00
个字节。