저는 현재 s3-upload-stream 이라는 node.js 플러그인을 사용 하여 매우 큰 파일을 Amazon S3 로 스트리밍 하고 있습니다. 멀티 파트 API를 사용하며 대부분 잘 작동합니다.
그러나이 모듈은 그 나이를 보여주고 있으며 이미 수정해야했습니다 (저자도이 모듈을 사용하지 않습니다). 오늘 저는 Amazon에서 또 다른 문제를 만났고 작성자의 추천을 받아 공식 aws-sdk를 사용하여 업로드를 완료하고 싶습니다.
그러나.
공식 SDK는 s3.upload()
. s3.upload의 특성은 읽을 수있는 스트림을 S3 생성자에 인수로 전달해야한다는 것입니다.
다양한 파일 처리를 수행하는 약 120 개 이상의 사용자 코드 모듈이 있으며 출력의 최종 대상에 대해 알 수 없습니다. 엔진은 파이프 가능한 쓰기 가능한 출력 스트림을 전달하고 여기에 파이프합니다. 나는 그들에게 AWS.S3
객체를 건네 upload()
주고 모든 모듈에 코드를 추가하지 않고는 그것을 호출하도록 요청할 수 없습니다 . 내가 사용한 이유 s3-upload-stream
는 배관을 지원했기 때문입니다.
s3.upload()
스트림을 파이프 할 수 있는 대상으로 aws-sdk를 만드는 방법 이 있습니까?
답변
S3 upload()
함수를 node.js stream.PassThrough()
스트림으로 래핑합니다 .
예를 들면 다음과 같습니다.
inputStream
.pipe(uploadFromStream(s3));
function uploadFromStream(s3) {
var pass = new stream.PassThrough();
var params = {Bucket: BUCKET, Key: KEY, Body: pass};
s3.upload(params, function(err, data) {
console.log(err, data);
});
return pass;
}
답변
조금 늦게 대답하면 다른 사람에게 도움이 될 수 있습니다. 쓰기 가능한 스트림과 프라 미스를 모두 반환 할 수 있으므로 업로드가 완료되면 응답 데이터를 얻을 수 있습니다.
const AWS = require('aws-sdk');
const stream = require('stream');
const uploadStream = ({ Bucket, Key }) => {
const s3 = new AWS.S3();
const pass = new stream.PassThrough();
return {
writeStream: pass,
promise: s3.upload({ Bucket, Key, Body: pass }).promise(),
};
}
그리고 다음과 같이 기능을 사용할 수 있습니다.
const { writeStream, promise } = uploadStream({Bucket: 'yourbucket', Key: 'yourfile.mp4'});
const readStream = fs.createReadStream('/path/to/yourfile.mp4');
const pipeline = readStream.pipe(writeStream);
이제 약속을 확인할 수 있습니다.
promise.then(() => {
console.log('upload completed successfully');
}).catch((err) => {
console.log('upload failed.', err.message);
});
또는 stream.pipe()
파이프 체인을 허용하는 대상 (위의 writeStream 변수) 인 stream.Writable 을 반환하므로 해당 이벤트를 사용할 수도 있습니다.
pipeline.on('close', () => {
console.log('upload successful');
});
pipeline.on('error', (err) => {
console.log('upload failed', err.message)
});
답변
수락 된 답변에서 업로드가 완료되기 전에 기능이 종료되므로 잘못된 것입니다. 아래 코드는 읽을 수있는 스트림에서 올바르게 파이프됩니다.
async function uploadReadableStream(stream) {
const params = {Bucket: bucket, Key: key, Body: stream};
return s3.upload(params).promise();
}
async function upload() {
const readable = getSomeReadableStream();
const results = await uploadReadableStream(readable);
console.log('upload complete', results);
}
다음과 같이 한 단계 더 나아가 진행 정보를 출력 할 수도 있습니다 ManagedUpload
.
const manager = s3.upload(params);
manager.on('httpUploadProgress', (progress) => {
console.log('progress', progress) // { loaded: 4915, total: 192915, part: 1, key: 'foo.jpg' }
});
답변
내가 원했기 때문에 어떤 답변도 나를 위해 일하지 않았습니다.
- 파이프
s3.upload()
- 결과
s3.upload()
를 다른 스트림으로 파이프
받아 들여진 대답은 후자를하지 않습니다. 다른 것들은 promise api에 의존하는데, 이는 하천 파이프로 작업 할 때 번거 롭습니다.
이것은 받아 들여진 대답의 수정입니다.
const s3 = new S3();
function writeToS3({Key, Bucket}) {
const Body = new stream.PassThrough();
s3.upload({
Body,
Key,
Bucket: process.env.adpBucket
})
.on('httpUploadProgress', progress => {
console.log('progress', progress);
})
.send((err, data) => {
if (err) {
Body.destroy(err);
} else {
console.log(`File uploaded and available at ${data.Location}`);
Body.destroy();
}
});
return Body;
}
const pipeline = myReadableStream.pipe(writeToS3({Key, Bucket});
pipeline.on('close', () => {
// upload finished, do something else
})
pipeline.on('error', () => {
// upload wasn't successful. Handle it
})
답변
유형 스크립트 솔루션 :
이 예제에서는 다음을 사용합니다.
import * as AWS from "aws-sdk";
import * as fsExtra from "fs-extra";
import * as zlib from "zlib";
import * as stream from "stream";
그리고 비동기 기능 :
public async saveFile(filePath: string, s3Bucket: AWS.S3, key: string, bucketName: string): Promise<boolean> {
const uploadStream = (S3: AWS.S3, Bucket: string, Key: string) => {
const passT = new stream.PassThrough();
return {
writeStream: passT,
promise: S3.upload({ Bucket, Key, Body: passT }).promise(),
};
};
const { writeStream, promise } = uploadStream(s3Bucket, bucketName, key);
fsExtra.createReadStream(filePath).pipe(writeStream); // NOTE: Addition You can compress to zip by .pipe(zlib.createGzip()).pipe(writeStream)
let output = true;
await promise.catch((reason)=> { output = false; console.log(reason);});
return output;
}
이 메서드를 다음과 같이 호출하십시오.
let result = await saveFileToS3(testFilePath, someS3Bucket, someKey, someBucketName);
답변
위의 가장 많이 받아 들여진 답변에서 주목할 점은 다음과 같습니다. 파이프를 사용하는 경우 함수에서 패스를 반환해야합니다.
fs.createReadStream(<filePath>).pipe(anyUploadFunction())
function anyUploadFunction () {
let pass = new stream.PassThrough();
return pass // <- Returning this pass is important for the stream to understand where it needs to write to.
}
그렇지 않으면 오류를 발생시키지 않고 조용히 다음으로 이동하거나 TypeError: dest.on is not a function
함수를 작성한 방법 에 따라 오류가 발생 합니다.
답변
그것이 누구에게나 도움이된다면 클라이언트에서 s3로 성공적으로 스트리밍 할 수있었습니다.
https://gist.github.com/mattlockyer/532291b6194f6d9ca40cb82564db9d2a
서버 측 코드는 req
스트림 객체 라고 가정합니다 . 제 경우에는 헤더에 파일 정보가 설정된 클라이언트에서 전송되었습니다.
const fileUploadStream = (req, res) => {
//get "body" args from header
const { id, fn } = JSON.parse(req.get('body'));
const Key = id + '/' + fn; //upload to s3 folder "id" with filename === fn
const params = {
Key,
Bucket: bucketName, //set somewhere
Body: req, //req is a stream
};
s3.upload(params, (err, data) => {
if (err) {
res.send('Error Uploading Data: ' + JSON.stringify(err) + '\n' + JSON.stringify(err.stack));
} else {
res.send(Key);
}
});
};
예, 관습을 어기지만 요점을 보면 multer, busboy 등을 사용하여 찾은 다른 것보다 훨씬 깨끗합니다.
실용주의에 +1하고 도움을 주신 @SalehenRahman에게 감사드립니다.