skipper-azure
Version:
A Skipper adapter for Microsoft Azure storage.
153 lines (119 loc) • 4.33 kB
JavaScript
/**
*
* Author: Lukas Reichart on 3/9/15.
* Purpose: Skipper adapter ( used by the sails.js framework )
* License: MIT
* Copyright Lukas Reichart @Antum 2015
*/
var path = require('path');
var Writable = require('stream').Writable;
var concat = require('concat-stream');
var azure = require( 'azure-storage');
var _ = require( 'lodash' );
var mime = require( 'mime' );
module.exports = function SkipperAzure( globalOptions ) {
globalOptions = globalOptions || {};
var blobService = azure.createBlobService( globalOptions.key,
globalOptions.secret );
var adapter = {
read: function( fd, cb ) {
var prefix = fd;
var res = blobService.createReadStream( globalOptions.container, prefix, function( err ) {
if ( err ) {
cb( err );
}
});
res.pipe(concat(function (data) {
return cb(null, data);
}));
},
rm: function( fd, cb ) {
blobService.deleteBlobIfExists( globalOptions.container, fd, function( err, result, response ){
if( err ) {
return cb( err );
}
// construct response
cb( null, {
filename: fd,
success: true,
extra: response
});
});
},
ls: function( dirname, cb ) {
if ( !dirname ) {
dirname = '/';
}
var prefix = dirname;
blobService.listBlobsSegmentedWithPrefix( globalOptions.container, prefix,
null, function( err, result, response ) {
if( err ) {
return cb( err );
}
var data = _.pluck( result.entries, 'name');
data = _.map(data, function snipPathPrefixes (thisPath) {
thisPath = thisPath.replace(/^.*[\/]([^\/]*)$/, '$1');
// Join the dirname with the filename
thisPath = path.join(dirname, path.basename(thisPath));
return thisPath;
});
cb( null, data );
})
},
receive: AzureReceiver
};
return adapter;
/**
* A simple receiver for Skipper that writes Upstreams to Azure Blob Storage
* to the configured container at the configured path.
*
* @param {Object} options
* @returns {Stream.Writable}
*/
function AzureReceiver( options ) {
options = options || {};
options = _.defaults( options, globalOptions );
var receiver = Writable({
objectMode: true
});
receiver.once( 'error', function( err ) {
console.log( 'ERROR ON RECEIVER :: ', err );
});
receiver._write = function onFile( newFile, encoding, done ) {
var startedAt = new Date();
newFile.once( 'error', function( err ) {
console.log( ('ERROR ON file read stream in receiver (%s) :: ', newFile.filename, err ).red );
});
var headers = options.headers || {};
// Lookup content type with mime if not set
if ( typeof headers['content-type'] === 'undefined' ) {
headers['content-type'] = mime.lookup( newFile.fd );
}
var uploadOptions = {
contentType: headers['content-type']
};
// TODO: only used for the waterline-adapter-tests, because they do not set the byteCount attribute
// checkout the issue on: https://github.com/lukasreichart/skipper-azure/pull/2
if( !newFile.byteCount ){
newFile.byteCount = newFile._readableState.length;
}
var uploader = blobService.createBlockBlobFromStream( options.container,
newFile.fd, newFile, newFile.byteCount, uploadOptions, function( err, result, response ) {
if( err ) {
console.log( ('Receiver: Error writing ' + newFile.filename + ' :: Cancelling upload and cleaning up already-written bytes ... ' ).red );
receiver.emit( 'error', err );
return;
}
newFile.extra = response;
newFile.size = new Number( newFile.size );
var endedAt = new Date();
var duration = ( endedAt - startedAt ) / 1000;
//console.log( 'UPLOAD took ' + duration + ' seconds .. ' );
// TODO ?? is this line necessary: skipper-s3/index.js line: 254 does not use it. But skipper-adapter-tests do not work without this line.
receiver.emit( 'finish', err, result, response );
done();
});
};
return receiver;
}
};