Base64 encoding and decoding in Java 8

Java 8's new Base64 API could be just what you need to ensure data integrity in transit

1 2 Page 2
Page 2 of 2
  • byte[] decode(byte[] src): Decode all bytes in src to a newly-allocated byte array, which this method returns. IllegalArgumentException is thrown when src isn't valid Base64.
  • int decode(byte[] src, byte[] dst): Decode all bytes in src to dst (starting at offset 0). If dst isn't big enough to hold the decoding, or when src isn't valid Base64, IllegalArgumentException is thrown. Otherwise, the number of bytes written to dst is returned.
  • byte[] decode(String src): Decode all bytes in src to a newly-allocated byte array, which is returned. Invoking this method is equivalent to invoking decode(src.getBytes(StandardCharsets.ISO_8859_1)). IllegalArgumentException is thrown when src isn't valid Base64.
  • ByteBuffer decode(ByteBuffer buffer): Decode all bytes in buffer to a newly-allocated java.nio.ByteBuffer object. Upon return, buffer's position will be updated to its limit; its limit won't have been changed. The returned output buffer's position will be zero and its limit will be the number of resulting decoded bytes. IllegalArgumentException is thrown when src isn't valid Base64. In this case, buffer's position isn't updated.
  • InputStream wrap(InputStream is): Wrap an input stream for decoding byte data. The read() methods of the is object throw java.io.IOException when the input isn't valid Base64. Closing the returned output stream will close the underlying output stream.

Hello, Base64

Java's Base64 API is easy to use. Consider a "Hello, World"-style program that Base64-encodes a message using the Basic encoder and then Base64-decodes the encoded text with the Basic decoder. Listing 1 presents the source code.

Listing 1. HelloBase64.java

import java.util.Base64;

public class HelloBase64
{
   public static void main(String[] args)
   {
      String msg = "Hello, Base64!";
      Base64.Encoder enc = Base64.getEncoder();
      byte[] encbytes = enc.encode(msg.getBytes());
      for (int i = 0; i < encbytes.length; i++)
      {
         System.out.printf("%c", (char) encbytes[i]);
         if (i != 0 && i % 4 == 0)
            System.out.print(' ');
      }
      System.out.println();
      Base64.Decoder dec = Base64.getDecoder();
      byte[] decbytes = dec.decode(encbytes);
      System.out.println(new String(decbytes));
   }
}

Compile Listing 1 as follows:

javac HelloBase64.java

Run the resulting application as follows:

java HelloBase64

You should observe the following output:

SGVsb G8sI EJhc 2U2N CE=
Hello, Base64!

File encoding and decoding

Base64 is more useful for encoding files. I've created a second application that demonstrates this usefulness along with more of the Base64 API. Listing 2 presents the application's source code.

Listing 2. FileEncDec.java

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;

import java.util.Base64;

public class FileEncDec
{
   public static void main(String[] args)
   {
      if (args.length != 1)
      {
         System.err.println("usage: java FileEncDec filename");
         return;
      }
      try (FileInputStream fis = new FileInputStream(args[0]))
      {
         Base64.Encoder enc1 = Base64.getEncoder();
         Base64.Encoder enc2 = Base64.getMimeEncoder();
         Base64.Encoder enc3 = Base64.getUrlEncoder();
         OutputStream os1 = enc1.wrap(new FileOutputStream(args[0] + "1.enc"));
         OutputStream os2 = enc2.wrap(new FileOutputStream(args[0] + "2.enc"));
         OutputStream os3 = enc3.wrap(new FileOutputStream(args[0] + "3.enc"));
         int _byte;
         while ((_byte = fis.read()) != -1)
         {
            os1.write(_byte);
            os2.write(_byte);
            os3.write(_byte);
         }
         os1.close();
         os2.close();
         os3.close();
      }
      catch (IOException ioe)
      {
         System.err.printf("I/O error: %s%n", ioe.getMessage());
      }
      try (FileOutputStream fos1 = new FileOutputStream("1" + args[0]);
           FileOutputStream fos2 = new FileOutputStream("2" + args[0]);
           FileOutputStream fos3 = new FileOutputStream("3" + args[0]))
      {
         Base64.Decoder dec1 = Base64.getDecoder();
         Base64.Decoder dec2 = Base64.getMimeDecoder();
         Base64.Decoder dec3 = Base64.getUrlDecoder();
         InputStream is1 = dec1.wrap(new FileInputStream(args[0] + "1.enc"));
         InputStream is2 = dec2.wrap(new FileInputStream(args[0] + "2.enc"));
         InputStream is3 = dec3.wrap(new FileInputStream(args[0] + "3.enc"));
         int _byte;
         while ((_byte = is1.read()) != -1)
            fos1.write(_byte);
         while ((_byte = is2.read()) != -1)
            fos2.write(_byte);
         while ((_byte = is3.read()) != -1)
            fos3.write(_byte);
         is1.close();
         is2.close();
         is3.close();
      }
      catch (IOException ioe)
      {
         System.err.printf("I/O error: %s%n", ioe.getMessage());
      }
   }
}

The FileEncDec application requires the name of a file as its solitary command-line argument. It proceeds to open this file and read its contents. Each read byte is written to another file by way of a different encoder and a wrapped output stream. Later, these files are opened and read via different decoders and wrapped input streams. The results are stored in three separate files.

Compile Listing 2 as follows:

javac FileEncDec.java

Run the resulting application as follows (which assumes a JPEG file named image.jpg -- see the post's code archive):

java FileEncDec image.jpg

You should observe image.jpg1.enc, image.jpg2.enc, and image.jpg3.enc files in the current directory.

image.jpg1.enc stores the Basic encoding on one long line. Below is a prefix of the output, split across two lines for readability (the ... sequence indicates content not shown):

/9j/4AAQSkZJRgABAQEASABIAAD/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAEAAAAAAAD/
   4QM6aHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+...

image.jpg2.enc stores the MIME encoding across multiple lines:

/9j/4AAQSkZJRgABAQEASABIAAD/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAEAAAAAAAD/
4QM6aHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9
Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pg0KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpu
czptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEw
LzAyLzEyLTE3OjMyOjAwICAgICAgICAiPg0KCTxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3
dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+DQoJCTxyZGY6RGVzY3JpcHRpb24g
cmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1s
bnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJo
dHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRv
clRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzUgV2luZG93cyIgeG1wTU06SW5zdGFuY2VJRD0ieG1w
LmlpZDoyMzlCQTU3RjY3RDMxMUUzODg3OEFGOTg0RUUzMkVBOSIgeG1wTU06RG9jdW1lbnRJRD0i
eG1wLmRpZDoyMzlCQTU4MDY3RDMxMUUzODg3OEFGOTg0RUUzMkVBOSI+DQoJCQk8eG1wTU06RGVy
aXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDoyMzlCQTU3RDY3RDMxMUUzODg3OEFG
OTg0RUUzMkVBOSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDoyMzlCQTU3RTY3RDMxMUUzODg3
OEFGOTg0RUUzMkVBOSIvPg0KCQk8L3JkZjpEZXNjcmlwdGlvbj4NCgk8L3JkZjpSREY+DQo8L3g6
eG1wbWV0YT4NCjw/eHBhY2tldCBlbmQ9J3cnPz7/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYE
BAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYM
CAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wAAR
CAAeADADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgED
AwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRol
JicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWW
l5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3
+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3
AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5
OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaan
qKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIR
AxEAPwD4y1DwrZ3epxppsxS3cfeu+HUDO5yqAnbweEDkY6k11fw4+GWqeI9JhSVdBt9Ljma6Ek9r
FctNIiqTEzxgybSDGChYL+9XALMM/JegyRfEgSTb9bsWRDKRNEbhlD7WXaU5IYuMMABkkkdSOh8R
RSJcxx3etaha2cajCTJ5URXLFXljPzu3mbThlYZ6Z5A7KvFNFv2cZpPzvf8AJK/3k0chqW9pKLa8
rW/Vn3t8QvFPjb4x67pOm+NNQ0O60nTLb93YWV9Nplpdg4HkzSAFlCRFkHUgBc8FiPBde8IyWuma
pfXnhW6sYdPVXeaK6WO0hSRhHENzZ8xtxUfKxZuSc5BHz78NfjD4m8D+L9S1nSbfxprV5q0TLqd3
aRzNLewuypyXyZkO9sIE2rsU4GMjd+HXizVPhemq3Xh61+L1zHrbLpl3LcaVt8uzYoGkBKs8bqD8
skfzFN4Kxh+eT/Wilhnytp7bJ2v1Wj0b8/yR1f2DOurxut92vlvul5H0xofxB8Eaf8HZobj4c6dp
uqarp8unwa3DK97I43qXxHK5SKbB/wBafmCnCAKeOB8Pado91dTxpazpBJC6nMMV3M4xyFZ02xnq
fMVdy449R4V8X/E0PhT4kRtZ2Xju+tlhMUE0OoJHLDKjgMojKbGiMSbxlVGSoyONvK3H7Rnia90O
2l0OG8WeztVW5kJE7pN5SLvXAHLSuxbhjwNuxQRV4XiqlVp+0hCXvaq7Xyvdt+W2+yM8Vw/UpT5J
zj7ujsn+iS8/zZ+h91qGi3t7Lpsmi3CR+SLi3+y3kkIjTgZYBDhlG7bxkZPLFhWh47+EmgzeDdQ8
UeEdYbTdPUL9p0xtUuJyJtuxWEsbxnBOThwuArAsVznFstCaLxjpuk7bdYLi5+ybQhYIpcxEg8cj
jHsPUkn16Pwf/wAIHqL2d5cR6ppdx+7Wze2CQuihTyqsNh28gglg3RgBz+FYOpDBzi5RUodU0ndd
d1v1Wq1P0DETnibtVGpdNWrfd/wT520DRfEV1eyWt3cXek/2ZCF3Tt9str8RxKCVZozI0rKFwC6Y
L84GBUC+GfE2sQ6nNc3Gl6T/AGezSodT0mOX7RIxxGEEbqFkxuHqSVXJJBEn7YNvJ8Dvit9ttbi+
uNH1WITC3FwI7i33gSbFYJt2j7v3eRk4ycjwo/F6bTPOjT7bI0gJLtPy+75lDDoccE+9fquG4Syv
F044mnTSUkmrJrR+V/0PjqnE2OoN0ZTbs3e7PbdHbXtGvlSbS/Ccmm3Nq7/aLN73TWluFXhgD5q5
BZeuMbQAVANN8feJYfBuoLqFj4L1jUrW1t45ZpV8SIJbNiqyNuD5Dqm6TDA/KcggEYrF8M/FTQ9c
+HurfZLfV7jVNJ0sag320oIBH5yqyoUbcrfMT6HnIGa53Qvi9b+WGkspp47hWQo7jKj2YflyDkVn
/qPgJXlFyT23dvuTX5ov/WrGxsna3or/AHtM/9k=

image.jpg3.enc stores the URL and Filename Safe encoding on one long line. Below is a prefix of the output, split across two lines for readability:

_9j_4AAQSkZJRgABAQEASABIAAD_4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAEAAAAAAAD_
   4QM6aHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu-...

The difference between image.jpg1.enc and image.jpg3.enc is that each / has been replaced with an _ and each + has been replaced with a -.

You should also observe 1image.jpg, 2image.jpg, and 3image.jpg files in the current directory. Each of these files contains identical content to image.jpg.

Conclusion

The Base64 API is one of various small "gems" introduced by Java 8. You'll find this API very handy if you ever have to work with Base64. I encourage you to experiment with Base64, starting with those methods not covered in this post.

download
Get the source code for this post's applications. Created by Jeff Friesen for JavaWorld

The following software was used to develop the post's code:

  • 64-bit JDK 9ea+181

The post's code was tested on the following platform(s):

  • JVM on 64-bit Windows 10
1 2 Page 2
Page 2 of 2