Description
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