2

I am using node.js to create file service for Azure File storage. I am using azure-storage-node (http://azure.github.io/azure-storage-node/) for this.

I am trying to download a file from Azure file storage. Below is my code snippet

// Download a file from Share
exports.get = function(request, response){
    var shareName = request.headers.sharename;
    var dirPath = request.headers.directorypath;
    var fileName = request.headers.filename;

    var fileService = azure.createFileService();

    var readStream = fileService.createReadStream(shareName, dirPath, fileName);

    var dataLength = 0;
    var body = '';
    readStream.on('data', function (chunk) {
    dataLength += chunk.length;
  })

    readStream.on('end', function(){
      console.log('The length was:', dataLength);
    });

    response.setHeader('Content-Type', 'application/json');
    response.send(statusCodes.OK, JSON.stringify("Success!"));

}

I able to get the stream of data. But how can I sent the stream in the response so that we can get it in the rest call.

I tried readStream.pipe(response); and

response.write(typeof chunk);
response.end() but it doesnt work; 

I am new to node.js. Please help me on this.

Updated:

I tried the following.

response.writeHead(200, {'Content-Type': 'application/json'});

var readStream = fileService.createReadStream(shareName, dirPath, fileName);
readStream.pipe(response);

But its throwing follwing error.

    ERROR
An unhandled exception occurred. Error: Can't set headers after they are sent.
    at ServerResponse.OutgoingMessage.setHeader (http.js:679:11)
    at ServerResponse.res.setHeader (D:\home\site\wwwroot\node_modules\express\node_modules\connect\lib\patch.js:59:22)
    at ServerResponse.res.set.res.header (D:\home\site\wwwroot\node_modules\express\lib\response.js:518:10)
    at addDefaultHeaders (D:\home\site\wwwroot\runtime\request\requesthandler.js:582:9)
    at ServerResponse.<anonymous> (D:\home\site\wwwroot\runtime\request\requesthandler.js:291:13)
    at ServerResponse._.wrap [as end] (D:\home\site\wwwroot\node_modules\underscore\underscore.js:692:22)
    at ChunkStream.onend (stream.js:66:10)
    at ChunkStream.EventEmitter.emit (events.js:126:20)
    at ChunkStream.end (D:\home\site\wwwroot\App_Data\config\scripts\node_modules\azure-storage\lib\common\streams\chunkstream.js:90:8)
    at Request.onend (stream.js:66:10)

The return datatype of fileService.createReadStream(shareName, dirPath, fileName); is ChunkStream

Updated:

This is my updated code which works.

    var option = new Object();
    option.disableContentMD5Validation = true;
    option.maximumExecutionTimeInMs = 20 * 60000;
    option.timeoutIntervalInMs = 20 * 6000;

    fileService.getFileToStream(shareName, dirPath, fileName, response, option, function(error, result, res) {
        if(!error) {
            if(res.isSuccessful) {
                console.log(result);
        console.log(res);
        console.log("Success!");
      }
        }
    });

But more frequently I am getting below error.

ERROR
An unhandled exception occurred. Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (http.js:679:11)
at ServerResponse.res.setHeader (D:\home\site\wwwroot\node_modules\express\node_modules\connect\lib\patch.js:59:22)
at ServerResponse.res.set.res.header (D:\home\site\wwwroot\node_modules\express\lib\response.js:518:10)
at addDefaultHeaders (D:\home\site\wwwroot\node_modules\azure-mobile-services\runtime\request\requesthandler.js:590:9)
at ServerResponse. (D:\home\site\wwwroot\node_modules\azure-mobile-services\runtime\request\requesthandler.js:299:13)
at ServerResponse._.wrap as end
at Request.onend (stream.js:66:10)
at Request.EventEmitter.emit (events.js:126:20)
at IncomingMessage.Request.onRequestResponse.strings (D:\home\site\wwwroot\App_Data\config\scripts\node_modules\azure-storage\node_modules\request\request.js:1153:12)
at IncomingMessage.EventEmitter.emit (events.js:126:20)
Sniper
  • 2,412
  • 11
  • 44
  • 49
  • try to put readStream.pipe(response); just after you declare readStream and remove everything else. also i believe you cant send content type application/json when you send a binary file (i am not sure on this). – Alex Michailidis Oct 19 '15 at 21:21
  • I tried ".pipe(response)". But its giving the error "An unhandled exception occurred. Error: Can't set headers after they are sent.". The output of the fileService.createReadStream is a ChunckStream. How can we pipe the chunk stream to response. – Sniper Oct 20 '15 at 05:18

2 Answers2

4

The NodeJS Class http.ServerResponse implements the Writable Stream interface, please refer to the NodeJS API https://nodejs.org/api/http.html#http_class_http_serverresponse and https://nodejs.org/api/stream.html#stream_class_stream_writable_1.

So you just need to use the object response instead of the stream writer fs.createStreamWriter(...) in the sample code "getFileToStream" http://azure.github.io/azure-storage-node/#toc8 when you send data stream into response for NodeJS.

This is my sample code as below:

var http = require('http');
var azure = require('azure-storage');
var fileService = azure.createFileService('<storage_key_name>','<storage_access_key>');

http.createServer(function (request, response) {
    var shareName = request.headers.sharename;
    var dirPath = request.headers.directorypath;
    var fileName = request.headers.filename;
    response.setHeader('Content-Type', 'application/json');
    fileService.getFileToStream(shareName, dirPath, fileName, response, {disableContentMD5Validation: true}, function(error, result, response) {
            if(!error) {
                    //console.log(result);
                    //console.log(response);
                   if(response.isSuccessful) {
                                console.log("Success!");
                   }
            }
    });
}).listen(1337, "127.0.0.1");

console.log('Server running at http://127.0.0.1:1337/');

Best Regards.


For getting File greater than 4MB from Azure File Storage, there is a request header x-ms-range-get-content-md5 that it will cause the status code 400(Bad Request) error, please refer to the Get File REST API doc of Azure File Storage https://msdn.microsoft.com/en-us/library/azure/dn194274.aspx, see below:

enter image description here

So I reviewed the source of Azure File Storage SDK for Node (https://github.com/Azure/azure-storage-node/blob/master/lib/services/file/fileservice.js). For the function getFileToText, getFileToLocalFile, createReadStream and getFileToStream, you need to set the options.disableContentMD5Validation attribute to avoid the error, see below.

  • @param {boolean} [options.disableContentMD5Validation] When set to true, MD5 validation will be disabled when downloading files.

And refer to the source of getFileToStream as example:

enter image description here

In my sample code, need to add the code {disableContentMD5Validation: true} as options at the front of invoking the function getFileToStream.

Peter Pan
  • 23,476
  • 4
  • 25
  • 43
  • Thanks a lot #Peter Pan. It works file for files below 4MB. For above 4MB it gives the error Connection reset at java.net.SocketInputStream.read(Unknown Source). What is the max size to upload and download using node.js file service? – Sniper Oct 27 '15 at 06:41
  • @Sniper Please refer to https://azure.microsoft.com/en-us/documentation/articles/azure-subscription-service-limits/#storage-limits. – Peter Pan Oct 27 '15 at 07:19
  • @Sniper For the error of donloading above 4MB, please see the post updated. – Peter Pan Oct 27 '15 at 07:48
  • Pan - I am frequently getting issue while downloading. I have updated the question. Please help me to fix it. – Sniper Feb 17 '16 at 06:07
  • @Sniper Hi, I'm pleasure for helping you. But the updated question was similar with the original. I don't see your newest code to help analysising issues. Could you create a new thread for the updated question and supply the related code, then paste the new thread url at here to notice me? – Peter Pan Feb 17 '16 at 07:05
1

You might want to try in this way:

exports.get = function(request, response) {
  var shareName = request.headers.sharename;
  var dirPath = request.headers.directorypath;
  var fileName = request.headers.filename;

  var fileService = azure.createFileService();
  var readStream = fileService.createReadStream(shareName, dirPath, fileName);    

  var dataLength = 0;
  readStream.on('data', function (chunk) {
    dataLength += chunk.length;
  })

  readStream.on('end', function(){
    response.setHeader('Content-Type', 'application/json');
    response.setHeader('Content-Length', dataLength);
  });

  readStream.pipe(response);

  response.on('finish', function (chunk) {
    response.send(statusCodes.OK, JSON.stringify("Success!"));
  })
}