2009-08-12 46 views
1

我将jpeg图像插入到我的UTF-8编码Postgres数据库到bytea列/ s中。我正在使用准备好的sql语句插入图像。在声明中,我在jpeg镜像中创建了一个文件对象,然后将其作为FileInputStream传递给setBinaryStream方法。但是每一个现在,再次执行该语句我的Java应用程序会抛出异常,指出:Postgres在通过Java插入图像时编码“UTF8”错误

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

出现这种情况有选择的几个图像这很奇怪。这些图像是从前一组图像中提取的,所有先前的图像都插入了罚款,只有几个提取的图像似乎会导致错误。那么我该如何解决这个问题呢?能够以某种方式将字节流编码为UTF-8?或者它是数据库的问题?

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

其代码如下要求...

有一些代码,否则缺少这将会是很长,但是,基本上我做的路径和目录名的几项检查,以确保他们遵守文件系统规则。这是一个遍历所有子目录的循环,并在所有子目录中添加jpeg文件的所有 。然后我到带有图像子目录的下一个目录,直到没有任何图像。我还没有添加尝试捕获和记录部分。

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()抛出:

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

+1

我们能看到你的代码吗? – 2009-08-12 14:55:57

+0

显示打开文件并将其传递给JDBC的实际代码,例外情况也很好看,例如,无论是扼流圈数据库还是文件读取失败的数据库。 – nos 2009-08-12 15:01:04

+0

你试图插入图像的列的类型是什么?这是否是一次机会? – 2009-08-12 15:35:02

回答

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'))在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); 

问表是:

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

数据(插入后)的外观像这样:

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

好吧,我根本不知道Perl,但我想我得到了一般想法。我正确地假设我需要执行以下步骤: 1.获取文件并创建一个字节数组 2.将字节数组中包含的文件存储为字节 3.将字节数组编码为'base64' 4 。然后尝试执行SQL语句 问题,在您的代码中执行'undef,$ encoded'是什么? 是否有人知道如何将File对象分别转换为包含File对象的相应字节的字节数组? – binarycreations 2009-08-12 16:01:35

+0

undef不重要 - 它是一个DBI(Perl中的数据库接口)的东西。 $ encoded是包含$ blob编码(至base64)内容的变量。 和 - > do(sql,undef,$ some_variable),事情只是运行SQL代替$ some_variable而不是?在SQL中。 – 2009-08-12 16:48:02

1

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

我会忍不住为Base64编码流如果可能的话。

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

+0

出于兴趣,什么导致这个无效的字节编码。我有一个大的图像,剪切和裁剪它的一小部分,然后我将两个都添加到数据库。较大的图像插入罚款如何裁剪一个。我是否愚蠢地认为较小的图像不包含较大图像的子集字节表示? – binarycreations 2009-08-12 16:07:10

+0

@格拉汉姆:我对图像格式并不熟悉,但我不认为大图像的裁剪部分会转换为相同的字节序列 - 我认为我说得对,大多数格式都采用某种形式压缩将取决于每个像素值的频率。例如,如果图像整体大部分为浅色,并且裁剪出较暗的区域,则可能会发生这种情况。 – 2009-08-12 16:16:48

+0

哦,所以问题可能是由于在jpeg格式中使用的压缩创建了一个0x84的字节。所以也许改变它被压缩多少可能也解决了这个问题,或者我吠叫了错误的树? – binarycreations 2009-08-12 16:23:33

0

问题解决了:-)经过编码和解码不同的文件,我发现,发生同样的SQL错误。我相信这个问题是由于FK属性之一在我创建的Java应用程序中插入一些值后在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=0xthis网内查找的价值,它说的性格是:

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'\')

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

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

  5. 错误确实可以在最好的时候欺骗。如果你得到这个错误,你必须先检查我所做的所有事情。

顺便说一句,感谢所有发布答案的人。我总是非常慷慨地放弃他们的时间来帮助别人!它真的很有用,并证明了为什么StackOverflow能够很好地工作! :-)

相关问题