Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parse multipart/form-data Body on AWS Lambda in Java

I am new to AWS Lambda and I am trying to implement a Lambda function that receives a POST request containing data encoded as multipart/form-data. The message is received through the API Gateway using Lambda Proxy integration and the body is encoded in Base64 when it arrives to the Lambda function. After decoding it manually, I see it contains a multipart body like the following:

-----WebKitFormBoundary3EZ0C3tbP2JpAmz4
Content-Disposition: form-data; name="param1"

value1
-----WebKitFormBoundary3EZ0C3tbP2JpAmz4
Content-Disposition: form-data; name="param2"

value2
------WebKitFormBoundary3EZ0C3tbP2JpAmz4
Content-Disposition: form-data; name="myfile"; filename="ivr.png"
Content-Type: image/png

PNG
... [binary stuff]
------WebKitFormBoundary3EZ0C3tbP2JpAmz4--

What I need is to parse this message in java 8 so I can access the individual parts. I managed to do it using the +javax.mail.Multipart+ object but it seems I cannot access the "name" property for the parts and as such I cannot distinguish between same type elements, e.g. "param1" and "param2". I believe this can be related to the fact that this Class is for parsing email messages... Is there another way to parse this multipart body inside the lambda function? This is the code I have to parse it (base64 is the string containing the body):

DataSource source = new ByteArrayDataSource(new ByteArrayInputStream(Base64.decodeBase64(base64)), "multipart/mixed");
MimeMultipart mp = new MimeMultipart(source);

I'd appreciate any help you can provide.

like image 341
Armando Pinto Avatar asked May 09 '17 16:05

Armando Pinto


People also ask

Is react serverless?

We've got a completely serverless click counter in React. js. A local development environment, to test and make changes. And it's deployed to production as well, so you can share it with your users.

How do you use multipart form data?

Multipart form data: The ENCTYPE attribute of <form> tag specifies the method of encoding for the form data. It is one of the two ways of encoding the HTML form. It is specifically used when file uploading is required in HTML form. It sends the form data to server in multiple parts because of large size of file.

How do you call an AWS API in react JS?

You will want to select “New API'” here, enter in an “API name” and click “Create API”. Under the “Actions” menu please select “Create Method”. Please select “ANY” here and click on the tick to confirm. Make sure you select “Lambda Function” here and type in your “Lambda Function” name, in this case “mediumtutorial”.


1 Answers

Ok so this is definitely NOT the ideal solution but I was able to make this work.

Problem

There are actually many libraries out there to parse multipart form data. The actual problem is all libraries rely on javax.servlet package - most importantly HttpServletRequest class (and few more).

Since we can't access javax.servlet package classes in AWS Lambda environment, my solution is to fix that.


Solution

  1. Download the javax.servlet package from GitHub and add that to you your lambda function. See the image below - you can see that my class MultipartFormDataTest is within my package com... and I also have javax.servlet package within the same Java module.

Javax Servlet Package added to Lambda Module

  1. Once we do this, we can use one or more libraries that will do all the work for us. The best library I've found that will parse the file content is Delight FileUpload - https://mvnrepository.com/artifact/org.javadelight/delight-fileupload.

  2. Once that library is added, the method below getFilesFromMultipartFormData() will return ArrayList<File> where each item in the list represents a File that was sent in the request.

/**
 * @param context context
 * @param body this value taken from the `request.getBody()`
 * @param contentType this value is taken from `request.headers().get("Content-Type")`
 * @return List of File objects
 */
private List<File> getFilesFromMultipartFormData(Context context, String body, String contentType) {
    ArrayList<File> files = new ArrayList<>();
    List<FileItem> fileItems = FileUpload.parse(body.getBytes(StandardCharsets.UTF_8), contentType);

    for(FileItem fileItem : fileItems) {
        if(fileItem == null) {
            continue;
        }

        logger.log("fileItem name: " + fileItem.getName());
        logger.log("fileItem content-type: " + fileItem.getContentType());
        logger.log("fileItem size: " + fileItem.getSize());

        // Note: instead of storing it locally, you can also directly store it to S3 for example
        try {
            // I'm setting the extension to .png but you can look at the fileItem.getContentType()
            // to make sure it is an image vs. pdf vs. some other format
            File temp = File.createTempFile(fileItem.getName(), ".png");
            Files.copy(fileItem.getInputStream(), temp.toPath(), StandardCopyOption.REPLACE_EXISTING);
            files.add(temp);
        } catch (Exception e) {
            continue;
        }
    }

    return files;
}
like image 87
ᴛʜᴇᴘᴀᴛᴇʟ Avatar answered Oct 11 '22 03:10

ᴛʜᴇᴘᴀᴛᴇʟ