Postgres在通过Java插入图像时编码“UTF8”错误

时间:2009-08-12 14:53:05

标签: java sql database postgresql image

我将jpeg图像插入到我的UTF-8编码的Postgres数据库中,进入bytea列/ s。我正在使用准备好的sql语句来插入图像。在语句中,我在Java中创建了一个jpeg图像的文件对象,然后将其作为FileInputStream传递给setBinaryStream方法。但是,一旦执行该语句,我的Java应用程序将不时抛出异常,并声明:

“错误:用于编码的无效字节序列”UTF8“:0x84”

对于奇数的少数几个图像会发生这种情况。这些图像是从前一组图像中提取的,所有以前的图像插入都很精细,只有少数提取的图像似乎会导致错误。那么我该如何解决这样的问题呢?能以某种方式将字节流编码为UTF-8吗?或者这是数据库的问题?

顺便说一句,如果我用新的图像替换提取的图像并将它们保存为jpeg,则会发生相同的错误。谢谢你的帮助!

代码如下所示......

缺少一些代码,否则会很长,但基本上我会对路径和目录名称进行一些检查,以确保它们符合文件系统规则。这是一个循环,遍历所有子目录并添加所有子目录 这些子目录中的jpeg文件。然后我转到带有图像的子目录的下一个目录,直到没有。我还没有添加try-catches和logging部分。

String imgStr = image.toString();
int age = getAgeFromDir(imgStr);
String gender = getSexFromDir(imgStr);
String table = "";
switch(validIdx){
    case 0: table = "carpals";
        break;
    case 1: table = "d_phalanges";
        break;
    case 2: table = "p_phalanges";
        break;
    case 3: table = "i_phalanges";
        break;
    case 4: table = "epiphyses";
        break;
    case 5: table = "sesamoids";
        break;
    case 6: table = "metacarpals ";
        break;
}

    PreparedStatement ps = con.prepareCall("INSERT INTO " + table +
            " VALUES( (SELECT hands.hand_id FROM hands WHERE hands.age = " + age + " AND hands.gender = '" + gender + "' AND hands.location = '" + path + directory + imageNames[i] + "' )," +
            " (SELECT COUNT(" + table + ".location) FROM " + table + " ), " +
            " ?, ? )"   );

        //go through each sub-directory which contains jpeg images and add them to
        //the database
        File sublist = new File(image + "\\" + subdir[j]);
        String[] files = sublist.list();
        String[] pics = sublist.list(new JpegFilter());

        if(files.length > pics.length){
            //WRITE TO LOG
            //WARNING UNEXPECTED FILES OR DIRECTORIES FOUND IN....
        }

            for(int r = 0; r < pics.length; r++ ){

                    String location = image + "\\" + subdir[j] + "\\" + pics[r];
                    System.out.println(i + "\t" + r + " location : " + location);

                    File f = new File(location);
                    FileInputStream pic = new FileInputStream(f);


                    if(f.isFile()){
                    ps.setString(2, location);
                    ps.setBinaryStream(1, pic, (int)f.length());
                    ps.execute();
                    pic.close();
                    }
            }
    ps.close();

}

抛出的SQLException如下,它抛出ps.execute():

线程“main”中的异常org.postgresql.util.PSQLException:错误:编码“UTF8”的无效字节序列:0x84         at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:1608)         at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1343)         at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:194)         at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:451)         at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:350)         at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:343)         在nuffielddb.HandDB.addExtractedImages(HandDB.java:406)         在nuffielddb.Main.main(Main.java:37) Java结果:1

3 个答案:

答案 0 :(得分:2)

好吧,0x84 是一个有效的utf8字符:

=> perl -e 'print "\x84"' | iconv -f utf8 -t utf8
iconv: illegal input sequence at position 0

通常 - bytea可以使用任何字节,但INSERT语句是文本字符串,因此必须符合客户端的编码!

插入数据的简单方法:

  1. 将应用程序中的数据编码为Base64格式(还有其他选项,但这个选项对我来说最简单)
  2. 插入:INSERT INTO q(x)VALUES(decode(?,'base64'))
  3. Perl中的示例(抱歉,我不写Java):

    #!/usr/bin/perl
    use MIME::Base64;
    use DBI;
    
    my $dbh = DBI->connect( "dbi:Pg:dbname=depesz;port=5840", "depesz" );
    my $blob = "\x84";
    my $encoded = encode_base64( $blob );
    $dbh->do("INSERT INTO q (x) VALUES (decode(?, 'base64'))", undef, $encoded );
    

    q表是:

          Table "public.q"
     Column | Type  | Modifiers
    --------+-------+-----------
     x      | bytea |
    

    数据(插入后)如下所示:

    # select x, octet_length(x) from q;
      x   | octet_length
    ------+--------------
     \x84 |            1
    (1 row)
    

答案 1 :(得分:1)

这就是窗外的想法(与我对原始问题的评论有关) - 显然有一些编码发生,某些图像包含无效的字节序列,因此无法编码,但我的理由是是使用clob(必须学会更仔细地阅读问题)。

如果可能的话,我很想对BASE64进行编码。

一个快速的谷歌出现了这个 - http://commons.apache.org/codec/api-release/org/apache/commons/codec/binary/Base64InputStream.html - 我怀疑它可能有用(即使只是为了灵感)。

答案 2 :(得分:0)

问题解决了:-)在对不同文件进行编码和解码后,我发现发生了同样的SQL错误。我认为问题是由于在我创建的Java应用程序插入一些值后,其中一个FK属性在Postgres数据库中存储了一个空值。在子查询(下面)中引用值hand_id时:

(SELECT hands.hand_id FROM hands WHERE hands.age =“+ age +”AND hands.gender ='“+ gender +”'AND hands.location ='“+ path + directory + imageNames [i] +” ')

当在Java中替换变量时postgres中返回的结果是某种空的不可返回字符,我相信像Java中的转义字符或回车符(例如反斜杠的“\”)。在UTF-8值表和字符表示中查找字符后,该表显示一个空格。

在我找到http://www.utf8-chartable.de/unicode-utf8-table.pl?utf8=0x this的网上查找值时,它说的是:

Unicode value, Character, UTF-Hex, Name

----------------------------------------------

U+0084,             ,0xc2 0x84,<control>

请注意,表中的字符列是空的。

问题是由不包含必要转义字符的子查询引起的。 要解决此问题,必需的转义字符已添加到SQL子查询中。在我的代码中,它意味着发送的最终SQL语句中的'hands.location'部分发生了以下更改:

BEFORE

... hands.location = 'C:\directory\anotherdir\picture.jpg'

AFTER

... hands.location = E'C:\\directory\\anotherdir\\picture.jpg'

那么,我学到了什么?

  1. 总是检查您的SQL语句,即使您认为它是正确的

  2. 将字符串插入VARCHAR列时,请记住所需的不同转义字符,并在第一个打开的引号之前放置一个E(如此,E')。请记住,反斜杠需要添加两个反斜杠(因此,E'\')

  3. 如果您确实遇到数据库编码问题,您可以随时尝试重新定义配置中的数据库编码,或者将数据转换并编码为所需的编码,以使数据库可以接受。

  4. APACHE COMMONS作为Java的有用base64编码编解码器。非常有用,必须记住以后的日期。

  5. 错误在最好的时候真的可以欺骗。如果你收到这个错误,你就想要检查我先做的所有事情。

  6. 不过,谢谢所有发布答案的人。人们慷慨地放弃时间帮助别人,我总是感到惊讶!它真的很有用,并证明了为什么StackOverflow工作得那么好! : - )