Upload files in Spring Boot

Kshitij Bajracharya
7 min readJul 29, 2019

--

Image Source: https://www.lifewire.com/what-is-a-system-file-2626015

A web application may be required to give options to its users to upload different types of files. But some of these files may be sensitive and confidential (like the scanned copy of your passport or driving license) and should never be stored in plain. What if the server is breached? The intruder can access all the documents that are stored and use it as per their wish, possibly harming the owner of the document.

In this series of articles, I’ll be trying to create a basic application using Spring Boot to upload secured files on to the server, that can only be accessed from the application. Although I make no claims that this is the best possible method for file security, this should give a general idea on how things are done.

Note: This is the first article of a three-part series. In this article, I’ll be setting up the model, the api and service to upload documents. This article does not cover the security aspects, nor does it upload the actual file. That will be covered in the subsequent articles.

Articles in this series:

  1. Upload files in Spring Boot (this)
  2. Saving Files to System in Spring Boot

For experienced developers, some of the content might seem unnecessary. Please bare with me as I’m trying to explain things so that audiences of all level can understand this. Please feel free to skim over if things are too easy for you.

Alright. First of all, let’s create a new Spring Boot project. We’ll call it secretfile.

We won’t be using a lot of dependencies here. Since this is a web application, we’ll need to select the Spring Web Starter; we’ll use the Spring Data JPA to persist our data and we’ll use H2 Database as our underlying database. You could easily replace H2 with your favorite database (MySQL, PostgreSQL or others). To keep things simple and avoid details about database configuration, here we’ll be using H2.

Alright. Go ahead and click on finish. Your project is now ready to be run. Easy Peasy Japanesey.

Well, it runs but if you check localhost:8080 at this point, you’ll just get an error page, and that’s not very nice to see. Let’s add some meaningful code to it. Since our ultimate goal is to save files, let’s go ahead and add a model — Document that gives a blueprint for our files. First of all, let’s create a package for it and name it com.example.secretfile.model. Create this under src/main/java.

Next, we create a class in this package called Document.

We’ll just add a name field for now to give a name for our document. Add the getters and setters (could easily be generated from STS). Yes, you could use Lombok to handle those, but let’s keep things simple. Finally, we need to add @Entity annotation to this class to let Spring know that we want to persist this class into a table. Our new class Document now looks like this.

Not much in it right now, but this works. Let’s go ahead and run our application.

Bummer! Our code doesn’t run. Let’s look why. The console log says no identifier specified for entity. Of course. Let’s add an identifier to our entity.

@Id will tell Spring that this is the primary key. @GeneratedValue defines how to increment this primary key for every row in the table. Read more about identifiers here. Finally, in @Column, we define that we don’t want to update the primary key and we always want a value for it. Since this is auto-generated and we don’t want to update it, we just want a getter for the id column.

Alright. Time for some action. Run the application and there should be no problem. But wait, how do we know the table is created or not? Where do we check that?

Open application.properties under src/main/resources and add the following code.

We now have our datasource url and driver classes defined. We also enabled h2 console and defined the path at /h2-console. Great! Run the application and head over to localhost:8080/h2-console. You should see the following in your browser.

Click on connect and there you have it.

The table is created and the columns are defined. But this empty table doesn’t look good. Let’s add some data to it. To do that, we would need to expose an api to add documents. But before that, let’s add some more columns to this table, so that we have more information about the documents we add.

First is mimeType, which represents the type of the file that we upload (jpeg, png, pdf, etc.). Next we have size, which gives the size in bytes. Finally, there’s hash. We’ll see why we need hash later on. For now, let’s just say it is a unique identifier for each document. So, if you upload the same document twice, they may have the same name, but they will have different hash. Why? The answer is in the setter method setHash. It takes the name, mimeType and size of the document, plus the current time. If a document is uploaded twice, the name, mimeType and the size are the same, but the time differs so the transformedName becomes different. Next, the transformedName is hashed using MD5. You can read more about MD5 and hashing here.

Our model is now ready. Now, let’s define that REST api that will enable us to add the documents. Create DocumentController.java under a new package com.example.secretfile.controller and add the following code.

We have a POST method that consumes multipart/form-data. The parameter contains multipartFiles that will be used by the service — DocumentService. We have injected this service using @Autowired but we have not yet defined it. Let’s do that next.

Create an interface DocumentService.java under com.example.secretfile.service. Define a method addDocuments that accepts an array of MultipartFile.

But what good is an interface without an implementation? Create DocumentServiceImpl.java under com.example.secretfile.service.impl.

The implementation of addDocuments doesn’t do much. It just loops through the array and calls the create method on each MultipartFile. The create method does all the work of creating a new document and setting the details. The setHash method from our model generates the hash for this document. Finally, the document is saved. Once again, we have autowired DocumentRepository which we will define next.

This repository extends the JpaRepository which provides us with the save method that we used in the service implementation. Add the required exceptions in the service interface and the controller, and we’re good to go.

Run the application. Open up an api client like Postman and then send a POST request to the api we just created.

Remember we named our request param as documents in the controller? That’s what we need to send now. Add as many files as you like, just make sure that all of the keys are named documents and the type chosen is File. Here, I’ve chosen two files — a jpeg and a csv. POSTing this gives us the 200 OK status, but there is no response because our return type was void.

Go the H2 console and check.

And we’ll see that our documents were successfully added. Nice work. But wait, these are just the information about the files. Where’s the actual physical file? It’s nowhere. Because we never really uploaded it in our file system. And that’s what we need to do next. But this article is getting too long, so we’ll continue this in Part 2. You can find the complete code in the link below:

--

--