我将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
答案 0 :(得分:2)
好吧,0x84 不是一个有效的utf8字符:
=> perl -e 'print "\x84"' | iconv -f utf8 -t utf8
iconv: illegal input sequence at position 0
通常 - bytea可以使用任何字节,但INSERT语句是文本字符串,因此必须符合客户端的编码!
插入数据的简单方法:
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'
那么,我学到了什么?
总是检查您的SQL语句,即使您认为它是正确的
将字符串插入VARCHAR列时,请记住所需的不同转义字符,并在第一个打开的引号之前放置一个E(如此,E')。请记住,反斜杠需要添加两个反斜杠(因此,E'\')
如果您确实遇到数据库编码问题,您可以随时尝试重新定义配置中的数据库编码,或者将数据转换并编码为所需的编码,以使数据库可以接受。
APACHE COMMONS作为Java的有用base64编码编解码器。非常有用,必须记住以后的日期。
错误在最好的时候真的可以欺骗。如果你收到这个错误,你就想要检查我先做的所有事情。