lambda multipart/file 在 go 中上传

lambda multipart/file upload in go

我需要使用 Go 对通过 AWS Lambda 上传的上传文件的内容进行一些简单的操作,但不确定如何解析接收的内容,因为我是 Go 的新手。到目前为止我找到的解决方案与http包和MultiPart表单功能有关。

type Request events.APIGatewayProxyRequest

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

这是我的请求正文的样子

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

this is content

------WebKitFormBoundaryx0SVDelfa90Fi5Uo--

我的请求是 APIGatewayProxyRequest 的实例。

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

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

这个有 3 个部分:

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

第 1 步:创建 multipart.Reader

multipart.NewReader 采用 io.Readerboundary 字符串,如签名所示:

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

为此,您需要从 Content-Type HTTP 请求 header 中提取边界字符串,这可以使用 mime.ParseMediaType.

来完成

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

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

第 2 步:获取 MIME 部分

获得 mime.Reader 后,浏览 MIME 消息,直到找到所需的 MIME 部分。

这里的例子只有一个部分,所以你可以直接调用:

part, err := reader.NextPart()

第 3 步:提取 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
}

上面的答案看起来是 正确的 方法,但是如果你很懒惰(就像我一样 :p )你可以创建一个 HTTP 请求,然后让本机库适合您:

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