在Go语言中进行Lambda多部分/文件上传

huangapple go评论95阅读模式
英文:

lambda multipart/file upload in go

问题

我需要使用Go语言对通过AWS Lambda上传的文件内容进行一些简单的操作,但是由于我对Go语言还不熟悉,所以不确定如何解析接收到的内容。到目前为止,我找到的解决方案与http包和MultiPart表单函数有关。

这是我的请求体的样子:

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

this is content

------WebKitFormBoundaryx0SVDelfa90Fi5Uo--

我的请求是APIGatewayProxyRequest的实例。

我想知道是否有可能获得一个自定义的结构体,从中可以访问数据,例如:

customStruct.content => "this is content"
customStruct.fileName => upload.txt
customStruct.fileExtension => txt
英文:

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

答案1

得分: 4

这里有三个部分:

  1. events.APIGatewayProxyRequest创建一个multipart.Reader
  2. 获取MIME部分。
  3. 提取MIME部分的值。

第一步:创建multipart.Reader

multipart.NewReader接受一个io.Reader和一个boundary字符串,如下所示的签名:

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

为了做到这一点,你需要从Content-Type的HTTP请求头中提取boundary字符串,可以使用mime.ParseMediaType来完成。

一个简单的方法是调用go-awslambda包中的NewReaderMultipart,它具有以下签名:

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

第二步:获取MIME部分

一旦你有了mime.Reader,就可以浏览MIME消息,直到找到所需的MIME部分。

在这个例子中,只有一个部分,所以你可以简单地调用:

part, err := reader.NextPart()

第三步:提取MIME部分的值

一旦你有了MIME部分,就可以提取所需的值。

第3.1步:内容

content, err := io.ReadAll(part)

第3.2步:文件名

从MIME部分中获取文件名,如下所示:

filename := part.FileName()

第3.3步:文件扩展名

调用path/filepath.Ext。这将在扩展名前添加前导句点.,但可以很容易地删除。

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

总结

你可以将它们组合如下:

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
}
英文:

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
}

答案2

得分: 2

上面的答案看起来是做这个的正确方法,但如果你懒得做(就像我一样 :p ),你可以创建一个HTTP请求,让本地库为你工作:

httpReq, err := http.NewRequestWithContext(ctx, req.HTTPMethod, reqURL.String(), strings.NewReader(req.Body))
if err != nil {
	return nil, errors.New("将lambda请求转换为http请求失败")
}

// 一些头部可能很重要,让我们获取所有的头部,以防万一
for name, value := range req.Headers {
	httpReq.Header.Add(name, value)
}


// 从这里开始,你可以使用httpReq.FormFile()来读取文件
英文:

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

huangapple
  • 本文由 发表于 2021年7月22日 20:27:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/68484861.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定