Project Trouble Shooting : File image Upload API - File Size Problems With MySQL + Tomcat

#Foreword

We've decided to develop File Image Upload APIs, we didn't have specific API design, I needed to develop this on my own. We didn't use Amazon S3, therefore we decided to use Spring Data Jpa for File Image upload API.

When trying to add Image File that is 675.66KB that is less than 1MB, MySQL returns error : MysqlDataTruncation : "Data too long for column"

+After adding @Column(columnDefinition = "MEDIUMBLOB"), It could resolve MySQL column size limitation, but Tomcat return another error : FileSizeLimitExceededException: The field image exceeds its maximum permitted size of 1048576 bytes.(1048576 bytes is equivalent to 1 megabytes)

Reference URL : File Image APIs and API Design

(Image File Upload Using JPA With MySQL _ Url)

: https://joo.hashnode.dev/project-file-image-apis-with-jpamysql-api-design

#Problem 1 : MySQL - MysqlDataTruncation

: MysqlDataTruncation: Data truncation: Data too long for column 'image_data' at row1

DB error : 675.66KB is too big.

36.3KB is relatively small, successfully uploaded.

#Bytes, KiloBytes, MegaBytes, GigaBytes

  1. 1 Gigabyte (GB) to Megabytes (MB) and Kilobytes (KB)

    • 1 GB = 1024 MB

    • 1 GB = 1024 MB x 1024 KB/MB = 1,048,576 KB

  2. 1 Megabyte (MB) to Kilobytes (KB)

    • 1 MB = 1024 KB
  3. 1 Kilobyte (KB) to Bytes

    • 1 KB = 1024 Bytes

Here's a summary in ascending order:

  • 1 Kilobyte (KB) = 1024 Bytes

  • 1 Megabyte (MB) = 1024 Kilobytes (KB)

  • 1 Gigabyte (GB) = 1024 Megabytes (MB) = 1,048,576 Kilobytes (KB)

For example, 1 gigabyte is equal to 1,048,576 kilobytes.

So, if you want to upload an image of size 1 GB, you would need a byte array of approximately 1,048,576 kilobytes.

In fact, 1MB isn't small size for Image File.

#Solution1 : ColumnDefinition "MEDIUMBLOB"

It stands for Medium Binary Large Object.

A. new byte[4*1024] - [1000*1024] ==> Fail

4 * 1024 -> 4096 bytes : It's equivalent to 4 kilobytes.

what If i set this 1000*1024 -> it's equivalent to 1000kb, but 688.66KB image can't be uploaded.


B. Set ColumnDefinition = MEDIUMBLOB (16MB)

ImageData Entity before modification

ImageData Entity after adding@Column(columnDefinition = "MEDIUMBLOB")

POSTMAN Test Result (For solution 1)

: Success..........................! This is supposed to acommodate 16MB file size.

Below Image Size is 872.77KB

Success !


#Problem 2 : Tomcat - FileSizeLimitExceededException

I tried to upload a 1.4MB file, MEDIUMBLOB was supposed to accept this file size, but................Tomcat returns FileSizeLimitExceededException, refer to image below.

Although MEDIUMBLOB resolved DB size limitation, which can increase the size up to 16MB, Tomcat is throwing another exception: FileSizeLimitExceededException.

Caused by: java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException: The field image exceeds its maximum permitted size of 1048576 bytes.

-> 1048576 bytes is equivalent to 1 megabyte


#Solution2 : application.yml + 2 config class

import jakarta.servlet.MultipartConfigElement;
import org.springframework.boot.web.servlet.MultipartConfigFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.unit.DataSize;

@Configuration
public class FileUploadConfig {

    @Bean
    public MultipartConfigElement multipartConfigElement() {
        MultipartConfigFactory factory = new MultipartConfigFactory();
        factory.setMaxFileSize(DataSize.ofMegabytes(16));
        factory.setMaxRequestSize(DataSize.ofMegabytes(16));
        return factory.createMultipartConfig();
    }
}

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;

@Component
public class TomcatCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    @Override
    public void customize(TomcatServletWebServerFactory factory) {
        factory.addConnectorCustomizers(connector -> {
            connector.setMaxPostSize(16 * 1024 * 1024); // 16MB
        });
    }
}
spring:
  servlet:
    multipart:
      max-file-size: 16MB  # Set desired maximum file size
      max-request-size: 16MB  # Set desired maximum request size

server:
  tomcat:
    max-swallow-size: 16MB  # This sets the maximum size, Tomcat will swallow

POSTMAN Test Result (For solution 2)

7.87MB size file (It's for test, but I don't think we need such a big size image)

#conclusion

In conclusion, To resolve file size limitations of MySQL and Tomcat, 2 step solutions were needed.

Problem 1. Data too long for Column by DB MySQL
--> Solved by @Column(columnDefinition = "MEDIUMBLOB") : 16MB file size

Problem 2.FileSizeLimitExceededException by Tomcat(When file size is over 1MB)

--> Solved by application.yml file configuration + FileUploadConfig.class + TomcatCustomizer.class

Both solutions should be used for this. Like my trace, When using MEDIUMBLOB can increase DB's column size up to 16MB, but we can't upload a file that is over 1MB if we don't use application.yml configuration + other 2 classes config file.


#+Filtering Specific File Size On Window (For Exact Test)

Hm,,, Suddenly, it just came to my mind, How to find out Image File between 15MB and 16MB.............................? (For exact Test)

(On Window)

We can filter images like below.