Uploading a file in Azure File Storage using node.js Uploading a file in Azure File Storage using node.js azure azure

Uploading a file in Azure File Storage using node.js


There are several issue here. Let us go over them one by one.

1. In your Java client you cannot just dump the binary data into an Azure mobile service connection.

The reason for this is that an Azure mobile service has two body parsers that ensure that no matter what, the request body is parsed for you.So, while you can walk around the Express body parser by specifying an uncommon content type, you will still hit the Azure body parser that will mess up your data stream by naively assuming that it is a UTF-8 string.

The only option therefore is to skip the Express parser by specifying a content type it cannot handle and then play along with the Azure parser by encoding your binary data with Base64 encoding.

So, in the Java client replace

Path path = Paths.get("C:/Users/uma.maheshwaran/Desktop/Temp.txt");byte[] data = Files.readAllBytes(path);

with

con.setRequestProperty("content-type", "binary");    Path path = Paths.get("C:/Users/uma.maheshwaran/Desktop/Temp.txt");byte[] data = Files.readAllBytes(path);data = Base64.getEncoder().encode(data);

If you are not on Java 8, replace the java.util.Base64 encoder with any other Base64 encoder you have access to.

2. The createFileFromStream Azure storage api function you are trying to use expects a stream.

At the same time, the best you can get when parsing a request body manually is a byte array. Unfortunately, Azure mobile services use NodeJS version 0.8, which means there is no easy way to construct a readable stream from a byte array, and you you will have to assemble your own stream suitable for Azure storage api. Some duct tape and stream@0.0.1 should do just fine.

var base64 = require('base64-js'),    Stream = require('stream'),    fileService = require('azure-storage')        .createFileService('yourStorageAccount', 'yourStoragePassword');exports.post = function (req, res) {    var data = base64.toByteArray(req.body),        buffer = new Buffer(data),        stream = new Stream();        stream['_ended'] = false;        stream['pause'] = function() {            stream['_paused'] = true;        };        stream['resume'] = function() {            if(stream['_paused'] && !stream['_ended']) {                stream.emit('data', buffer);                stream['_ended'] = true;                stream.emit('end');            }        };     try {        fileService.createFileFromStream(req.headers.sharename, req.headers.directorypath,             req.headers.filename, stream, data.length, function (error, result, resp) {                res.statusCode = error ? 500 : 200;                res.end();            }        );    } catch (e) {        res.statusCode = 500;        res.end();    }};

These are the dependencies you need for this sample.

"dependencies": {       "azure-storage": "^0.7.0",    "base64-js": "^0.0.8",    "stream": "0.0.1"}

If specifying them in your service's package.json does not work you can always go to this link and install them manually via the console.

cd site\wwwrootnpm install azure-storagenpm install base64-jsnpm install stream@0.0.1

3. To increase the default upload limit of 1Mb, specify MS_MaxRequestBodySizeKB for your service.

MS_MaxRequestBodySizeKB

Do keep in mind though that since you are transferring you data as Base64-encoded you have to account for this overhead. So, to support uploading files up to 20Mb in size, you have to set MS_MaxRequestBodySizeKB to roughly 20 * 1024 * 4 / 3 = 27307.


I find the easiest way is to use pkgcloud which abstracts the differences between cloud providers and also provides a clean interface for uploading and downloading files. It uses streams so the implementation is memory efficient as well.

var pkgcloud = require('pkgcloud')var fs = require('fs')var client = pkgcloud.storage.createClient({  provider: 'azure',  storageAccount: 'your-storage-account',  storageAccessKey: 'your-access-key'});var readStream = fs.createReadStream('a-file.txt');var writeStream = client.upload({  container: 'your-storage-container',  remote: 'remote-file-name.txt'});writeStream.on('error', function (err) {  // handle your error case});writeStream.on('success', function (file) {  // success, file will be a File model});readStream.pipe(writeStream);


We can leverage this answer of the thread on SO How to send an image from Android client to Node.js server via HttpUrlConnection?, which create a custom middleware to get the upload file content into a buffer array, then we can use createFileFromText() to store the file in Azure Storage.

Here is the code snippet:

function rawBody(req, res, next) {    var chunks = [];    req.on('data', function (chunk) {        chunks.push(chunk);    });    req.on('end', function () {        var buffer = Buffer.concat(chunks);        req.bodyLength = buffer.length;        req.rawBody = buffer;        next();    });    req.on('error', function (err) {        console.log(err);        res.status(500);    });}router.post('/upload', rawBody,function (req, res){    fileService.createShareIfNotExists('taskshare', function (error, result, response) {        if (!error) {            // if result = true, share was created.            // if result = false, share already existed.            fileService.createDirectoryIfNotExists('taskshare', 'taskdirectory', function (error, result, response) {                if (!error) {                    // if result = true, share was created.                    // if result = false, share already existed.                    try {                        fileService.createFileFromText('taskshare', 'taskdirectory', 'test.txt', req.rawBody, function (error, result, resp) {                            if (!error) {                                // file uploaded                                res.send(200, "File Uploaded");                            } else {                                res.send(200, "Error!");                            }                        });                    } catch (ex) {                        res.send(500, { error: ex.message });                    }                }            });        }    });})router.get('/getfile', function (req, res){    fileService.createReadStream('taskshare', 'taskdirectory', 'test.txt').pipe(res);})