1

I need to do some simple manipulation with the content of the uploaded file uploaded through AWS Lambda using Go, but not sure how to parse the receiving content since I'm new to Go. The solutions that I've found so far are related to http package and MultiPart form function.

type Request events.APIGatewayProxyRequest

func Handler(ctx context.Context, req Request) (Response, error) {
fmt.Println(req.Body)
....
}

This is how my request body looks like

------WebKitFormBoundaryx0SVDelfa90Fi5Uo
Content-Disposition: form-data; name="file"; filename="upload.txt"
Content-Type: text/plain

this is content

------WebKitFormBoundaryx0SVDelfa90Fi5Uo--

my request is instance of APIGatewayProxyRequest.

I would like to know if it is possible to get a custom struct from which I can access data like f.e.

customStruct.content => "this is content"
customStruct.fileName => upload.txt
customStruct.fileExtension => txt
4
  • You haven't given much information about what you need help with. Would the strings package help you (e.g. for strings.Split() or for strings.Index())? pkg.go.dev/strings Commented Jul 22, 2021 at 12:52
  • understand, my actual question would be is there an option to parse the content in the body in way so that I can get a struct from which I can do something like ``` fileStruct.content fileStruct.fileName fileStruct.fileFormat ``` Commented Jul 22, 2021 at 12:57
  • 1
    pkg.go.dev/mime/multipart#NewReader Commented Jul 22, 2021 at 17:41
  • 1
    if you read the standard api, cs.opensource.google/go/go/+/refs/tags/go1.16.6:src/net/http/…, they call for cs.opensource.google/go/go/+/refs/tags/go1.16.6:src/mime/… that should do it to reproduce this code. Commented Jul 22, 2021 at 17:44

2 Answers 2

6

There are 3 parts to this:

  1. create a multipart.Reader from events.APIGatewayProxyRequest
  2. get the MIME Part
  3. extract MIME Part values

Step 1: Create a multipart.Reader

The multipart.NewReader takes an io.Reader and boundary string as shown by the signature:

func NewReader(r io.Reader, boundary string) *Reader

To do this, you will need to extract the boundary string from the Content-Type HTTP request header which can be done using mime.ParseMediaType.

An easy way to do this is to call NewReaderMultipart from the go-awslambda package which has the following signature:

func NewReaderMultipart(req events.APIGatewayProxyRequest) (*multipart.Reader, error)

Step 2: get the MIME Part

Once you have the mime.Reader, navigate the MIME message till you find the MIME part desired.

In the example here, there's only one part, so you can simply call:

part, err := reader.NextPart()

Step 3: Extract MIME part values

Once you have the MIME Part, the desired values can be extracted.

Step 3.1: Content

content, err := io.ReadAll(part)

Step 3.2: File name

Get the file name from the MIME part as follows:

filename := part.FileName()

Step 3.3: File extension

Call path/filepath.Ext. This will add the leading period . in the extension but this can be easily removed.

ext := filepath.Ext(part.FileName())

Summary

You can combine this as follows:

import (
    "context"
    "encoding/json"
    "io"

    "github.com/aws/aws-lambda-go/events"
    "github.com/grokify/go-awslambda"
)

type customStruct struct {
    Content       string
    FileName      string
    FileExtension string
}

func handleRequest(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
    res := events.APIGatewayProxyResponse{}
    r, err := awslambda.NewReaderMultipart(req)
    if err != nil {
        return res, err
    }
    part, err := r.NextPart()
    if err != nil {
        return res, err
    }
    content, err := io.ReadAll(part)
    if err != nil {
        return res, err
    }
    custom := customStruct{
        Content:       string(content),
        FileName:      part.FileName(),
        FileExtension: filepath.Ext(part.FileName())}

    customBytes, err := json.Marshal(custom)
    if err != nil {
        return res, err
    }

    res = events.APIGatewayProxyResponse{
        StatusCode: 200,
        Headers: map[string]string{
            "Content-Type": "application/json"},
        Body: string(customBytes)}
    return res, nil
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for your help! I've actually tried the approach before, but instead of creating NewReaderMultipart I've tried creating the buffer that I would pass in to NewReader which was allowed bu compiler but would return me nil as return value every time. My temp solution was base64 encoding of the file before it being sent to the lambda
2

The answer above looks to be the correct way to do that, but if you are lazy (as I am :p ) you can just create an HTTP request, and let the native lib work to you:

httpReq, err := http.NewRequestWithContext(ctx, req.HTTPMethod, reqURL.String(), strings.NewReader(req.Body))
if err != nil {
    return nil, errors.New("failed to convert a lambda req into a http req")
}

// some headers may be important, let get all of them, just in case
for name, value := range req.Headers {
    httpReq.Header.Add(name, value)
}


// from here you can use httpReq.FormFile() to read the file

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.