Skip to content

SpringBoot3 not processing all Multipart files when Array of files with same fieldName is sent in request #633

Closed
@LuisTDev

Description

@LuisTDev

To help us debug your issue fill in the basic information below using the options provided

Serverless Java Container version: 2.0.0-M2

Implementations: Spring Boot 3

Framework version: SpringBoot 3.1.2

Frontend service: REST API

Deployment method: SAM, Serverless Framework

Scenario

I am trying to process this Spring Controller method where in "rightFile" I am expecting an Array of files with the same fieldName

@PostMapping("/compare") public List<ProcessedRow> compare( @RequestParam("leftFile") MultipartFile leftFile, @RequestParam("rightFile") List<MultipartFile> rightFile) { ... }

Expected behavior

I should get all files with same fieldname sent in request mapped as a list in rightFile

Actual behavior

I am only getting the last file mapped as a list of 1 MultipartFile in rightFile

Steps to reproduce

1.- create a SAM project with a Java Lambda that has enabled multipart form data in BinaryMediaTypes

template.yaml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Example XlsComparator API written with SpringBoot with the aws-serverless-java-container library

Globals:
Api:
# API Gateway regional endpoints
EndpointConfiguration: REGIONAL
# Configure API to accept multipart form data
BinaryMediaTypes:
- "multipart~1form-data"
Cors:
AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
AllowMethods: "'GET,POST,OPTIONS'"
AllowOrigin: "'*'"

Resources:

XlsComparatorFunction:
Type: AWS::Serverless::Function
Properties:
Handler: com.lthome.comparexcel.StreamLambdaHandler::handleRequest
Runtime: java17
CodeUri: .
MemorySize: 1512
Policies:
- AWSLambdaBasicExecutionRole
Timeout: 60
SnapStart:
ApplyOn: PublishedVersions
Events:
ApiEvent:
Type: Api
Properties:
Path: /compare
Method: post

2.- Create a Rest Controller in Spring with a POST Method with the following signature
@PostMapping("/compare")
public List compare(
@RequestParam("leftFile") MultipartFile leftFile,
@RequestParam("rightFile") List rightFile) {
...
}

3.- Set the library aws-serverless-java-container and configure it in the project to make the Lambda Integration with our Spring Boot Project, here is an example of StreamLambdaHandler.java used.

public class StreamLambdaHandler implements RequestHandler<AwsProxyRequest, AwsProxyResponse> {
private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;

static {
try {
handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(ComparexcelApplication.class);
} catch (ContainerInitializationException e) {
e.printStackTrace();
throw new RuntimeException("Could not initialize Spring Boot application", e);
}
}

@OverRide
public AwsProxyResponse handleRequest(AwsProxyRequest input, Context context) {
AwsProxyResponse response = null;
response = handler.proxy(input, context);
// Set CORS Headers
response
.getMultiValueHeaders()
.put("Access-Control-Allow-Methods", List.of("GET", "POST", "OPTIONS"));
response
.getMultiValueHeaders()
.put(
"Access-Control-Allow-Headers",
List.of(
"Content-Type",
"X-Amz-Date",
"Authorization",
"X-Api-Key",
"X-Amz-Security-Token"));
response.getMultiValueHeaders().put("Access-Control-Allow-Origin", List.of("*"));
return response;
}
}

4.- Test the method by sending as form data only 1 file in field "leftFile" and 2 Files in field "righFile", you will se that in spring controller we will get only 2 files: 1 for leftFile and 1 for rightFile.

Full log output

There are no errors in Logs but still not getting all Files sent in request in the controller, I believe the issue is in AwsHttpServletRequest.java in this method:

@SuppressFBWarnings({"FILE_UPLOAD_FILENAME", "WEAK_FILENAMEUTILS"})
protected Map<String, Part> getMultipartFormParametersMap() {
if (multipartFormParameters != null) {
return multipartFormParameters;
}
if (!JakartaServletFileUpload.isMultipartContent(this)) { // isMultipartContent also checks the content type
multipartFormParameters = new HashMap<>();
return multipartFormParameters;
}
Timer.start("SERVLET_REQUEST_GET_MULTIPART_PARAMS");
multipartFormParameters = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);

    JakartaServletFileUpload upload = new JakartaServletFileUpload(DiskFileItemFactory.builder().get());

    try {
        **//Note: here we  get the 3 items which is correct**
        List<FileItem> items = upload.parseRequest(this);
        for (FileItem item : items) {
            String fileName = FilenameUtils.getName(item.getName());
            AwsProxyRequestPart newPart = new AwsProxyRequestPart(item.get());
            newPart.setName(item.getFieldName());
            newPart.setSubmittedFileName(fileName);
            newPart.setContentType(item.getContentType());
            newPart.setSize(item.getSize());
            item.getHeaders().getHeaderNames().forEachRemaining(h -> {
                newPart.addHeader(h, item.getHeaders().getHeader(h));
            });

           **//ISSUE: the problem is here when there are two items with the same fieldName we lose the list of items because map is updated with a newPart instead of adding it to a List of newParts ?**
            multipartFormParameters.put(item.getFieldName(), newPart);
        }
    } catch (FileUploadException e) {
        Timer.stop("SERVLET_REQUEST_GET_MULTIPART_PARAMS");
        log.error("Could not read multipart upload file", e);
    }
    Timer.stop("SERVLET_REQUEST_GET_MULTIPART_PARAMS");
    return multipartFormParameters;
}
logs

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions