From Image to Bytes (and Back Again)

Malina Tran
Tech and the City
Published in
3 min readOct 20, 2016

After a little over a month of immersion in the Scala ecosystem, I have returned to Java and my HTTP server project. In an effort to better understand how servers communicate with clients, I figured I’d document my learning process. One of the stories I’m working on this week is figuring out how to render images in the browser by storing the contents of an image file in the body of a response, given a request from a client.

With text files, storing the content in the body is relatively straightforward since we’re dealing with strings. But with images, it’s trickier since the images must be rendered in the browser without being stored or saved. The best way to accomplish this is by converting images to a sequence of byte arrays.

1. Read the image file and store as a BufferedImage

BufferedImage image = ImageIO.read(new File("helloworld.png"));

Reading an image file in Java requires calling ImageIO.read and passing in a file. Creating aBufferedImage object is considered to be very efficient: it manages the image in-memory and provides methods for storing, interpreting, and obtaining pixel data. BufferedImage extends both theImage class (which represents graphical images as rectangular arrays of pixels) and data buffer (physical memory storage used to temporarily store data while it is being moved from one place to another). What an aptly named subclass.

2. Save the BufferedImage object

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(image, "png", outputStream);

The code snippet above demonstrates how to use the Image I/O class to save images, passing in the BufferedImage object, the image file type, and a ByteArrayOutputStream. The ByteArrayOutputStream class stream creates a buffer in memory and all the data sent to the stream is stored in the buffer. The constructor new ByteArrayOutputStream() creates a ByteArrayOutputStream having buffer of 32 byte (you can pass in an integer for a specific size).

3. Create a byte array and encode with Base64

String encodedImage = Base64.encode(outputStream.toByteArray());

Calling the toByteArray method to the output stream creates a newly allocated byte array (I won’t pretend to know what this is, beyond being an array of bytes). The size of the output stream and the contents of the buffer will be copied into the byte array, which will be the return type.

Rather than save this as binary data, which was exactly what I expected to do, Base64 encoding (converting binary to text) allows us to embed images in our code base. Interestingly, Base64 makes file sizes about 33% larger than their original binary representations.

Source: Wikipedia

I just wanted to take some time to geek out about Base64 which I think is conceptually hard to grasp, but is more accessible with an example. Wikipedia to the rescue! In the chart above, you can see that the [1] text “Man” becomes converted to [5] “TWFu.” [2] The individual characters are encoded in ASCII and stored as bytes 77, 97, and 110, which are [3] 8-bit binary values 01001101, 01100001, and 01101110. These three values are joined together into a 24-bit string, producing 010011010110000101101110. [4] Groups of 6 bits are converted into individual numbers, which are then converted into [5] their corresponding Base64 character values.

4. Wrap the encoded string in an HTML image tag

<img src="data:image/png;base64,[ENCODED STRING GOES HERE]">

Finally, the last step is to store the resulting Base64 encoded string in an HTML img tag. And when sending it with the response body, you need to also set the Content-Type as HTML (rather than an image type) since it will automatically be read as a string.

--

--