Eyal Lupu is a Java/JEE software architect, consultant and trainer. Working as a software professional since 1993 Eyal is specialized in architecting, designing and building infrastructures and frameworks for software development teams. Eyal is also specialized in technology leadership of globally distributed development teams. He blogs at http://blog.eyallupu.com Eyal is a DZone MVB and is not an employee of DZone and has posted 6 posts at DZone. You can read more from them at their website. View Full User Profile

Base64 Encoding in Java 8

11.15.2013
| 11332 views |
  • submit to reddit

The lack of Base64 encoding API in Java is, in my opinion, by far one of the most annoying holes in the libraries. Finally Java 8 includes a decent API for it in the java.util package. Here is a short introduction of this new API (apparently it has a little more than the regular encode/decode API). 



The java.util.Base64 Utility Class The entry point to Java 8's Base64 support is the java.util.Base64 class. It provides a set of static factory methods used to obtain one of the three Base64 encodes and decoders: 
  • Basic
  • URL
  • MIME
Basic Encoding The standard encoding we all think about when we deal with Base64: no line feeds are added to the output and the output is mapped to characters in the Base64 Alphabet: A-Za-z0-9+/ (we see in a minute why is it important). Here is a sample usage:

// Encode 
String asB64 = Base64.getEncoder().encodeToString("some string".getBytes("utf-8"));
System.out.println(asB64); // Output will be: c29tZSBzdHJpbmc=

// Decode
byte[] asBytes = Base64.getDecoder().decode("c29tZSBzdHJpbmc=");
System.out.println(new String(asBytes, "utf-8")); // And the output is: some string

It can't get simpler than that and, unlike in earlier versions of Java, there is any need for external dependencies (commons-codec), Sun classes (sun.misc.BASE64Decoder) or JAXB's DatatypeConverter (Java 6 and above). However it gets even better.
URL Encoding Most of us are used to get annoyed when we have to encode something to be later included in a URL or as a filename - the problem is that the Base64 Alphabet contains meaningful characters in both URIs and filesystems (most specifically the forward slash (/)). The second type of encoder uses the alternative "URL and Filename safe" Alphabet which includes -_ (minus and underline) instead of +/. See the following example:

String basicEncoded = Base64.getEncoder().encodeToString("subjects?abcd".getBytes("utf-8"));
System.out.println("Using Basic Alphabet: " + basicEncoded);

String urlEncoded = Base64.getUrlEncoder().encodeToString("subjects?abcd".getBytes("utf-8"));
System.out.println("Using URL Alphabet: " + urlEncoded);
The output will be:
Using Basic Alphabet: c3ViamVjdHM/YWJjZA==
Using URL Alphabet: c3ViamVjdHM_YWJjZA==

The sample above illustrates some content which if encoded using a basic encoder will result in a string containing a forward slash while when using a URL safe encoder the output will include an underscore instead (URL Safe encoding is described in clause 5 of the RFC)

MIME Encoding The MIME encoder generates a Base64 encoded output using the Basic Alphabet but in a MIME friendly format: each line of the output is no longer than 76 characters and ends with a carriage return followed by a linefeed (\r\n). The following example generates a block of text (this is needed just to make sure we have enough 'body' to encode into more than 76 characters) and encodes it using the MIME encoder:

StringBuilder sb = new StringBuilder();
for (int t = 0; t < 10; ++t) {
 sb.append(UUID.randomUUID().toString());
}

byte[] toEncode = sb.toString().getBytes("utf-8");
String mimeEncoded = Base64.getMimeEncoder().encodeToString(toEncode);
System.out.println(mimeEncoded);
The output:
NDU5ZTFkNDEtMDVlNy00MDFiLTk3YjgtMWRlMmRkMWEzMzc5YTJkZmEzY2YtM2Y2My00Y2Q4LTk5
ZmYtMTU1NzY0MWM5Zjk4ODA5ZjVjOGUtOGMxNi00ZmVjLTgyZjctNmVjYTU5MTAxZWUyNjQ1MjJj
NDMtYzA0MC00MjExLTk0NWMtYmFiZGRlNDk5OTZhMDMxZGE5ZTYtZWVhYS00OGFmLTlhMjgtMDM1
ZjAyY2QxNDUyOWZiMjI3NDctNmI3OC00YjgyLThiZGQtM2MyY2E3ZGNjYmIxOTQ1MDVkOGQtMzIz
Yi00MDg0LWE0ZmItYzkwMGEzNDUxZTIwOTllZTJiYjctMWI3MS00YmQzLTgyYjUtZGRmYmYxNDA4
Mjg3YTMxZjMxZmMtYTdmYy00YzMyLTkyNzktZTc2ZDc5ZWU4N2M5ZDU1NmQ4NWYtMDkwOC00YjIy
LWIwYWItMzJiYmZmM2M0OTBm




Wrapping All encoders and decoders created by the java.util.Base64 class support streams wrapping - this is a very elegant construct - both coding and efficiency wise (since no redundant buffering are needed) - to stream in and out buffers via encoders and decoders. The sample bellow illustrates how a FileOutputStream can be wrapped with an encoder and a FileInputStream is wrapped with a decoder - in both cases there is no need to buffer the content:

 public void wrapping() throws IOException {

   String src = "This is the content of any resource read from somewhere" +
   " into a stream. This can be text, image, video or any other stream.";

   // An encoder wraps an OutputStream. The content of /tmp/buff-base64.txt will be the
   // Base64 encoded form of src.
   try (OutputStream os = Base64.getEncoder().wrap(new FileOutputStream("/tmp/buff-base64.txt"))) {
     os.write(src.getBytes("utf-8"));
   }

   // The section bellow illustrates a wrapping of an InputStream and decoding it as the stream
   // is being consumed. There is no need to buffer the content of the file just for decoding it.
   try (InputStream is = Base64.getDecoder().wrap(new FileInputStream("/tmp/buff-base64.txt"))) {
     int len;
     byte[] bytes = new byte[100];
     while ((len = is.read(bytes)) != -1) {
       System.out.print(new String(bytes, 0, len, "utf-8"));
     }
   }
 }
Published at DZone with permission of Eyal Lupu, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

John Yeary replied on Wed, 2013/11/20 - 1:58pm

This is very nicely done. The examples are simple enough to follow, and demonstrate the ease of use the API provides.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.