Before I start this blog post, just a caveat that this post will be different than other posts. I started this series of blog posts so that I can document few things that I try out or want to remember. However, today it will be a post that documents a process that needs to be implemented by my team, and is really a reference to them. With that out of the way, let’s start with the blog post now.
What is Base64?
So, what is Base64 encoding and why is it needed? Base64 came from the need for transferring binary data across systems. There is a lot of different systems across the world and they handle binary data differently. Base64 converts this data in safe readable characters in 64 ASCII characters. Base64 just means that it uses 6 bit binary sequence between 0 and 63 to encode all data. That is mathematically called Base64. This standard is defined in RFC4648.
RFC defines a BASE64 conversion table as follows.
Value Encoding Value Encoding Value Encoding Value Encoding
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 +
12 M 29 d 46 u 63 /
13 N 30 e 47 v
14 O 31 f 48 w (pad) =
15 P 32 g 49 x
16 Q 33 h 50 y
Let’s see how we convert something to Base64 text. We will take the string HELLO.
The first step is to convert it to binary.
01001000 01000101 01001100 01001100 01001111
Next we would distribute it in 6 bit sequences. We pad two zeroes at the end as we have to make it to 24bit number. Next we just get the decimal representation for them.
010010 000100 010101 001100 010011 000100 111100
18 04 21 12 19 04 60
Using the table for converting Base64, let’s see what we get. Since we also added one padding, we will add a = at the end.
S E V M T E 8 =
This is the final Base64 representation. If you notice, Base64 text is always larger in size than the original version. Now that we know what Base64 is, let us move to the project at hand.
The Goal
We are getting a JSON object from a different team that contains a PDF file as a Base64 string. The goal is to extract this PDF, decode it to bytes and send back to the browser. Of course, we have a side goal too. We have to display this file on supported browsers.
For this project, we will define our own structures and will also just display JPEGs. We will build this project using Spring Boot for convenience. We will mostly ignore error checking.
Creating the Project
For this project, I have not used a spring initializer, but just created a Maven project and added spring web dependency as follows.
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.7.7</version> </dependency> </dependencies>
Also to build a uber jar, I have added spring maven plugin.
<plugins <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.6.0</version> <configuration> <mainClass>com.suturf.demo.App</mainClass> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.7.14</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> <configuration> <classifier>spring-boot</classifier> <mainClass>com.suturf.demo.App</mainClass> </configuration> </execution> </executions> </plugin> </plugins>
That out of the way, we will start building the project now.
The Beans
Let’s create one bean to start with. We will use this as a replacement for the bean that we mentioned in the goals for the project.
public class MediaResponse { private boolean status; private String message; private String mimeType; private String mediaContent; }
We will keep the Base64 string in mediaContent. Also, we keep the type of file in mimeType. Since we are building all JPEG only project, we can hard code this to the correct mime type for the file.
Utility Services
Next we will create a utility service to convert between Base64 and Binary objects. We will be able to use this later to get the JSON file and later the image file.
import java.util.Base64; import com.suturf.demo.beans.MediaResponse; import java.io.File; import java.io.IOException; import java.nio.file.Files; /** * Base64 encoder/ decoder (small files for test) * Loads full file in memory in one go */ public class MediaEncoder { public MediaResponse encodeMedia(final File file, final String mimeType) { final MediaResponse resp = new MediaResponse(); try { final byte[] content = Files.readAllBytes(file.toPath()); resp.setStatus(true); resp.setMessage("Success"); resp.setMimeType(mimeType); resp.setMediaContent(Base64.getEncoder().encodeToString(content)); } catch (IOException se) { se.printStackTrace(); resp.setStatus(false); resp.setMessage(se.getMessage()); } return resp; } public byte[] decodeMedia(final String content) { final byte[] media = Base64.getDecoder().decode(content); return media; } }
The interesting thing to note here is on line 22 and 33. On line 22, we are encoding a binary data to Base64. On the other hand, on line 33, we are converting it back to binary.
Controllers
We will build couple of controllers. The first one will generate a JSON object given a file. The second one will take the output given by the first one and generate an image.
Let’s check the first controller.
@GetMapping("/base64") public ResponseEntity<MediaResponse> loadFileAsString( @RequestParam("file") final String fileName) { final FileLister lster = new FileLister(); final String fullName = lster.getFullPath(fileName); final MediaEncoder encoder = new MediaEncoder(); final MediaResponse media = encoder.encodeMedia( new File(fullName), "image/jpg"); return ResponseEntity.ok().body(media); }
Since we have no real way of getting the JSON files, we use this method to generate the JSON object with Base64 embedded. A sample run will return values like below.
{ "status": true, "message": "Success", "mimeType": "image/jpg", "mediaContent": "/9j/4AAQSkZJRgABA....." }
Now that we have the JSON in place, we will create one more controller to return an image (given a JSON string).
@RequestMapping(value = "/image4base64", method = RequestMethod.POST, consumes="application/json") public ResponseEntity<byte[]> showBase64File( @RequestBody final MediaResponse b64img) { try { final MediaEncoder enc = new MediaEncoder(); final byte[] content = enc.decodeMedia(b64img.getMediaContent()); return ResponseEntity.ok() .contentType(MediaType.IMAGE_JPEG) .body(content); } catch (Exception ioe) { return ResponseEntity.ok().body("Error".getBytes()); } }
The first thing to note is that it is returning a byte array. We are also setting the content type for the media that we are sending. Since this is a POST call, we can use Insomnia or Postman to run this.
Main Application
We will also add the main application just for completion. This is just a simple Spring Boot application startup.
@SpringBootApplication public class App { public static void main( String[] args ) { SpringApplication.run(App.class, args); } }
That’s all there to it. This program will read a JSON object with embedded Base64 image and send it back to the browser.
Ciao for now!