Skip to content

Upload Download Files#

Introduction#

  • In this topic we will learn about  upload and download files to/from database with a Spring Boot Rest APIs. We also use Spring Web MultipartFile interface to handle HTTP multi-part requests.

Basic Example With Jpa And Postgres#

  • Our Spring Boot Application will provide APIs for:

    • uploading File to PostgreSQL/MySQL database
    • downloading File database with the link
    • getting list of Files’ information (file name, url, type, size)
  • These are APIs to be exported:

Methods Urls Actions
POST /v1/files upload a File
GET /v1/files get List of Files (name, url, type, size)
GET /v1/files/{id} download a File by fileId
  • The uploaded files will be stored in PostgreSQL Database files table with these fields as in the entity below.
FileDB
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.springboot.project.upload.download.files.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

import java.util.Date;
import java.util.UUID;

@Getter
@Setter
@Entity
@Table(name = "files")
public class FileDB {

    @Id
    @GeneratedValue(generator = "uuid")
    private UUID id;

    private String fileName;
    private String fileType;
    private String fileExtension;
    private byte[] fileData;
    private Date createdDate;
    private Date updatedDate;

    @PrePersist
    private void onCreate() {
        Date now = new Date();
        this.createdDate = now;
        this.updatedDate = now;
    }

    @PreUpdate
    private void onUpdate() {
        this.updatedDate = new Date();
    }

}

Dependency#

  • Firstly, let's add some dependencies as below for Spring Boot, Spring Data Jpa, Postgresql and Tika. Apache Tika is a toolkit for detecting and extracting metadata and structured text content from various documents using existing parser libraries.
pom.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
 <dependencies>

    <!-- spring boot 3 web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>3.1.3</version>
    </dependency>

    <!-- spring boot 3 test -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <version>3.1.3</version>
        <scope>test</scope>
    </dependency>

    <!-- spring boot 3 data jpa -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
        <version>3.1.3</version>
    </dependency>

    <!-- postgresql driver -->
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.6.0</version>
    </dependency>

    <dependency>
        <groupId>org.apache.tika</groupId>
        <artifactId>tika-core</artifactId>
        <version>2.9.0</version>
    </dependency>

    .....
</dependencies>

Entity#

  • Then let's define a entity which is FileDB which contains fields as below.
FileDB
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.springboot.project.upload.download.files.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

import java.util.Date;
import java.util.UUID;

@Getter
@Setter
@Entity
@Table(name = "files")
public class FileDB {

    @Id
    @GeneratedValue(generator = "uuid")
    private UUID id;

    private String fileName;
    private String fileType;
    private String fileExtension;
    private byte[] fileData;
    private Date createdDate;
    private Date updatedDate;

    @PrePersist
    private void onCreate() {
        Date now = new Date();
        this.createdDate = now;
        this.updatedDate = now;
    }

    @PreUpdate
    private void onUpdate() {
        this.updatedDate = new Date();
    }

}

Repository#

  • Next, lets create a repository for the entity above.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package com.springboot.project.upload.download.files.repository;

import com.springboot.project.upload.download.files.entity.FileDB;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.UUID;

@Repository
public interface FileDBRepository extends JpaRepository<FileDB, UUID> {
}

Model#

  • Then let's create some DTOs for using in Controller and Service as below.
FileDto.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package com.springboot.project.upload.download.files.model;

import lombok.Getter;
import lombok.Setter;

import java.util.Date;
import java.util.UUID;

@Getter
@Setter
public class FileDto {

    private UUID id;
    private String fileName;
    private String fileType;
    private String fileExtension;
    private Date createdDate;
    private Date updatedDate;
    private String location;

}
FileContentDto.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.springboot.project.upload.download.files.model;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class FileContentDto {

    private String fileName;
    private byte[] content;

}

Config#

  • Let's create an ApplicationProperty.java config as below which will load the configuration for Spring Boot server url and api path for downloading file. We will use these information for generating download file url.
ApplicationProperty.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package com.springboot.project.upload.download.files.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "application")
public class ApplicationProperty {

    private String serverBaseUrl;
    private String getFileApi;

}
  • Now, let's add configuration for our application.yaml as below.
application.yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# config datasource for Jpa and Jdbc

application:
  server-base-url: "http://localhost:8080"
  get-file-api: "/v1/files/"

spring:
  servlet:
    multipart:
      max-file-size: 500KB
      max-request-size: 500KB
  datasource:
    driverClassName: org.postgresql.Driver
    url: jdbc:postgresql://localhost:5432/storage?useUnicode=true&characterEncoding=UTF-8
    username: root
    password: password
  jpa:
    database-platform: org.hibernate.dialect.PostgreSQLDialect
    hibernate.ddl-auto: update
    generate-ddl: true
    show-sql: true
    open-in-view: true
    properties:
      hibernate:
        generate_statistics: true

Service#

  • Now let's create a Service and export 3 methods for saveFile, getFileDate and getFilesInformation as below.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package com.springboot.project.upload.download.files.service;

import com.springboot.project.upload.download.files.config.ApplicationProperty;
import com.springboot.project.upload.download.files.entity.FileDB;
import com.springboot.project.upload.download.files.model.FileContentDto;
import com.springboot.project.upload.download.files.model.FileDto;
import com.springboot.project.upload.download.files.repository.FileDBRepository;
import lombok.AllArgsConstructor;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor(onConstructor = @__(@Autowired))
public class FileStorageService {

    private final FileDBRepository fileDBRepository;
    private final ApplicationProperty applicationProperty;

