英文:
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
这里有三个部分:
- 从
events.APIGatewayProxyRequest
创建一个multipart.Reader
。 - 获取MIME部分。
- 提取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:
- create a
multipart.Reader
fromevents.APIGatewayProxyRequest
- get the MIME Part
- 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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论