Search code examples
node.jsazureazure-storageazure-mobile-servicesnode.js-stream

Sending stream of data in response - node.js - Azure File service using node.js


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)

Solution

  • 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.