{{announcement.body}}
{{announcement.title}}

Java Spring Boot Rest API to Upload/Download File on Server

DZone 's Guide to

Java Spring Boot Rest API to Upload/Download File on Server

This article demonstrates how to use a Spring Boot application and MySQL to maintain a database of uploaded files and their metadata.

· Integration Zone ·
Free Resource

I will be using Spring Initialzr to generate my Spring Boot application, include dependencies while creating a project. Also, I will be using the MySQL database to keep track of files uploaded and their metadata.

This API will be helpful for those who are registering new users to their business/application and want users to upload documents for verification.

Spring Boot dependencies

Spring Boot dependencies


Set up application.properties file with the database, upload the directory, and other details:

Properties files
 




x
24


 
1
spring.datasource.url = jdbc:mysql://127.0.0.1:8889/merchant
2
spring.datasource.username = root
3
spring.datasource.password = root
4
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
5
 
          
6
spring.jpa.hibernate.ddl-auto=update
7
spring.jpa.database-platform=org.hibernate.dialect.MySQL57Dialect
8
spring.jpa.generate-ddl=true
9
spring.jpa.show-sql=true
10
 
          
11
spring.application.name=media-service
12
server.port=8083
13
 
          
14
## MULTIPART (MultipartProperties)
15
# Enable multipart uploads
16
spring.servlet.multipart.enabled=true
17
# Threshold after which files are written to disk.
18
spring.servlet.multipart.file-size-threshold=2KB
19
# Max file size.
20
spring.servlet.multipart.max-file-size=200MB
21
# Max Request Size
22
spring.servlet.multipart.max-request-size=215MB
23
# All files uploaded through the REST API will be stored in this directory
24
file.upload-dir=/home/yogesh/media/upload


