Ошибка кодирования файлов в base64 java
у меня есть этот класс для кодирования и декодирования файла. Когда я управляю классом .txt файлы результат успешно. Но когда я запускаю код .формат JPG или. док я не могу открыть файл или он не равен оригиналу. Я не знаю, почему это происходит. Я изменил этот класс 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 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
2 ответов
у вас есть две проблемы в вашей программе.
первое, Как упоминалось в @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 последовательных байта. Если вход не делится равномерно на три, это добавляет дополнительные нулевые биты.
вот небольшая программа, которая демонстрирует проблему:
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 символов. Каждая группа из трех байтов преобразуется в четыре блока по 6 бит, которые в свою очередь преобразуются в четыре символа.
но кодировка трех двухбайтовых массивов дала вам строку из 9 символов. Это совершенно другая струна! Каждая группа из двух байтов была расширена до трех кусков по 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
байт. И тогда, если число не делится на три, возникнет та же проблема.
посмотреть:
while ( ( r = imageInFile.read( buff)) > 0 ) {
String imageData = encodeImage(buff);
read
возвращает -1 в конец файла или на фактическое количество байт это было прочитано.
Итак, последний buff
может быть не полностью прочитан и даже содержать мусор из любого предыдущего чтения. Поэтому вам нужно использовать r
.
как это задание, остальное зависит от вас.
кстати:
byte[] array = new byte[1024]
является более традиционным в Java. Синтаксис:
byte array[] = ...
был для совместимости с C / C++.