1

We are try to create a service using node.js in Azure to download. We are sending writeStream in response.

Updated:

var option = new Object();
option.disableContentMD5Validation = true;
option.maximumExecutionTimeInMs = 20 * 60000;
fileService.getFileToStream(shareName, dirPath, fileName, response, option, function (error, result, response) {
    if(!error) {
        if(response.isSuccessful) {
      console.log("Success!");
  }
    }
});

While downloading files less than 4MB its working fine. But while downloading more than 4MB its giving error.

java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at sun.security.ssl.InputRecord.readFully(Unknown Source)
at sun.security.ssl.InputRecord.read(Unknown Source)
at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
at sun.security.ssl.SSLSocketImpl.readDataRecord(Unknown Source)
at sun.security.ssl.AppInputStream.read(Unknown Source)
at java.io.BufferedInputStream.fill(Unknown Source)
at java.io.BufferedInputStream.read1(Unknown Source)
at java.io.BufferedInputStream.read(Unknown Source)
at sun.net.www.MeteredStream.read(Unknown Source)
at java.io.FilterInputStream.read(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(Unknown Source)
at FileserviceTest.sendGET(FileserviceTest.java:58)
at FileserviceTest.main(FileserviceTest.java:18)

Below is the sample java client code.

    public static void sendGET() throws IOException {
    FileOutputStream fos = null;

    URL obj = new URL("https://crowdtest-fileservice.azure-mobile.net/api/files/");
    HttpURLConnection con = (HttpURLConnection) obj.openConnection();
    con.setRequestMethod("GET");
    con.setRequestProperty("sharename", "newamactashare");
    con.setRequestProperty("directorypath", "MaheshApp/TestLibrary/");
    con.setRequestProperty("filename", "Test.apk");

    int responseCode = con.getResponseCode();
    System.out.println("GET Response Code :: " + responseCode);
    if (responseCode == HttpURLConnection.HTTP_OK) { // success
        fos = new FileOutputStream("C:/Users/uma.maheshwaran/Desktop/Test.mt");
        InputStream iin = con.getInputStream();

        byte[] buffer = new byte[4096]; // declare 4KB buffer
        int len;

        // while we have availble data, continue downloading and storing to
        // local file
        while ((len = iin.read(buffer)) > 0) {
            fos.write(buffer, 0, len);
        }
        iin.close();
        fos.close();

        // print result
        System.out.println("Done");
    } else {
        BufferedReader br = new BufferedReader(new InputStreamReader(con.getErrorStream()));
        String line = "";
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
        System.out.println("GET request not worked");
    }

}

Can we convert write stream to buffer stream. If so how can we send that in response. Or is there any other way to send a large stream of data in response. Please help me on this. I am new to node.js.

Sniper
  • 2,412
  • 11
  • 44
  • 49

1 Answers1

3

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 the NodeJS code, you need to add the code response.disableContentMD5Validation = true; at the front of invoking the function getFileToStream.

Updated Code

// this sentence must be removed in Azure Mobile Service, it cause the error.
// response.setHeader('Content-Type', 'application/json');
// add the argument {disableContentMD5Validation: true}
fileService.getFileToStream(shareName, dirPath, fileName, response, {disableContentMD5Validation: true}, function (error, result, response) {
    if(!error) {
        if(response.isSuccessful) {
           console.log("Success!");
        }
    }
});

Updated 2015-10-29: For Java Code:

The issue of Java exception is not related to Mobile Service Code. It was caused by missing a request header property called X-ZUMO-APPLICATION as below.

con.addRequestProperty("X-ZUMO-APPLICATION", "<Manage Access Key>");

You can find the key <Manage Access Key> at the bottom of Mobile Service Dashboard on Azure Portal, as the snapshot below:

enter image description here

Click the MANAGE KEYS button, you can see two keys as below:

enter image description here

Add the any one into the code, then the code works fine.


Server sample code in NodeJS for Azure Mobile Service:

exports.get = function(request, response) {
    var azure = require('azure-storage');
    var fileService = azure.createFileService('<storage_account_name>','<storage_access_key>');
    fileService.getFileToStream('taskshare', 'taskdirectory', 'taskfile', response,{disableContentMD5Validation:true}, function(error, result, res) {
                if(!error) {
                  console.log(result);
                  console.log(res);
                }
    });
};

Client sample code in Java:

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import javax.net.ssl.HttpsURLConnection;

import org.apache.commons.io.IOUtils;

public class MobileSample {

    public static void main(String[] args) throws IOException {
        URL url = new URL("https://<mobile_service_name>.azure-mobile.net/api/<api_name: example 'filestorage'>");
        HttpURLConnection https = (HttpURLConnection) url.openConnection();
        https.addRequestProperty("X-ZUMO-APPLICATION", "<manage_access_key>");
        https.connect();
        int respCode = https.getResponseCode();
        System.out.println(respCode);
        InputStream is = https.getInputStream();
        IOUtils.copy(is, new FileOutputStream("<file_name>"));
        is.close();
    }
}

Please check the permission of the api in Azure Portal, see the picture below:

enter image description here

Best Regards.

Peter Pan
  • 23,476
  • 4
  • 25
  • 43
  • 1
    Thanks @Peter Pan. I tried adding the above code in both the node.js and java codes. Still giving the same error. In the Azure mobile service log we got an error after adding this. "An unhandled exception occurred. Error: Can't set headers after they are sent.". Please check the updated question for the detailed error. – Sniper Oct 27 '15 at 10:32
  • @Sniper Sorry for my mistake of function arguments. I modified my code and tested it successfully on Azure Mobile Service. Please see my updated code. – Peter Pan Oct 28 '15 at 09:04
  • 1
    @ Peter Pan - I tried the updated code but still I am facing the same "Connection reset - at java.net.SocketInputStream.read(Unknown Source)" issue. I also tried to add maximumExecutionTimeInMs = 20 * 60000 in the option but no luck. Please check my updated code that I tried. – Sniper Oct 28 '15 at 10:28
  • @Sniper The issue is not related to Mobile Service Code. It was caused by missing a request header property called `X-ZUMO-APPLICATION`. Please see the details at the updated post. And I think you need to recovery the complete content of the issue to help other people met the same problem. Thanks. – Peter Pan Oct 29 '15 at 04:54
  • 1
    Pan. Sure I will do that. I tried adding "x-zumo-application" and "x-zumo-master" in the request header, but still facing the same issue. Do we have to handle that headers in our node.js code or server will take care of it? Is there any specific clear documentations for fileservice using node.js. Or can you please provide us a sample client and server code which is working for? – Sniper Oct 29 '15 at 11:22
  • @Sniper OK, please see the post updated of my sample client in Java and server in NodeJS. They are working. – Peter Pan Oct 30 '15 at 01:33
  • @ Peter Pan. Its working fine if the network speed is high. But if the speed is bit slow (ex: 2G) is giving the same error. I tried adding "option.maximumExecutionTimeInMs = 20 * 60000;" in the option object. But no luck. Please check my updated code. Is there any way to increase the time out? – Sniper Nov 05 '15 at 10:22
  • @Sniper I think you also need to set the client timeout thru HttpConnection code `con.setConnectTimeout(1000*30); //set timeout to 30 seconds` on the Java side. – Peter Pan Nov 06 '15 at 09:45
  • Pan. Thank you for the great support. I have tried adding timeout in both client and server side but no luck. We are facing the same issue. Please help us on this. – Sniper Nov 18 '15 at 10:21
  • @Sniper What's the same issue? I didn't get any exception even without timout settings in both side. Could you supply more details? I'm pleasure for helping you. – Peter Pan Nov 19 '15 at 07:59
  • 1
    Pan Its giving the error "java.net.SocketException: Connection reset" if the network is slow while downloading. – Sniper Nov 19 '15 at 09:13
  • @Sniper It's possible for unstable network. As references, there are two page explain some reasons http://stackoverflow.com/questions/62929/java-net-socketexception-connection-reset and http://www.minecraftforum.net/forums/support/unmodified-minecraft-client/tutorials-and-faqs/1871643-how-to-fix-internal-exception-java-net. – Peter Pan Nov 19 '15 at 09:32