Hello everybody,

Greetings of the day! 

Today we will explore how to validate file types in Spring Boot Rest API using custom annotations. Therefore, we create a custom annotation that can be used when file types need to be validated throughout the application.

Before you start, if you have questions about uploading JSON and multipart files in a single REST API call, check out the link below.

Spring Boot REST API File Upload With JSON Data In Single API Call

So let's begin.

First, create a basic project structure for your project by visiting the Spring Initializr and adding web, validation, and Lombok dependencies.


Now unzip your project and import it into an IDE.

We need a file extension to validate the file type and to get a file extension we will use Apache Commons, so let's add a dependency for that.

<dependency>
	<groupId>commons-io</groupId>
	<artifactId>commons-io</artifactId>
	<version>2.11.0</version>
</dependency>
The next step is to build an enum FILE_TYPE that will be used to specify valid file types.
package com.learning.file.mgt.enums;

public enum FILE_TYPE {

    PDF,
    PNG;
}

Next, let's create a custom @FileType annotation that can take two input values ​​AllowedFileTypes, an array of FILE_TYPE, and a message.

Example 

@FileType(allowedFileTypes = {FILE_TYPE.PDF},message = "Please enter valid file format")

We also need to create a FileTypeValidator class that will implement ConstraintValidator in which we will add code to validate the file extension.

Here is the code for the creation of the FileType annotation & FileTypeValidator.

FileType.java

package com.learning.file.mgt.validation;

import com.learning.file.mgt.enums.FILE_TYPE;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

@Target({ElementType.FIELD,ElementType.METHOD
        ,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = FileTypeValidator.class)
@Documented
public @interface FileType {

    Class<? extends Payload> [] payload() default{};
    Class<?>[] groups() default {};

    String message() default "Invalid file type.";

    FILE_TYPE[] allowedFileTypes();
}

FileTypeValidator.java

  
package com.learning.file.mgt.validation;

import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class FileTypeValidator implements
     ConstraintValidator<FileType, MultipartFile> {

    private List<String> validFileTypes;

    @Override
    public void initialize(FileType fileType) {
        validFileTypes=new ArrayList<>();
        Arrays.stream(fileType.allowedFileTypes())
                .forEach(file_type -> validFileTypes
                        .add(file_type.name()));
    }

    @Override
    public boolean isValid(MultipartFile multipartFile,
         ConstraintValidatorContext constraintValidatorContext){
        String extension = FilenameUtils.getExtension
                (multipartFile.getOriginalFilename())
                .toUpperCase();
        return validFileTypes.contains(extension);
    }
}

Next, let's create ResponseDTO.java to create and send API responses.

package com.learning.file.mgt.dto;

import lombok.*;

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ResponseDTO<T> {

    private String message;

    private T response;

}

Every time we get an invalid file in request @FileType annotation will throw BindException so we need to handle and display the appropriate response to the end user.

For that let's create GlobalExceptionHandler.java using @ControllerAdvice.

  
package com.learning.file.mgt.controlleradvice;

import com.learning.file.mgt.dto.ResponseDTO;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import java.util.HashMap;
import java.util.Map;

@ControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(BindException.class)
    public ResponseEntity<ResponseDTO<Object>> handleBindException
            (BindException bindException){

        Map<String,String> errorMap=new HashMap<>();

        bindException.getAllErrors().stream()
                .forEach(objectError -> {
            errorMap.put(((FieldError)objectError).getField()
                    ,objectError.getDefaultMessage());
        });

        return new ResponseEntity(
                ResponseDTO.builder().
                        response(errorMap.toString())
                        .message("Object validation failed")
                        .build(), HttpStatus.BAD_REQUEST);
    }
}

Now we will create FileUploadDTO.java which will be used as Request Body. Here on MultipartFile, we specify allowedFileTypes as pdf under @FileType annotation.

  
package com.learning.file.mgt.dto;

import com.learning.file.mgt.enums.FILE_TYPE;
import com.learning.file.mgt.validation.FileType;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.constraints.NotBlank;


@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class FileUploadDTO {

    @NotBlank
    private String fileDescription;

    @FileType(allowedFileTypes = {FILE_TYPE.PDF},
            message = "Please enter valid file format")
    private MultipartFile file;
}

Now we will create a REST API Post endpoint just to check whether our validations are working as expected or not, here we are not adding any business logic.

In order to trigger validation, we need to use @Valid annotation with @RequestBody or @ModelAttribute as shown below.

package com.learning.file.mgt.controller;

import com.learning.file.mgt.dto.FileUploadDTO;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;


@RestController
@RequestMapping("manage-file")
public class FileMgtController {

    @PostMapping
    public void addFile(@Valid 
                            @ModelAttribute 
                                    FileUploadDTO fileUploadDTO){
        System.out.println("File is valid");
    }

}

Here is the result if we upload an invalid file.

{

    "message": "Object validation failed",

    "response": "{file=Invalid file type.}"

}


Enclose the project structure for your reference.

We thus learned how to validate the file type in the Spring Boot Rest API by means of custom annotations.

If you are facing challenges when implementing the above solution do not hesitate to comment, and we will try to help you.

Thank you 

Enjoy your learning!