    public FileDto saveFile(MultipartFile uploadFile) {
        if (Objects.isNull(uploadFile.getOriginalFilename())) {
            throw new IllegalArgumentException("FileName can not be null");
        }
        String fileName = StringUtils.cleanPath(uploadFile.getOriginalFilename());
        FileDB fileDB = new FileDB();
        fileDB.setFileName(fileName);
        fileDB.setFileExtension(FilenameUtils.getExtension(fileName));
        fileDB.setFileType(uploadFile.getContentType());
        try {
            fileDB.setFileData(uploadFile.getBytes());
        } catch (IOException e) {
            throw new RuntimeException("Can not map File Content");
        }
        return this.mapToFileDto(this.fileDBRepository.save(fileDB));
    }

    public FileContentDto getFileData(UUID id) {
        Optional<FileDB> fileDB = this.fileDBRepository.findById(id);
        if (fileDB.isPresent()) {
            return this.mapToFileContentDto(fileDB.get());
        }
        throw new RuntimeException("File Not Found");
    }

    public List<FileDto> getFilesInformation() {
        List<FileDB> fileDBs = this.fileDBRepository.findAll();
        return fileDBs.stream().map(this::mapToFileDto).collect(Collectors.toList());
    }

    private FileDto mapToFileDto(FileDB fileDB) {
        FileDto fileDto = new FileDto();
        fileDto.setId(fileDB.getId());
        fileDto.setFileName(fileDB.getFileName());
        fileDto.setFileType(fileDB.getFileType());
        fileDto.setCreatedDate(fileDB.getCreatedDate());
        fileDto.setUpdatedDate(fileDB.getUpdatedDate());
        fileDto.setFileExtension(fileDB.getFileExtension());
        fileDto.setLocation(this.buildLocationUrl(fileDB.getId()));
        return fileDto;
    }

    private FileContentDto mapToFileContentDto(FileDB fileDB) {
        FileContentDto fileContentDto = new FileContentDto();
        fileContentDto.setFileName(fileDB.getFileName());
        fileContentDto.setContent(fileDB.getFileData());
        return fileContentDto;
    }

    private String buildLocationUrl(UUID fileId) {
        return this.applicationProperty.getServerBaseUrl()
                + this.applicationProperty.getGetFileApi()
                + fileId;
    }

}
  • saveFile method saves a file to the database. It checks for a non-null original filename, cleans the filename using StringUtils.cleanPath, and creates a FileDB entity to store information such as filename, file extension, content type, and file data.
  • getFileData method retrieves the content of a file by its unique identifier (UUID). It returns a FileContentDto containing the filename and file content.
  • getFilesInformation method retrieves information about all stored files in the database. It returns a list of FileDto objects containing details such as file ID, filename, file type, creation date, update date, file extension, and file location.
  • Helper Methods:
    • mapToFileDto: Maps a FileDB entity to a FileDto.
    • mapToFileContentDto: Maps a FileDB entity to a FileContentDto.
    • buildLocationUrl: Builds the URL for accessing a file based on its ID.

Controller#

  • Finally, let's create a controller for exporting apis as we mentioned in the beginning.
UploadDownloadFileController.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.springboot.project.upload.download.files.controller;

import com.springboot.project.upload.download.files.model.FileContentDto;
import com.springboot.project.upload.download.files.model.FileDto;
import com.springboot.project.upload.download.files.service.FileStorageService;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;
import java.util.UUID;

@CrossOrigin("*")
@AllArgsConstructor(onConstructor = @__(@Autowired))
@RestController
public class UploadDownloadFileController {

    private final FileStorageService fileStorageService;

    @RequestMapping(method = RequestMethod.POST, path = "/v1/files", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<FileDto> uploadFile(@RequestParam("file") MultipartFile uploadFile) {
        return ResponseEntity.status(HttpStatus.CREATED).body(this.fileStorageService.saveFile(uploadFile));
    }

    @RequestMapping(method = RequestMethod.GET, path = "/v1/files/{id}", produces = {MediaType.APPLICATION_OCTET_STREAM_VALUE})
    public ResponseEntity<byte[]> downloadFile(@PathVariable("id") UUID id) {
        FileContentDto fileContentDto = this.fileStorageService.getFileData(id);
        return ResponseEntity.status(HttpStatus.OK)
                .header(HttpHeaders.CONTENT_DISPOSITION,"attachment; filename=\"" + fileContentDto.getFileName() + "\"")
                .body(fileContentDto.getContent());
    }

    @RequestMapping(method = RequestMethod.GET, path = "/v1/files", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<FileDto>> getFilesInformation() {
        return ResponseEntity.status(HttpStatus.OK).body(this.fileStorageService.getFilesInformation());
    }

}
  • uploadFile endpoint handles the file upload operation. It takes a MultipartFile as a request parameter and returns a FileDto in the response body. The file is saved using the fileStorageService.saveFile method.

  • downloadFile endpoint handles the file download operation. It takes the file ID as a path variable and returns a ResponseEntity containing the file content as bytes. The Content-Disposition header is set to indicate that the response should be treated as an attachment.

  • getFilesInformation endpoint retrieves information about all stored files and returns a list of FileDto objects in the response body.

  • Helper Methods:

    • There are no explicit helper methods in the controller. The logic for file storage operations is delegated to the FileStorageService class.

Testing#

  • Now, let's start our Spring Boot service and use Postman to test exported apis as below.

  • For calling upload file api with postman let's choose body with form-data. Then put the key and select the type is File then we can click on the value column and choose New file from local machine and you can select a file from your local machine.

 #zoom

  • After the call is successful we can see the response contains some information and the link for getting our file.
  • Next, let's copy this link and put it on any browser then you can download it immediately.

 #zoom

 #zoom

  • Finally, for getFilesInformation api, we will have the result as below.

 #zoom

References#