如何使用 AWS SDK v3 将流上传到 S3
How to upload a stream to S3 with AWS SDK v3
我必须将文件从 API 端点传输到两个不同的存储桶。
原始上传使用:
curl -X PUT -F "data=@sample" "http://localhost:3000/upload/1/1"
上传文件的端点:
const PassThrough = require('stream').PassThrough;
async function uploadFile (req, res) {
try {
const firstS3Stream = new PassThrough();
const secondS3Stream = new PassThrough();
req.pipe(firstS3Stream);
req.pipe(secondS3Stream);
await Promise.all([
uploadToFirstS3(firstS3Stream),
uploadToSecondS3(secondS3Stream),
]);
return res.end();
} catch (err) {
console.log(err)
return res.status(500).send({ error: 'Unexpected error during file upload' });
}
}
如你所见,我用了两个PassThrough streams, in order to duplicate the request stream into two readable streams, as suggested 。
这段代码不变,这里有趣的是uploadToFirstS3
和uploadToSecondS3
函数。在这个最小的例子中,两者都用不同的配置做完全相同的事情,我将在这里只花费一个。
有效的方法:
const aws = require('aws-sdk');
const s3 = new aws.S3({
accessKeyId: S3_API_KEY,
secretAccessKey: S3_API_SECRET,
region: S3_REGION,
signatureVersion: 'v4',
});
const uploadToFirstS3 = (stream) => (new Promise((resolve, reject) => {
const uploadParams = {
Bucket: S3_BUCKET_NAME,
Key: 'some-key',
Body: stream,
};
s3.upload(uploadParams, (err) => {
if (err) reject(err);
resolve(true);
});
}));
这段代码(为了减少工程大小,基于aws-sdk package) works fine. My issue here is that i want it to run with the @aws-sdk/client-s3包。
什么不起作用:
我第一次尝试使用 S3Client.send(PutObjectCommand):
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const s3 = new S3Client({
credentials: {
accessKeyId: S3_API_KEY,
secretAccessKey: S3_API_SECRET,
},
region: S3_REGION,
signatureVersion: 'v4',
});
const uploadToFirstS3 = (stream) => (new Promise((resolve, reject) => {
const uploadParams = {
Bucket: S3_BUCKET_NAME,
Key:'some-key',
Body: stream,
};
s3.send(new PutObjectCommand(uploadParams), (err) => {
if (err) reject(err);
resolve(true);
});
}));
然后我尝试了 S3.putObject(PutObjectCommandInput):
const { S3 } = require('@aws-sdk/client-s3');
const s3 = new S3({
credentials: {
accessKeyId: S3_API_KEY,
secretAccessKey: S3_API_SECRET,
},
region: S3_REGION,
signatureVersion: 'v4',
});
const uploadToFirstS3 = (stream) => (new Promise((resolve, reject) => {
const uploadParams = {
Bucket: S3_BUCKET_NAME,
Key:'some-key',
Body: stream,
};
s3.putObject(uploadParams, (err) => {
if (err) reject(err);
resolve(true);
});
}));
最后两个例子都给我一个 501 - Not Implemented 错误 header Transfer-Encoding
。我检查了 req.headers
里面没有 Transfer-Encoding
,所以我猜 sdk 添加到 s3 的请求中 ?
自第一个示例(基于 aws-sdk) works fine, I'm sure the error is not due to an empty body in the request as suggested in this SO thread.
不过,我认为在触发上传时流可能还不可读,因此我将对 uploadToFirstS3
和 uploadToSecondS3
的调用包装在 req.on('readable', callback)
触发的回调中事件,但没有任何改变。
我想处理内存中的文件,而不是随时将其存储在磁盘上。有没有办法使用 @aws-sdk/client-s3 包来实现它?
在 S3 中,您可以使用 @aws-sdk/lib-storage
中的 Upload
class 进行分段上传。不幸的是,@aws-sdk/client-s3
的文档站点中似乎没有提及这一点。
这里的升级指南中提到了它:https://github.com/aws/aws-sdk-js-v3/blob/main/UPGRADING.md#s3-multipart-upload
这是https://github.com/aws/aws-sdk-js-v3/tree/main/lib/lib-storage中提供的示例:
import { Upload } from "@aws-sdk/lib-storage";
import { S3Client } from "@aws-sdk/client-s3";
const target = { Bucket, Key, Body };
try {
const parallelUploads3 = new Upload({
client: new S3Client({}),
tags: [...], // optional tags
queueSize: 4, // optional concurrency configuration
leavePartsOnError: false, // optional manually handle dropped parts
params: target,
});
parallelUploads3.on("httpUploadProgress", (progress) => {
console.log(progress);
});
await parallelUploads3.done();
} catch (e) {
console.log(e);
}
我必须将文件从 API 端点传输到两个不同的存储桶。 原始上传使用:
curl -X PUT -F "data=@sample" "http://localhost:3000/upload/1/1"
上传文件的端点:
const PassThrough = require('stream').PassThrough;
async function uploadFile (req, res) {
try {
const firstS3Stream = new PassThrough();
const secondS3Stream = new PassThrough();
req.pipe(firstS3Stream);
req.pipe(secondS3Stream);
await Promise.all([
uploadToFirstS3(firstS3Stream),
uploadToSecondS3(secondS3Stream),
]);
return res.end();
} catch (err) {
console.log(err)
return res.status(500).send({ error: 'Unexpected error during file upload' });
}
}
如你所见,我用了两个PassThrough streams, in order to duplicate the request stream into two readable streams, as suggested
这段代码不变,这里有趣的是uploadToFirstS3
和uploadToSecondS3
函数。在这个最小的例子中,两者都用不同的配置做完全相同的事情,我将在这里只花费一个。
有效的方法:
const aws = require('aws-sdk');
const s3 = new aws.S3({
accessKeyId: S3_API_KEY,
secretAccessKey: S3_API_SECRET,
region: S3_REGION,
signatureVersion: 'v4',
});
const uploadToFirstS3 = (stream) => (new Promise((resolve, reject) => {
const uploadParams = {
Bucket: S3_BUCKET_NAME,
Key: 'some-key',
Body: stream,
};
s3.upload(uploadParams, (err) => {
if (err) reject(err);
resolve(true);
});
}));
这段代码(为了减少工程大小,基于aws-sdk package) works fine. My issue here is that i want it to run with the @aws-sdk/client-s3包。
什么不起作用:
我第一次尝试使用 S3Client.send(PutObjectCommand):
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const s3 = new S3Client({
credentials: {
accessKeyId: S3_API_KEY,
secretAccessKey: S3_API_SECRET,
},
region: S3_REGION,
signatureVersion: 'v4',
});
const uploadToFirstS3 = (stream) => (new Promise((resolve, reject) => {
const uploadParams = {
Bucket: S3_BUCKET_NAME,
Key:'some-key',
Body: stream,
};
s3.send(new PutObjectCommand(uploadParams), (err) => {
if (err) reject(err);
resolve(true);
});
}));
然后我尝试了 S3.putObject(PutObjectCommandInput):
const { S3 } = require('@aws-sdk/client-s3');
const s3 = new S3({
credentials: {
accessKeyId: S3_API_KEY,
secretAccessKey: S3_API_SECRET,
},
region: S3_REGION,
signatureVersion: 'v4',
});
const uploadToFirstS3 = (stream) => (new Promise((resolve, reject) => {
const uploadParams = {
Bucket: S3_BUCKET_NAME,
Key:'some-key',
Body: stream,
};
s3.putObject(uploadParams, (err) => {
if (err) reject(err);
resolve(true);
});
}));
最后两个例子都给我一个 501 - Not Implemented 错误 header Transfer-Encoding
。我检查了 req.headers
里面没有 Transfer-Encoding
,所以我猜 sdk 添加到 s3 的请求中 ?
自第一个示例(基于 aws-sdk) works fine, I'm sure the error is not due to an empty body in the request as suggested in this SO thread.
不过,我认为在触发上传时流可能还不可读,因此我将对 uploadToFirstS3
和 uploadToSecondS3
的调用包装在 req.on('readable', callback)
触发的回调中事件,但没有任何改变。
我想处理内存中的文件,而不是随时将其存储在磁盘上。有没有办法使用 @aws-sdk/client-s3 包来实现它?
在 S3 中,您可以使用 @aws-sdk/lib-storage
中的 Upload
class 进行分段上传。不幸的是,@aws-sdk/client-s3
的文档站点中似乎没有提及这一点。
这里的升级指南中提到了它:https://github.com/aws/aws-sdk-js-v3/blob/main/UPGRADING.md#s3-multipart-upload
这是https://github.com/aws/aws-sdk-js-v3/tree/main/lib/lib-storage中提供的示例:
import { Upload } from "@aws-sdk/lib-storage";
import { S3Client } from "@aws-sdk/client-s3";
const target = { Bucket, Key, Body };
try {
const parallelUploads3 = new Upload({
client: new S3Client({}),
tags: [...], // optional tags
queueSize: 4, // optional concurrency configuration
leavePartsOnError: false, // optional manually handle dropped parts
params: target,
});
parallelUploads3.on("httpUploadProgress", (progress) => {
console.log(progress);
});
await parallelUploads3.done();
} catch (e) {
console.log(e);
}