2

I am trying to read an image and use Base64 encoding to convert it into byte array and then to string to send it over network. The problem is that when I try to decode the Base64 encoded string, I am getting incorrect data.

For eg. I am facing issue with below special character.

I am using following code for encoding:

byte[] b = Base64.encodeBase64(IOUtils.toByteArray(loInputStream));
String ab = new String(b);

IOUtils is org.apache.commons.io.IOUtils.

and loInput

Code for decoding:

byte[] c = Base64.decodeBase64(ab.getBytes());
String ca = new String(c);
System.out.println(ca);

It prints ? for decoded String.

Can anyone please let me know the issue.

bluish
  • 26,356
  • 27
  • 122
  • 180
Ankit
  • 3,083
  • 7
  • 35
  • 59
  • Where does class `Base64` come from (this is not a standard Java API class)? – Jesper Jun 26 '11 at 13:56
  • @jesper: I am using apache commons (org.apache.commons.codec.binary.Base64) – Ankit Jun 26 '11 at 14:00
  • nos below has already answered your question. Just a small comment: when converting String to byte[] and visa-versa, it's better to specify encoding explicitly and not to rely on platform settings. I mean it should be "new String(b, "UTF-8")" and "ab.getBytes("UTF-8")" – Tarlog Jun 26 '11 at 14:49
  • @Tarlog : ahhhh!! thanks a lot, That might solve my problem. – Ankit Jun 26 '11 at 14:57
  • @Tarlog: if you use `encodeBase64String`, you already get a UTF-8 String (US_ASCII, really, since Base64 only uses characters from US_ASCII). – ninjalj Jun 27 '11 at 21:22
  • @ninjalj @Ankit was talking about org.apache.commons.codec.binary.Base64. There is no method encodeBase64String in this library. – Tarlog Jun 28 '11 at 06:29
  • @Tarlog: are you sure? http://commons.apache.org/codec/apidocs/org/apache/commons/codec/binary/Base64.html#encodeBase64String(byte[]) – ninjalj Jun 28 '11 at 18:46
  • @Tarlog: This method was not present in version 1.3 of this jar, I am using version 1.5 now, this method is present there. – Ankit Jun 29 '11 at 08:47
  • I see. It seems I have also an old jar in my classpath... Should upgrade :) – Tarlog Jun 29 '11 at 09:22

2 Answers2

4

If your input is an image, it makes sense to encode it as base64 - base64 is text, and can be represented by a String.

Decoding it again though, you get the original image. An image is usually a binary format; it does not make sense to try to convert that to a string - it is not text.

That is, the last 2 lines:

   String ca = new String(c);
   System.out.println(ca);

Simply does not make sense to do.

If you want to check that the decoding produces the same output as the original input, do e.g.

  System.out.println("Original and decoded are the same: " + Arrays.equals(b,c));

(Or save the byte array to a file and view the image in an image viewer)

Carl Manaster
  • 39,912
  • 17
  • 102
  • 155
nos
  • 223,662
  • 58
  • 417
  • 506
  • @nos: Thanks for your reply, The reason why I am converting it back to string is because I want to recreate that image again using the Base64 Encoded String. Is there any other way to do the same thing? – Ankit Jun 26 '11 at 14:05
  • @nos: I am sending this encoded string to my application, which uses my CMS api (alfresco) to create image, which takes String as input argument for Image data. – Ankit Jun 26 '11 at 14:06
  • 2
    @Ankit you already recreated it in the line that says `byte[] c = Base64.decodeBase64(ab.getBytes());` , the array `c` is now the original image. If your alfresco api takes an image as a string, you need to read the documentation about what format it expects. Perhaps the API expects you to send it the base64 encoded representation of the image. (i.e. you send it the result of `byte b[] = Base64.encodeBase64(IOUtils.toByteArray(loInputStream)); String ab = new String(b);` – nos Jun 26 '11 at 14:07
  • @nos: You are correct, It is working fine if i am trying to write the bytes to a file using FileOutputStream, but doesn't work right if I change the bytes to String and try to write it. I will have to think of another way to recreate image. Thanks for your help – Ankit Jun 26 '11 at 14:54
  • @Ankit As mentioned, you're not suppose to convert the bytes of an image directly to a string. If you need a string from an image, you do what you already did. You base64 encode it. Or you encode it as something else, e.g. base16 (more commonly known as hex) – nos Jun 26 '11 at 16:25
  • @Ankit: which Alfresco API are you using, that takes a `String` argument for image data? – ninjalj Jun 27 '11 at 21:09
2

As I've said elsewhere, in Java, String is for text, and byte[] is for binary data.

String ≠ byte[]

Text ≠ Binary Data

An image is binary data. Base64 is an encoding which allows transmission of binary data over US_ASCII compatible text channels (there is a similar encoding for supersets of ASCII text: Quoted Printable).

So, it goes like:

Image (binary data) → Image (text, Base64 encoded binary data) → Image (binary data)

where you would use String encodeBase64String(byte[]) to encode, and byte[] decode(String) to decode. These are the only sane API's for Base64, byte[] encodeBase64(byte[]) is misleading, the result is US_ASCII-compatible text (so, a String, not byte[]).

Now, text has a charset and an encoding, String uses a fixed Unicode/UTF-16 charset/encoding combination internally, and you have to specify a charset/encoding when converting something from/to a String, either explicitly, or implicitly, using the platform's default encoding (which is what PrintStream.println() does). Base64 text is pure US_ASCII, so you need to use that, or a superset of US_ASCII. org.apache.commons.codec.binary.Base64 uses UTF8, which is a superset of US_ASCII, so all is well. (OTOH, the internal java.util.prefs.Base64 uses the platform's default encoding, so I guess it would break if you start your JVM with, say, an UTF-16 encoding).

Back on topic: you've tried to print the decoded image (binary data) as text, which obviously hasn't worked. PrintStream has write() methods that can write binary data, so you could use those, and you would get the same garbage as if you wrote the original image. It would be much better to use a FileOutputStream, and compare the resulting file with the original image file.

Community
  • 1
  • 1
ninjalj
  • 42,493
  • 9
  • 106
  • 148