簡體   English   中英

通過Java插入圖像時,Postgres編碼為“ UTF8”錯誤

[英]Postgres encoding “UTF8” error whilst inserting images via Java

我正在將jpeg圖像插入UTF-8編碼的Postgres數據庫中,插入bytea列。 我正在使用准備好的sql語句插入圖像。 在該語句中,我在Java中創建了jpeg圖像的文件對象,然后將其作為FileInputStream傳遞到setBinaryStream方法中。 但是,一旦執行該語句,我的Java應用程序就會不時地拋出異常,說明:

“錯誤:編碼” UTF8“的字節序列無效:0x84”

對於少數幾張奇怪的圖像,會發生這種情況。 這些圖像是從先前的一組圖像中提取的,所有先前的圖像都很好地插入,只有少數提取的圖像似乎會導致錯誤。 那么我該如何解決這個問題呢? 可以將字節流以某種方式編碼為UTF-8嗎? 還是數據庫有問題?

順便說一句,如果我用新的圖像替換提取的圖像並將其另存為jpegs,則會發生相同的錯誤。 謝謝你的幫助!

代碼如下所示。

缺少一些代碼,否則將會很長,但是,基本上,我對路徑和目錄名稱進行了一些檢查,以確保它們遵守文件系統規則。 這是一個遍歷所有子目錄的循環,並將所有jpeg文件添加到這些子目錄中。 然后,我進入帶有圖像子目錄的下一個目錄,直到那里沒有。 我還沒有添加try-catches和日志記錄部分。

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”的字節序列無效:org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:1608)處的0x84。 org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:194)的core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1343)org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:451)在org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:343)在org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:350)在nuffielddb.HandDB.addExtractedImages(atDBuf。 Main.main(Main.java:37)Java結果:1

好吧,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 );

q表是:

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

數據(插入后)如下所示:

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

嗯,這就是窗外的想法(與我對原始問題的評論有關)-顯然正在發生某種編碼,並且某些圖像包含無效的字節序列,因此無法進行編碼,但是我這樣做的原因是使用了clob(必須學會更仔細地閱讀問題)。

如果可能的話,我很想對BASE64進行編碼。

一個快速的Google出現了這個問題-http://commons.apache.org/codec/api-release/org/apache/commons/codec/binary/Base64InputStream.html-我懷疑這可能有用(即使只是為了靈感)。

解決的問題:-)在對不同的文件進行編碼和解碼之后,我發現發生了相同的SQL錯誤。 我相信是由於在我創建的Java應用程序中插入了一些值之后,FK屬性之一在Postgres數據庫中存儲了一個空白值而發生了該問題。 在子查詢中引用值hand_id時(如下):

(從hands.age =“ + age +” AND hands.gender ='“ +性別+”'AND hands.location ='“ +路徑+目錄+ imageNames [i] +”'從手中選擇SELECT hands.hand_id))

在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. 錯誤在最好的時候確實可以欺騙。 如果遇到此錯誤,您要先檢查一下我做過的所有事情。

順便說一句,謝謝所有發布答案的人。 人們總是慷慨地放棄自己的時間來幫助別人,我總是感到驚訝! 它確實很有用,並證明了StackOverflow為什么如此出色! :-)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM