15

I'm uploading a file using Request.

req = request.post url: "http://foo.com", body: fileAsBuffer, (err, res, body) ->
    console.log "Uploaded!"

How do I know how much data has actually been uploaded? Is there some event that I can subscribe to or is there a property of the request that I can poll?

If none, what would be the best approach to upload data and know how much has been uploaded?

Gautham Badhrinathan
  • 2,377
  • 3
  • 20
  • 30

5 Answers5

14

I spent a couple of hours to find anything valid in request and node sources, and finally found a different approach, which feels more correct to me.

We can rely on drain event and bytesWritten property:

request.put({
  url: 'https://example.org/api/upload',
  body: fs.createReadStream(path)
}).on('drain', () => {
  console.log(req.req.connection.bytesWritten);
});

Alternatively if you need to handle progress of file bytes, it's easier to use stream data event:

let size = fs.lstatSync(path).size;
let bytes = 0;

request.put({
  url: 'https://example.org/api/upload',
  body: fs.createReadStream(path).on('data', (chunk) => {
    console.log(bytes += chunk.length, size);
  })
});

Stream buffer size is 65536 bytes and read/drain procedure runs iteratively.

This seems to be working pretty well for me with node v4.5.0 and request v2.74.0.

VisioN
  • 143,310
  • 32
  • 282
  • 281
  • 1
    The drain method worked for me. However I had to pipe the file as opposed to just pass it in via the body parameter. When I passed it in via the body parameter it would would drain multiple times however it would do it in quick succession and not when the server actually received the data. It seems that passing it via body results in it being buffered, which doesn't give the correct experience. Cheers. – McP Mar 13 '17 at 12:37
  • @vision, what is `req.req.connection`? I mean first "req" is a variable that contains... what does it contain? – Vladlen Volkov Aug 08 '19 at 07:54
  • 1
    @ВладВолков The first `req` is a variable from the question (instance of `Request`), the second `req` comes from here: https://github.com/request/request/blob/212570b6971a732b8dd9f3c73354bcdda158a737/request.js#L751. – VisioN Aug 09 '19 at 10:26
  • @VisioN I'm trying to get the bytesWritten of the request but I get only the total size of the FormData... I don't know what I'm doing wrong... also tried _bytesDispatched and the same... do you know what could be the problem? On drain doesn't work at all... I'm uploading a formdata file (binary) – Enrique Mar 06 '21 at 20:45
11

I needed a handle on the upload progress for yet another project of mine.

What I found out is that you can poll the request's connection._bytesDispatched property.

For example:

r = request.post url: "http://foo.com", body: fileAsBuffer
setInterval (-> console.log "Uploaded: #{r.req.connection._bytesDispatched}"), 250

Note: If you were piping to r, poll r.req.connection.socket._bytesDispatched instead.

Gautham Badhrinathan
  • 2,377
  • 3
  • 20
  • 30
  • 2
    Looking for this too. Although it looks like an internal API(expected to be change in the future `nodejs` versions), it seems to be the only solution. – Michael Yin May 12 '14 at 17:20
  • 2
    This is painful; is this the only Node.js solution available? Seriously, why hasn't the problem of file uploads to an HTTP API in Node.js been solved yet?! – Sukima Jul 29 '14 at 02:16
3
var request = require('request');
    var fs = require('fs');  
    let path ="C:/path/to/file";
    var formData = {
        vyapardb: fs.createReadStream(path)
    };

    let size = fs.lstatSync(path).size;

    var headers = {
        'Accept' : 'application/json',
        'Authorization' : 'Bearer '+token,
    };

    var r = request.post({url:'http://35.12.13/file/upload', formData: formData,  headers: headers}, function optionalCallback(err, httpResponse, body) {
        clearInterval(q);

    });
    var q = setInterval(function () {
        var dispatched = r.req.connection._bytesDispatched;
        let percent = dispatched*100/size;
         console.dir("Uploaded: " + percent + "%");

    }, 250);
}
Himanshu
  • 180
  • 1
  • 7
1

I know this is old, but I just found a library 'progress-stream' that has not been mentioned and does this very nicely. https://www.npmjs.com/package/progress-stream

const streamProg = require('progress-stream');
const fs = require('fs');
const request = require('request');

const myFile = 'path/to/file.txt';
const fileSize = fs.statSync('path/to/file.txt').size;
const readStream = fs.createReadStream(myFile);
const progress = streamProg({time: 1000, length: fileSize})
    .on('progress', state => console.log(state));

readStream.pipe(progress).pipe(request.put({url: 'SOMEURL/file.txt'}));

Similarly, it can be used between a pipe on download as well.

iceblueorbitz
  • 920
  • 1
  • 7
  • 17
-2

Someone has created a nice module to accomplish this that has been running in the production stack for transloadit (so it is dependable and well maintained). You can find it here:

https://github.com/felixge/node-formidable

Code should look like:

var formidable = require('formidable'),
    http = require('http'),

    util = require('util');

http.createServer(function(req, res) {
  if (req.url == '/upload' && req.method.toLowerCase() == 'post') {
    // parse a file upload
    var form = new formidable.IncomingForm();
    form.parse(req, function(err, fields, files) {
      res.writeHead(200, {'content-type': 'text/plain'});
      res.write('received upload:\n\n');
      res.end(util.inspect({fields: fields, files: files}));
    });
    return;
  }

  // show a file upload form
  res.writeHead(200, {'content-type': 'text/html'});
  res.end(
    '<form action="/upload" enctype="multipart/form-data" method="post">'+
    '<input type="text" name="title"><br>'+
    '<input type="file" name="upload" multiple="multiple"><br>'+
    '<input type="submit" value="Upload">'+
    '</form>'
  );
}).listen(80);

You could then push status to a client using Socket.io

Interesting Note: This was one of the problems that led to Node's creation. In this video Ryan talks about how node was started trying to find the best way of notifying a user, real time, about the status of file upload over the web...anyway I digress, but the video is worth a watch if you are interested in Node's history

Hortinstein
  • 2,667
  • 1
  • 22
  • 22
  • 2
    Well that's for *receiving* a file. I'm *sending* a file for node to another server. – Gautham Badhrinathan Aug 24 '12 at 14:38
  • 8
    Just spent about 4 hours searching Google. Is it just me, or are there only like *five* people in the world who send files **from** node to an HTTP API? Feels like the consensus is that Node.js is only good for server code and useless for anything else. Sigh. – Sukima Jul 29 '14 at 02:14
  • 1
    It's not just you.. I am googling about this for hours and still haven't found any reasonable answer.. did you find anything? – Besat Aug 04 '16 at 13:40