简体   繁体   中英

Stream data in Node without sending content-length header

I have this function which streams buffered data:

function doChunkInput() {
  console.log('Put Objects Chunk');
  let stream = new Readable({
    read() {}
  })

  for(i=0; i < 1000; i++) {
    stream.push(' data');
    stream.push(' more data');
    stream.push(' and more data');
  }

  // Pay attention to this
  // null indicates the end of the stream, so the `data` event will be fired
  stream.push(null)

  const params = {
    Bucket: bucket,
    Body: stream,
    Key: `sitemap.1.xml`,
  };
  return s3.upload(params).promise();

Is it possible to from a stream without buffering. What I want is to stream data where the content-length can't be calculted. In the above example it is getting calculated in the buffer.

I'm trying to test the chunked upload but with the above example the content-length header is added to the request and the transfer-encoding header is not.

Short answer is no. AWS SDK does not support streaming, but there's a nice way around it.

You can upload in parts of any size (mind you, as per anything AWS, you pay a tiny bit for each upload - lots of parts may grow to a visible cost). There's even a nice npm module called: s3-stream-upload which exposes a normal Writable stream interface that you can pipe to.

You could use my scramjet if you'd prefer to have some special logic around the upload - it's actually quite simple:

const { StringStream } = require("scramjet");

const params = {Key: 'filename', Bucket: 'my-bucket'};
const { UploadId } = await s3.createMultipartUpload(params).promise();
let i = 1;

StringStream
    .from(function*() {
        for(i=0; i < 1000; i++) {
           yield ' data';
           yield ' more data';
           yield ' and more data';
        }
    })
    .toBufferStream()
    .breakup(1048576)   // you'd buffer every 1meg
    .map(
        (Body) => s3.uploadPart({
            ...params,
            Body,
            UploadId,
            PartNumber: i++
        }).promise()
    )
    .toArray()
    .then(
        parts => s3.completeMultipartUpload({
            ...params,
            MultipartUpload: {
                Parts: parts.map(
                    ({ ETag }, i) => ({ ETag, PartNumber: i + 1 })
                )
            }
        }).promise()
    )
    .catch(
        e => {
            console.error("error, aborting", e);
            return s3.abortMultipartUpload({...params, UploadId})
        }
    )

I'm sorry - I didn't run the example above, so it's more of a guide what should happen - but I can fix up tomorrow if you get stuck.


edit: Now I understand that you want to know how to create a stream and add to it without building the whole stream upfront

I changed the answer above to create a stream from generator. You could also implement the read method in your example to return the 'data' via this.push ( see docs here ), but I guess it's way simpler with scramjet and generators.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM