在这个答案中,我将发布与这个问题相关的所有对我有用的东西。非常感谢@ThunderCat和@Flimzy提醒我上传请求的body参数已经是一个io.Reader。我将发布一些示例代码,评论我从这个问题中学到的知识以及它如何帮助我解决这个问题。也许这会对像我和@AlokKumarSingh 这样的人有所帮助。
情况 1:内存中已有数据(例如,从 Kafka、Kinesis 或 SQS 等流媒体/消息服务接收数据)
func main() {
if len(os.Args) != 3 {
fmt.Printf(
"bucket and file name required\nUsage: %s bucket_name filename",
os.Args[0],
)
}
bucket := os.Args[1]
filename := os.Args[2]
// this is your data that you have in memory
// in this example it is hard coded but it may come from very distinct
// sources, like streaming services for example.
data := "Hello, world!"
// create a reader from data data in memory
reader := strings.NewReader(data)
sess, err := session.NewSession(&aws.Config{
Region: aws.String("us-east-1")},
)
uploader := s3manager.NewUploader(sess)
_, err = uploader.Upload(&s3manager.UploadInput{
Bucket: aws.String(bucket),
Key: aws.String(filename),
// here you pass your reader
// the aws sdk will manage all the memory and file reading for you
Body: reader,
})
if err != nil {.
fmt.Printf("Unable to upload %q to %q, %v", filename, bucket, err)
}
fmt.Printf("Successfully uploaded %q to %q\n", filename, bucket)
}
情况 2:您已经有一个持久文件,并且想要上传它,但不想将整个文件保留在内存中:
func main() {
if len(os.Args) != 3 {
fmt.Printf(
"bucket and file name required\nUsage: %s bucket_name filename",
os.Args[0],
)
}
bucket := os.Args[1]
filename := os.Args[2]
// open your file
// the trick here is that the method os.Open just returns for you a reader
// for the desired file, so you will not maintain the whole file in memory.
// I know this might sound obvious, but for a starter (as I was at the time
// of the question) it is not.
fileReader, err := os.Open(filename)
if err != nil {
fmt.Printf("Unable to open file %q, %v", err)
}
defer fileReader.Close()
sess, err := session.NewSession(&aws.Config{
Region: aws.String("us-east-1")},
)
uploader := s3manager.NewUploader(sess)
_, err = uploader.Upload(&s3manager.UploadInput{
Bucket: aws.String(bucket),
Key: aws.String(filename),
// here you pass your reader
// the aws sdk will manage all the memory and file reading for you
Body: fileReader,
})
if err != nil {
fmt.Printf("Unable to upload %q to %q, %v", filename, bucket, err)
}
fmt.Printf("Successfully uploaded %q to %q\n", filename, bucket)
}
案例 3:这就是我在系统的最终版本上实现它的方式,但要了解我这样做的原因,我必须向您提供一些背景知识。
我的用例发生了一些变化。上传代码将成为 Lambda 中的一个函数,结果文件很大。此更改意味着什么:如果我通过附加到 Lambda 函数的 API Gateway 中的入口点上传文件,我将必须等待整个文件在 Lambda 中完成上传。由于 lambda 是根据调用的持续时间和内存使用情况定价的,因此这可能是一个非常大的问题。
因此,为了解决这个问题,我使用了预签名的帖子 URL 进行上传。这如何影响架构/工作流程?
我没有从后端代码上传到 S3,而是创建并验证一个 URL,用于在后端将对象发布到 S3,并将该 URL 发送到前端。这样,我就实现了对该 URL 的分段上传。我知道这比问题要具体得多,但发现这个解决方案并不容易,所以我认为在这里为其他人记录它是个好主意。
以下是如何创建预签名 URL 的示例nodejs.
const AWS = require('aws-sdk');
module.exports.upload = async (event, context, callback) => {
const s3 = new AWS.S3({ signatureVersion: 'v4' });
const body = JSON.parse(event.body);
const params = {
Bucket: process.env.FILES_BUCKET_NAME,
Fields: {
key: body.filename,
},
Expires: 60 * 60
}
let promise = new Promise((resolve, reject) => {
s3.createPresignedPost(params, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
})
return await promise
.then((data) => {
return {
statusCode: 200,
body: JSON.stringify({
message: 'Successfully created a pre-signed post url.',
data: data,
})
}
})
.catch((err) => {
return {
statusCode: 400,
body: JSON.stringify({
message: 'An error occurred while trying to create a pre-signed post url',
error: err,
})
}
});
};
如果你想使用go道理是一样的,只是换个sdk就可以了。