Hi All,

Greetings for today!

Today I'm going to show you how to validate file sizes with the Spring Boot Rest API. So the approach here is to create custom annotations that can be reused in different DTO classes in your application, similar to how you would use Spring annotations like @Size, @NotNull, etc.

Before You Begin Have Questions? Refer to the links below for information on how to upload JSON and multipart files in a single REST API call.

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

Let's get started.

First, let's create a basic project structure for our project by accessing the Spring Initializr and adding the web, validation, and Lombok dependencies.



The first step is to create a custom annotation to validate the file size along with a Validator class implementing ConstraintValidator that adds logic to validate the file size as shown below.
package com.spring.learning.validation;

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 = FileSizeValidator.class)
@Documented
public @interface FileSize {

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

    String message() default "Max file size exceed.";
}

Below is the code for the FileSizeValidator used in the @FileSize annotation.
package com.spring.learning.validation;

import org.springframework.web.multipart.MultipartFile;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class FileSizeValidator 
  implements ConstraintValidator<FileSize, MultipartFile> {

    private static final Integer MB=1024*1024;

    private long maxSizeInMB;

    @Override
    public void initialize(FileSize fileSize) {
        this.maxSizeInMB=fileSize.maxSizeInMB();
    }

    @Override
    public boolean isValid(MultipartFile multipartFile, 
		ConstraintValidatorContext 
        	constraintValidatorContext) {

        if(multipartFile==null)
            return true;

        return multipartFile.getSize()<maxSizeInMB*MB;
    }
}
Note - multipartFile.getSize() returns size in bytes so we are converting the size configured in the annotation which will be in MB to bytes by multiplying it with the MB constant declared in the starting.

Now I've added a custom annotation @FileSize(maxSizeInMB=value) that I can use anywhere to validate the incoming file in the request, so I have the ItemDTO.java which will have JSON fields & file as input for AddItem Endpoint
package com.spring.learning.dto;

import com.spring.learning.validation.FileSize;
import lombok.*;
import org.springframework.web.multipart.MultipartFile;

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString(exclude = "file")
public class ItemDTO {

    private String name;

    private String description;

    @FileSize(maxSizeInMB = 10)
    private MultipartFile file;
}
As you can see, I've configured a file size of up to 10 MB as allowed using custom annotations.

Then you need to globally set the maximum allowed size for any file in application.properties with the following property:

spring.servlet.multipart.max-file-size=50MB
spring.servlet.multipart.max-request-size=50MB

Let's create a REST endpoint to add an item using ItemDTO as the request. We haven't added any logic to save the item here. Because the purpose is to validate incoming requests.

Note: The @Valid annotation is required on the controller for Spring to perform validation configured on the ItemDTO.
package com.spring.learning.controller;

import com.spring.learning.dto.ItemDTO;
import com.spring.learning.dto.ResponseDTO;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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("/item")
public class ItemController {

    @PostMapping
    public ResponseEntity<ResponseDTO<Object>> 
             addItem(@Valid @ModelAttribute ItemDTO itemDTO){
        System.out.println(itemDTO);

        return new ResponseEntity<>(ResponseDTO.
        		builder().message("Item added").
				 build(), HttpStatus.CREATED);
    }
}
Now, whenever any validation fails it will throw BindException. You should handle this and provide an appropriate error response to the end user. For this, we need to create a GlobalExceptionHandler.java as shown below, and a ResponseDTO.java that will be used to return the response to the API caller.
package com.spring.learning.dto;

import lombok.*;

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

    private String message;

    private T response;
}

Below is the code for GlobalExceptionHandler.java.
package com.spring.learning.controlleradvice;

@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);
    }
}

In the code above, we created an error map with the field name as the key and the error message as the value. This is obtained from the BindException object.

For testing purposes, use Postman to upload files and other fields as shown below.


Attaching the project structure for your reference.



So, we learned how to use custom annotations to validate file sizes with the Spring Boot Rest API.
Feel free to comment if you encounter any issues while implementing the above solution. We are happy to support you.

Thank you
Let's enjoy learning!