Create an entity class, DocumnentStorageProperties.java, to save information about a file uploaded and to avoid the duplication of files (you can skip this if you don't want to keep metadata).

Here I am keeping my database name as merchant and table name as merchant_documents. There's no need to create the table manually as I have set up spring.jpa.generate-ddl=true in properties. It will fire the DDL command when the application starts to create a table according to the Entity class defined in Java.

Java
 




x


1
@ConfigurationProperties(prefix = "file")
2
@Entity
3
@Table(name = "merchant_documents")
4
public class DocumnentStorageProperties {
5
    @Id
6
    @GeneratedValue(strategy = GenerationType.AUTO)
7
    @Column(name = "document_id")
8
    private Integer documentId;
9
    
10
    @Column(name = "user_id")
11
    private Integer UserId;
12
    
13
    @Column(name = "file_name")
14
    private String fileName;
15
    
16
    @Column(name = "document_type")
17
    private String documentType;
18
    
19
    @Column(name = "document_format")
20
    private String documentFormat;
21
    
22
    @Column(name = "upload_dir")
23
    private String uploadDir;
24
  // Getter and Setter


In the above entity class, the upload_dir variable will get initialized by the value which we have set in application.properties(file.upload-dir).

Create a repository interface for the above entity to get built-in CRUD operations method support. Add our own new method to check whether a particular file is present for that user or not.

Java
 




xxxxxxxxxx
1
14


1
import org.springframework.data.jpa.repository.JpaRepository;
2
import org.springframework.data.jpa.repository.Query;
3
import com.pocketutility.mediaservice.file.DocumnentStorageProperties;
4
 
          
5
public interface DocumentStoragePropertiesRepo extends JpaRepository<DocumnentStorageProperties, Integer> {
6
    
7
    @Query("Select a from DocumnentStorageProperties a where user_id = ?1 and document_type = ?2")
8
    DocumnentStorageProperties checkDocumentByUserId(Integer userId, String docType);
9
    
10
    @Query("Select fileName from DocumnentStorageProperties a where user_id = ?1 and document_type = ?2")
11
    String getUploadDocumnetPath(Integer userId, String docType);
12
}


Create a Service class to store and download files on the server, and to store information in the database. Here I have used a few more new classes which I will be explaining in a later section of this article, so if you get any compilation issues, ignore them for the moment.

Java
 




xxxxxxxxxx
1
96


 
1
import org.springframework.beans.factory.annotation.Autowired;
2
import org.springframework.core.io.Resource;
3
import org.springframework.core.io.UrlResource;
4
import org.springframework.stereotype.Service;
5
import org.springframework.util.StringUtils;
6
import org.springframework.web.multipart.MultipartFile;
7
import java.io.FileNotFoundException;
8
import java.io.IOException;
9
import java.net.MalformedURLException;
10
import java.nio.file.Files;
11
import java.nio.file.Path;
12
import java.nio.file.Paths;
13
import java.nio.file.StandardCopyOption;
14
 
          
15
@Service
16
public class DocumentStorageService {
17
 
          
18
    private final Path fileStorageLocation;
19
    
20
    @Autowired
21
    DocumentStoragePropertiesRepo docStorageRepo;
22
 
          
23
    @Autowired
24
    public DocumentStorageService(DocumnentStorageProperties fileStorageProperties) {
25
        this.fileStorageLocation = Paths.get(fileStorageProperties.getUploadDir())
26
                .toAbsolutePath().normalize();
27
 
          
28
        try {
29
            Files.createDirectories(this.fileStorageLocation);
30
        } catch (Exception ex) {
31
            throw new DocumentStorageException("Could not create the directory where the uploaded files will be stored.", ex);
32
        }
33
    }
34
 
          
35
    public String storeFile(MultipartFile file, Integer userId, String docType) {
36
        // Normalize file name
37
        String originalFileName = StringUtils.cleanPath(file.getOriginalFilename());
38
        String fileName = "";
39
 
          
40
        try {
41
            // Check if the file's name contains invalid characters
42
            if(originalFileName.contains("..")) {
43
                throw new DocumentStorageException("Sorry! Filename contains invalid path sequence " + originalFileName);
44
            }
45
 
          
46
            String fileExtension = "";
47
            try {
48
            fileExtension = originalFileName.substring(originalFileName.lastIndexOf("."));
49
            } catch(Exception e) {
50
                fileExtension = "";
51
            }
52
            fileName = userId + "_" + docType + fileExtension;
53
         // Copy file to the target location (Replacing existing file with the same name)
54
            Path targetLocation = this.fileStorageLocation.resolve(fileName);
55
            Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);
56
            
57
            DocumnentStorageProperties doc = docStorageRepo.checkDocumentByUserId(userId, docType);
58
            if(doc != null) {
59
                doc.setDocumentFormat(file.getContentType());
60
                doc.setFileName(fileName);
61
                docStorageRepo.save(doc);
62
                
63
            } else {
64
                DocumnentStorageProperties newDoc = new DocumnentStorageProperties();
65
                newDoc.setUserId(userId);
66
                newDoc.setDocumentFormat(file.getContentType());
67
                newDoc.setFileName(fileName);
68
                newDoc.setDocumentType(docType);
69
                docStorageRepo.save(newDoc);
70
            }
71
 
          
72
            return fileName;
73
        } catch (IOException ex) {
74
            throw new DocumentStorageException("Could not store file " + fileName + ". Please try again!", ex);
75
        }
76
    }
77
 
          
78
    public Resource loadFileAsResource(String fileName) throws Exception {
79
        try {
80
            Path filePath = this.fileStorageLocation.resolve(fileName).normalize();
81
            Resource resource = new UrlResource(filePath.toUri());
82
            if(resource.exists()) {
83
                return resource;
84
            } else {
85
                throw new FileNotFoundException("File not found " + fileName);
86
            }
87
        } catch (MalformedURLException ex) {
88
            throw new FileNotFoundException("File not found " + fileName);
89
        }
90
    }
91
 
          
92
    public String getDocumentName(Integer userId, String docType) {
93
        return docStorageRepo.getUploadDocumnetPath(userId, docType);
94
        
95
    }
96
}


There are two important methods in the Service class; one is  storeFilewhich will store the file at the required location and save the file details in the database. Before storing the file, we will check whether a user has already given the type of file available, and if it is available, then replace the latest file on a server and update the database with the latest information. Otherwise, it will create a new entry in the database and save the file at the required location. We will be keeping the file name as  <userId>_<docType>.<extension> just to avoid ambiguity in the file name.

The second method is  loadFileAsResource which returns the file as a Resource on the basis of file name given.

Also, we have an autowired repository object to this service which we have created in thr previous step and autowired  fileStorageLocation  on basis of upload-dir properties.

Now let's create a controller that will handle the HTTP request and send the proper response back.

Java
 




xxxxxxxxxx
1
71


 
1
import org.springframework.beans.factory.annotation.Autowired;
2
import org.springframework.core.io.Resource;
3
import org.springframework.http.HttpHeaders;
4
import org.springframework.http.MediaType;
5
import org.springframework.http.ResponseEntity;
6
import org.springframework.web.bind.annotation.*;
7
import org.springframework.web.multipart.MultipartFile;
8
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
9
import com.pocketutility.mediaservice.file.UploadFileResponse;
10
import com.pocketutility.mediaservice.service.DocumentStorageService;
11
import javax.servlet.http.HttpServletRequest;
12
import java.io.IOException;
13
import java.util.Arrays;
14
import java.util.List;
15
import java.util.stream.Collectors;
16
 
          
17
@RestController
18
public class DocumentController {
19
    
20
    @Autowired
21
    private DocumentStorageService documneStorageService;
22
    
23
    @PostMapping("/uploadFile")
24
    public UploadFileResponse uploadFile(@RequestParam("file") MultipartFile file, 
25
            @RequestParam("userId") Integer UserId,
26
            @RequestParam("docType") String docType) {
27
        String fileName = documneStorageService.storeFile(file, UserId, docType);
28
 
          
29
        String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
30
                .path("/downloadFile/")
31
                .path(fileName)
32
                .toUriString();
33
 
          
34
        return new UploadFileResponse(fileName, fileDownloadUri,
35
                file.getContentType(), file.getSize());
36
    }
37
    @GetMapping("/downloadFile")
38
    public ResponseEntity<Resource> downloadFile(@RequestParam("userId") Integer userId,
39
            @RequestParam("docType") String docType,
40
            HttpServletRequest request) {
41
        
42
        String fileName = documneStorageService.getDocumentName(userId, docType);
43
        Resource resource = null;
44
        if(fileName !=null && !fileName.isEmpty()) {
45
        try {
46
            resource = documneStorageService.loadFileAsResource(fileName);
47
        } catch (Exception e) {
48
            e.printStackTrace();
49
        }
50
        // Try to determine file's content type
51
        String contentType = null;
52
        try {
53
            contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
54
        } catch (IOException ex) {
55
            //logger.info("Could not determine file type.");
56
        }
57
        // Fallback to the default content type if type could not be determined
58
        if(contentType == null) {
59
            contentType = "application/octet-stream";
60
        }
61
 
          
62
        return ResponseEntity.ok()
63
                .contentType(MediaType.parseMediaType(contentType))
64
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
65
                .body(resource);
66
    } else {
67
        return ResponseEntity.notFound().build();
68
    }
69
        
70
    }   
71
}


So the above controller has two mappings:

  1. For uploading file
    • Request URL: /uploadFile
    • Request Parameters: Actual file, userId, docType
    • Response: Will return JSON having file information(Shown in last part of this article)
  2.  For downloading file
    • Request URL: /downloadFile
    • Request Parameters: userId and docType
    • Response: Will return the file in attachment along with content-type and other details. (If file not found for that user it will return 404 Not found code)

Other Classes which we have used are:

 UploadFileResponse: To send the response back when the upload is successful.

Java
 




xxxxxxxxxx
1
13


1
public class UploadFileResponse {
2
    private String fileName;
3
    private String fileDownloadUri;
4
    private String fileType;
5
    private long size;
6
 
          
7
    public UploadFileResponse(String fileName, String fileDownloadUri, String fileType, long size) {
8
        this.fileName = fileName;
9
        this.fileDownloadUri = fileDownloadUri;
10
        this.fileType = fileType;
11
        this.size = size;
12
    }
13
// Getter and Setter


 DocumentStorageException: Our own Custom exception type

Java
 




xxxxxxxxxx
1


 
1
public class DocumentStorageException extends RuntimeException {
2
    public DocumentStorageException(String message) {
3
        super(message);
4
    }
5
 
          
6
    public DocumentStorageException(String message, Throwable cause) {
7
        super(message, cause);
8
    }
9
}


In order to secure your API with Spring Basic Auth add below the class. Update the required user name and password (use online Bcrypt encoder to encode your password).

Java
 




x


1
import javax.sql.DataSource;
2
import org.springframework.beans.factory.annotation.Autowired;
3
import org.springframework.context.annotation.Bean;
4
import org.springframework.context.annotation.Configuration;
5
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
6
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
7
import org.springframework.security.config.annotation.web.builders.WebSecurity;
8
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
9
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
10
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
11
import org.springframework.security.crypto.password.PasswordEncoder;
12
import org.springframework.security.provisioning.JdbcUserDetailsManager;
13
 
          
14
@Configuration
15
@EnableWebSecurity
16
public class ApiSecurity extends WebSecurityConfigurerAdapter {
17
 
          
18
    @Bean
19
    public BCryptPasswordEncoder encoder() {
20
        return new BCryptPasswordEncoder();
21
    }
22
 
          
23
        @Override
24
        protected void configure(HttpSecurity http) throws Exception {
25
            http.csrf().disable();
26
            http.authorizeRequests().anyRequest().fullyAuthenticated().and()
27
             .httpBasic();
28
        }
29
        @Autowired
30
         public void configureGlobal(AuthenticationManagerBuilder authenticationMgr)
31
         throws Exception {
32
         authenticationMgr.inMemoryAuthentication().withUser("username")
33
            .password("$6y$13$9OzrtK7X4mad/te8m7uhysjfaihjsdfZdJ/fDZFaOJjrfFbYzYtzky").roles("ADMIN");
34
         }
35
    }
36
 
          


Testing using Postman:

Post request to upload a file:

Request to upload a file

Request to upload a file


GET request to download the file:

Request to dowload file

Request to dowload file


Database screenshot:

Database screenshot

Database screenshot


Verify that file is visible at the required location.

Topics:
database, file download, file upload, java, metadata, mysql, rest api, spring basic authentication example, spring boot

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}