簡體   English   中英

在base64 java中編碼文件失敗

[英]Failure encoding files in base64 java

我有這個類來編碼和解碼文件。 當我使用.txt文件運行該類時,結果是成功的。 但是當我用.jpg或.doc運行代碼時,我無法打開文件,或者它不等於原始文件。 我不知道為什么會這樣。 我修改了這個類http://myjeeva.com/convert-image-to-string-and-string-to-image-in-java.html 但我想改變這一行

byte imageData[] = new byte[(int) file.length()];

對於

byte example[] = new byte[1024];

並多次讀取我們需要的文件。 謝謝。

import java.io.*;
import java.util.*;

  public class Encode {

輸入=輸入文件根 - 輸出=輸出文件根 - imageDataString =字符串編碼

  String input;
  String output;
  String imageDataString;


  public void setFileInput(String input){
    this.input=input;
  }

  public void setFileOutput(String output){
    this.output=output;
  }

  public String getFileInput(){
    return input;
  }

  public String getFileOutput(){
    return output;
  }

  public String getEncodeString(){
    return  imageDataString;
  }

  public String processCode(){
    StringBuilder sb= new StringBuilder();

    try{
        File fileInput= new File( getFileInput() );
        FileInputStream imageInFile = new FileInputStream(fileInput);

我在例子中看到人們創建一個與文件長度相同的byte []。 我不想要這個,因為我不知道該文件的長度。

        byte buff[] = new byte[1024];

        int r = 0;

        while ( ( r = imageInFile.read( buff)) > 0 ) {

          String imageData = encodeImage(buff);

          sb.append( imageData);

          if ( imageInFile.available() <= 0 ) {
            break;
          }
        }



       } catch (FileNotFoundException e) {
        System.out.println("File not found" + e);
      } catch (IOException ioe) {
        System.out.println("Exception while reading the file " + ioe);

    } 

        imageDataString = sb.toString();

       return imageDataString;
}  


  public  void processDecode(String str) throws IOException{

      byte[] imageByteArray = decodeImage(str);
      File fileOutput= new File( getFileOutput());
      FileOutputStream imageOutFile = new FileOutputStream( fileOutput);

      imageOutFile.write(imageByteArray);
      imageOutFile.close();

}

 public static String encodeImage(byte[] imageByteArray) {

      return  Base64.getEncoder().withoutPadding().encodeToString( imageByteArray);

    }

    public static byte[] decodeImage(String imageDataString) {
      return  Base64.getDecoder().decode(  imageDataString);  

    }


  public static void main(String[] args) throws IOException {

    Encode a = new Encode();

    a.setFileInput( "C://Users//xxx//Desktop//original.doc");
    a.setFileOutput("C://Users//xxx//Desktop//original-copied.doc");

    a.processCode( );

    a.processDecode( a.getEncodeString());

    System.out.println("C O P I E D");
  }
}

我試過改變

String imageData = encodeImage(buff);

對於

String imageData = encodeImage(buff,r);

和方法encodeImage

public static String encodeImage(byte[] imageByteArray, int r) {

     byte[] aux = new byte[r];

     for ( int i = 0; i < aux.length; i++) {
       aux[i] = imageByteArray[i];

       if ( aux[i] <= 0 ) {
         break;
       }
     }
return  Base64.getDecoder().decode(  aux);
}

但我有錯誤:

Exception in thread "main" java.lang.IllegalArgumentException: Last unit does not have enough valid bits   

你的程序有兩個問題。

第一個,正如@Joop Eggen所提到的,是你沒有正確處理你的輸入。

實際上,Java並不保證即使在文件的中間,你也會讀取整個1024字節。 它只能讀取50個字節,並告訴它讀取50個字節,然后下次再讀取50個字節。

假設您在上一輪中讀取了1024個字節。 而現在,在本輪中,你只讀50個。你的字節數組現在包含50個新字節,其余的是前一個讀取的舊字節!

因此,您始終需要復制復制到新數組的確切字節數,並將其傳遞給編碼函數。

因此,要解決此特定問題,您需要執行以下操作:

 while ( ( r = imageInFile.read( buff)) > 0 ) {

      byte[] realBuff = Arrays.copyOf( buff, r );

      String imageData = encodeImage(realBuff);

      ...
 }

但是,這不是唯一的問題。 你真正的問題在於Base64編碼本身。

Base64所做的是取你的字節,將它們分成6位塊,然后將每個塊視為N 0和63之間的數字。然后它從字符表中取出第N個字符來表示該塊。

但這意味着它不能只編碼一個字節或兩個字節,因為一個字節包含8位,這意味着一個6位的塊和2個剩余位。 兩個字節有16位。 這是2個6位的塊,還有4個剩余的位。

