以 base64 编码文件失败 java
Failure encoding files in base64 java
我有这个 class 来编码和解码文件。当我 运行 带有 .txt 文件的 class 结果是成功的。但是当我 运行 带有 .jpg 或 .doc 的代码时,我无法打开文件或者它不等于原始文件。我不知道为什么会这样。我修改了这个 class
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
查看:
while ( ( r = imageInFile.read( buff)) > 0 ) {
String imageData = encodeImage(buff);
read
returns -1 在文件末尾 或 实际字节数 已阅读。
所以最后一个 buff
可能没有被完全读取,甚至包含任何先前读取的垃圾。所以你需要使用 r
.
因为这是一个作业,剩下的就看你了。
顺便说一句:
byte[] array = new byte[1024]
在Java中更常规。语法:
byte array[] = ...
是为了与 C/C++ 兼容。
你的程序有两个问题。
第一个,正如@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 个字符 table, 代表那个块。
但这意味着它不能只编码一个字节或两个字节,因为一个字节包含 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
字节的情况。然后,如果这个数不能被三整除,也会出现同样的问题。
我有这个 class 来编码和解码文件。当我 运行 带有 .txt 文件的 class 结果是成功的。但是当我 运行 带有 .jpg 或 .doc 的代码时,我无法打开文件或者它不等于原始文件。我不知道为什么会这样。我修改了这个 class 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
查看:
while ( ( r = imageInFile.read( buff)) > 0 ) {
String imageData = encodeImage(buff);
read
returns -1 在文件末尾 或 实际字节数 已阅读。
所以最后一个 buff
可能没有被完全读取,甚至包含任何先前读取的垃圾。所以你需要使用 r
.
因为这是一个作业,剩下的就看你了。
顺便说一句:
byte[] array = new byte[1024]
在Java中更常规。语法:
byte array[] = ...
是为了与 C/C++ 兼容。
你的程序有两个问题。
第一个,正如@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 个字符 table, 代表那个块。
但这意味着它不能只编码一个字节或两个字节,因为一个字节包含 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
字节的情况。然后,如果这个数不能被三整除,也会出现同样的问题。