在使用MS Access创建的Oracle10g数据库中访问BLOBS

时间:2013-11-27 17:15:30

标签: java oracle ms-access character-encoding blob

我正在编写一个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,但这不起作用。 我在这里错过了什么吗?

1 个答案:

答案 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个字节。