2015-07-02 11 views
5

Ich habe diese Klasse zu kodieren und zu dekodieren eine Datei. Wenn ich die Klasse mit TXT-Dateien ausführen, ist das Ergebnis erfolgreich. Aber wenn ich den Code mit .jpg oder .doc ausführe, kann ich die Datei nicht öffnen oder sie ist nicht gleich dem Original. Ich weiß nicht, warum das passiert. Ich habe diese Klasse http://myjeeva.com/convert-image-to-string-and-string-to-image-in-java.html geändert. Aber ich möchte diese Zeile ändernFehler beim Kodieren von Dateien in Base64 Java

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

für

byte example[] = new byte[1024]; 

und lesen Sie die Datei so oft, wie wir brauchen. Vielen Dank.

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

    public class Encode { 

Input = Eingabe Stammdatei - Output = Ausgabe-Datei root - imageDataString = String codiert

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

Ich habe in den Beispielen zu sehen, dass die Menschen ein byte [] mit derselben Länge als die Datei zu erstellen. Ich will das nicht, weil ich nicht weiß, welche Länge die Datei haben wird.

 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"); 
    } 
} 

Ich versuchte

Ändern
String imageData = encodeImage(buff); 

für

String imageData = encodeImage(buff,r); 

und das Verfahren 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); 
} 

Aber ich habe den Fehler:

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

Antwort

4

Sie haben zwei Probleme in Ihrem Programm.

Die erste, wie von @Joop Eggen erwähnt, ist, dass Sie Ihre Eingaben nicht korrekt behandeln.

Tatsächlich verspricht Java Ihnen nicht, dass Sie sogar in der Mitte der Datei die gesamten 1024 Bytes lesen.Es könnte nur 50 Bytes lesen und Ihnen sagen, dass es 50 Bytes liest und dann das nächste Mal 50 Bytes mehr.

Angenommen, Sie haben in der vorherigen Runde 1024 Bytes gelesen. Und jetzt, in der aktuellen Runde, lesen Sie nur 50. Ihr Byte-Array enthält jetzt 50 der neuen Bytes, und der Rest sind die alten Bytes aus dem vorherigen lesen!

Sie müssen also immer die genaue Anzahl der in ein neues Array kopierten Bytes kopieren und diese an Ihre Codierungsfunktion übergeben.

Also, dieses besondere Problem zu beheben, müssen Sie so etwas wie zu tun:

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

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

     String imageData = encodeImage(realBuff); 

     ... 
} 

Dies ist jedoch nicht das einzige Problem hier. Ihr echtes Problem ist mit der Base64-Codierung selbst. Geben Sie Ihre Bytes ein, brechen Sie sie in 6-Bit-Blöcke und behandeln Sie dann jeden dieser Blöcke als eine Zahl zwischen N 0 und 63. Dann nimmt es das N-te Zeichen aus seiner Zeichentabelle, um das darzustellen Stück.

Aber das bedeutet, es kann nicht nur ein einzelnes Byte oder zwei Bytes codieren, weil ein Byte 8 Bits enthält, und das bedeutet ein Stück von 6 Bits und 2 übrig gebliebenen Bits. Zwei Bytes haben 16 Bits. Das sind 2 Stücke von 6 Bits und 4 übrig gebliebene Bits.

Um dieses Problem zu lösen, codiert Base64 immer 3 aufeinander folgende Bytes. Wenn der Eingang nicht gleichmäßig durch drei geteilt wird, addiert er zusätzliche Null-Bits.

Hier ist ein kleines Programm, das das Problem veranschaulicht:

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(); 
    } 
} 

Also, mein 6-Byte-Array vorstellen, ist die Bilddatei. Und stellen Sie sich vor, dass Ihr Puffer nicht 1024 Bytes, sondern jeweils 2 Bytes liest. Das wird die Ausgabe der Codierung sein:

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

Wie Sie die Codierung des gesamten Array hat uns 8 Zeichen sehen. Jede Gruppe von drei Bytes wird in vier Blöcke von 6 Bits umgewandelt, die wiederum in vier Zeichen umgewandelt werden.

Aber die Codierung der drei Zwei-Byte-Arrays gab Ihnen eine Zeichenfolge von 9 Zeichen. Es ist eine ganz andere Saite! Jede Gruppe von zwei Bytes wurde durch Füllen mit Nullen auf drei Blöcke von 6 Bits erweitert. Und da Sie kein Padding angefordert haben, erzeugt es nur 3 Zeichen, ohne das zusätzliche =, das normalerweise markiert, wenn die Anzahl der Bytes nicht durch 3 teilbar ist.

Die Ausgabe von dem Teil des Programms, der das 8-Zeichen dekodiert , korrekte codierte Zeichenfolge ist in Ordnung:

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

Aber das Ergebnis von dem Versuch, die 9-Zeichen, falsch codierte Zeichenfolge zu entschlüsseln:

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) 

Nicht gut! Eine gute Base64-Zeichenfolge sollte immer Vielfache von 4 Zeichen haben, und wir haben nur 9.

Da Sie eine Puffergröße von 1024, die kein Vielfaches von 3 ist, wählen, wird das Problem passieren. Sie müssen jedes Mal ein Vielfaches von 3 Byte codieren, um die richtige Zeichenfolge zu erzeugen.In der Tat müssen Sie einen Puffer Größe 3072 oder etwas Ähnliches erstellen.

Aber wegen des ersten Problems, sei sehr vorsichtig, was du an den Encoder übergibst. Weil es immer passieren kann, dass Sie weniger als 3072 Bytes lesen. Und wenn die Zahl nicht durch drei teilbar ist, wird das gleiche Problem auftreten.

+0

Danke für Ihre Zeit, um die Antwort zu machen. Ich habe das Base64 besser verstanden. Ich werde meine Klasse mit Ihren Ratschlägen ändern. – JGG

+0

@JGG Schauen Sie sich die Methode 'Base64.Encoder.wrap (OutputStream)' an. Es erstellt einen Ausgabestream, in dem Sie die Bytes in Chunks schreiben können, ohne das Problem zu haben, das ich erwähnte (z. B. können Sie jedes Mal 1024 Bytes schreiben, und nachdem Sie den Stream geschlossen haben, haben Sie die richtigen codierten Daten in seiner Ausgabe). – RealSkeptic

0

Blick auf:

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

read -1 zurück auf End-of-Datei oder die tatsächliche Anzahl von Bytes, die gelesen wurden.

So kann die letzte buff möglicherweise nicht vollständig gelesen werden, und enthalten sogar Müll von jedem vorherigen lesen. Sie müssen also r verwenden.

Da dies eine Aufgabe ist, liegt der Rest bei Ihnen.

By the way:

byte[] array = new byte[1024] 

ist konventionelleren in Java. Die Syntax:

byte array[] = ... 

war für die Kompatibilität mit C/C++.

+0

Vielen Dank für Ihre Antwort. Ich habe zusätzliche Informationen hinzugefügt. Ich habe versucht, die Klasse mit r, aber ich hatte eine Kompilierung Problem, und ich fand keine Lösung – JGG

Verwandte Themen