Hol*_*yol 35 stream node.js aws-sdk-nodejs aws-sdk-js-v3
我必须将文件从 API 端点传输到两个不同的存储桶。原始上传是使用:
curl -X PUT -F "data=@sample" "http://localhost:3000/upload/1/1"
Run Code Online (Sandbox Code Playgroud)
文件上传的端点:
curl -X PUT -F "data=@sample" "http://localhost:3000/upload/1/1"
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,我使用两个PassThrough流,以便将请求流复制为两个可读流,如该 SO thread 中所建议的。
这段代码保持不变,这里有趣的是uploadToFirstS3和uploadToSecondS3函数。在这个最小的示例中,两者使用不同的配置执行完全相同的操作,我在这里仅使用一个。
什么效果好:
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' });
}
}
Run Code Online (Sandbox Code Playgroud)
这段代码(基于aws-sdk包)工作正常。我的问题是,我希望它与@aws-sdk/client-s3包一起运行,以减少项目的大小。
什么不起作用:
我首先尝试使用S3Client.send(PutObjectCommand):
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);
});
}));
Run Code Online (Sandbox Code Playgroud)
然后我尝试了S3.putObject(PutObjectCommandInput):
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);
});
}));
Run Code Online (Sandbox Code Playgroud)
最后两个示例都给我提供了501 - Not Implemented标题错误Transfer-Encoding。我查了一下req.headers,里面没有Transfer-Encoding,所以我猜sdk添加了对s3的请求?
由于第一个示例(基于aws-sdk)工作正常,我确信该错误不是由于请求中的空主体所致,如该 SO thread中所建议的。
尽管如此,我认为触发上传时流可能还不可读,因此我将调用包装起来uploadToFirstS3并uploadToSecondS3使用事件触发的回调req.on('readable', callback),但没有任何改变。
我想随时处理内存中的文件而不将其存储在磁盘上。有没有办法使用@aws-sdk/client-s3包来实现它?
And*_*ndy 66
在 v3 中,您可以使用Uploadfrom 的类@aws-sdk/lib-storage来进行分段上传。不幸的是,文档网站中似乎没有提及这一点@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);
}
Run Code Online (Sandbox Code Playgroud)
我确实遇到了与您相同的错误。他们似乎有一个尚未准确记录的已知问题:
该错误确实是由于流长度未知而引起的。我们需要改进错误消息和文档
为了解决这个问题,你只需要指定Content-length属性PutObjectCommand
这是更新的片段:
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 = (passThroughStream) => (new Promise((resolve, reject) => {
const uploadParams = {
Bucket: S3_BUCKET_NAME,
Key:'some-key',
Body: passThroughStream,
ContentLength: passThroughStream.readableLength, // include this new field!!
};
s3.putObject(uploadParams, (err) => {
if (err) reject(err);
resolve(true);
});
}));
Run Code Online (Sandbox Code Playgroud)
希望能帮助到你!