要解決此問題,Base64始終編碼3個連續字節。 如果輸入沒有均勻地除以3,則會增加額外的零位

這是一個演示問題的小程序:

package testing;

import java.util.Base64;

public class SimpleTest {

    public static void main(String[] args) {

        // An array containing six bytes to encode and decode.
        byte[] fullArray = { 0b01010101, (byte) 0b11110000, (byte)0b10101010, 0b00001111, (byte)0b11001100, 0b00110011 };

        // The same array broken into three chunks of two bytes.

        byte[][] threeTwoByteArrays = {
            {       0b01010101, (byte) 0b11110000 },
            { (byte)0b10101010,        0b00001111 },
            { (byte)0b11001100,        0b00110011 }
        };
        Base64.Encoder encoder = Base64.getEncoder().withoutPadding();

        // Encode the full array

        String encodedFullArray = encoder.encodeToString(fullArray);

        // Encode the three chunks consecutively 

        StringBuilder encodedStringBuilder = new StringBuilder();
        for ( byte [] twoByteArray : threeTwoByteArrays ) {
            encodedStringBuilder.append(encoder.encodeToString(twoByteArray));
        }
        String encodedInChunks = encodedStringBuilder.toString();

        System.out.println("Encoded full array: " + encodedFullArray);
        System.out.println("Encoded in chunks of two bytes: " + encodedInChunks);

        // Now  decode the two resulting strings

        Base64.Decoder decoder = Base64.getDecoder();

        byte[] decodedFromFull = decoder.decode(encodedFullArray);   
        System.out.println("Byte array decoded from full: " + byteArrayBinaryString(decodedFromFull));

        byte[] decodedFromChunked = decoder.decode(encodedInChunks);
        System.out.println("Byte array decoded from chunks: " + byteArrayBinaryString(decodedFromChunked));
    }

    /**
     * Convert a byte array to a string representation in binary
     */
    public static String byteArrayBinaryString( byte[] bytes ) {
        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for ( byte b : bytes ) {
            sb.append(Integer.toBinaryString(Byte.toUnsignedInt(b))).append(',');
        }
        if ( sb.length() > 1) {
            sb.setCharAt(sb.length() - 1, ']');
        } else {
            sb.append(']');
        }
        return sb.toString();
    }
}

所以,想象一下我的6字節數組是你的圖像文件。 並且假設您的緩沖區不是每次讀取1024個字節而是2個字節。 這將是編碼的輸出:

Encoded full array: VfCqD8wz
Encoded in chunks of two bytes: VfAqg8zDM

如您所見,完整數組的編碼為我們提供了8個字符。 每組三個字節被轉換成4個6比特的塊,然后轉換成4個字符。

但是三個雙字節數組的編碼為您提供了一個包含9個字符的字符串。 這是一個完全不同的字符串! 通過用零填充將每組兩個字節擴展為3個6比特的塊。 並且由於你沒有要求填充,它只產生3個字符,沒有額外的=通常標記當字節數不能被3整除時。

解碼8個字符,正確編碼字符串的程序部分的輸出很好:

Byte array decoded from full: [1010101,11110000,10101010,1111,11001100,110011]

但嘗試解碼9個字符的錯誤編碼字符串的結果是:

Exception in thread "main" java.lang.IllegalArgumentException: Last unit does not have enough valid bits
    at java.util.Base64$Decoder.decode0(Base64.java:734)
    at java.util.Base64$Decoder.decode(Base64.java:526)
    at java.util.Base64$Decoder.decode(Base64.java:549)
    at testing.SimpleTest.main(SimpleTest.java:34)

不好! 一個好的base64字符串應該總是有4個字符的倍數,我們只有9個。

由於您選擇的緩沖區大小為1024(不是3的倍數),因此出現問題。 您需要每次編碼3個字節的倍數以生成正確的字符串。 所以實際上,你需要創建一個3072或類似的緩沖區。

但由於第一個問題,要小心傳遞給編碼器的內容。 因為總是會發生讀取少於3072字節的事情。 然后,如果數字不能被3整除,則會出現同樣的問題。

看着:

    while ( ( r = imageInFile.read( buff)) > 0 ) {
      String imageData = encodeImage(buff);

read在文件結尾返回-1 讀取的實際字節數

因此,最后一個buff可能不會被完全讀取,甚至包含來自任何先前讀取的垃圾。 所以你需要使用r

由於這是一項任務,其余部分由您決定。

順便說說:

 byte[] array = new byte[1024]

在Java中更常規。 語法:

 byte array[] = ...

是為了與C / C ++兼容。

暫無
暫無

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

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