azure-storage
Advanced tools
Comparing version 0.2.1 to 0.3.0
Note: This is an Azure Storage only package. The all up Azure node sdk still has the old storage bits in there. In a future release, those storage bits will be removed and an npm dependency to this storage node sdk will | ||
be taken. This is a CTP v1 release and the changes described below indicate the changes from the Azure node SDK 0.9.8 available here - https://github.com/Azure/azure-sdk-for-node. | ||
2014.07.07 Version 0.3.0 | ||
BLOB | ||
* Fixed a bug which failed to return single item blocklists while doing listBlocks. | ||
FILE | ||
* Added File Service support. The File Service and the associated APIs are in preview. | ||
2014.07.01 Version 0.2.1 | ||
@@ -5,0 +13,0 @@ |
@@ -118,3 +118,3 @@ // | ||
blobService.createBlockBlobFromFile(containerName, blobName, file, function (error) { | ||
blobService.createBlockBlobFromLocalFile(containerName, blobName, file, function (error) { | ||
finished++; | ||
@@ -159,3 +159,3 @@ | ||
blobs.forEach(function (blob) { | ||
blobService.getBlobToFile(containerName, blob.name, destinationDirectoryPath + '/' + blob.name, function (error2) { | ||
blobService.getBlobToLocalFile(containerName, blob.name, destinationDirectoryPath + '/' + blob.name, function (error2) { | ||
blobsDownloaded++; | ||
@@ -162,0 +162,0 @@ |
@@ -125,2 +125,26 @@ // | ||
/** | ||
* File client exports. | ||
* @ignore | ||
*/ | ||
var FileService = require('./services/file/fileservice'); | ||
exports.FileService = FileService; | ||
exports.FileUtilities = require('./services/file/fileutilities'); | ||
/** | ||
* Creates a new {@link FileService} object. | ||
* If no storageaccount or storageaccesskey are provided, the AZURE_STORAGE_CONNECTION_STRING and then the AZURE_STORAGE_ACCOUNT and AZURE_STORAGE_ACCESS_KEY | ||
* environment variables will be used. | ||
* | ||
* @param {string} storageAccountOrConnectionString The storage account or the connection string. | ||
* @param {string} [storageAccessKey] The storage access key. | ||
* @param {string|object} [host] The host address. To define primary only, pass a string. | ||
* Otherwise 'host.primaryHost' defines the primary host and 'host.secondaryHost' defines the secondary host. | ||
* @return {FileService} A new FileService object. | ||
*/ | ||
exports.createFileService = function (storageAccountOrConnectionString, storageAccessKey, host) { | ||
return new FileService(storageAccountOrConnectionString, storageAccessKey, host); | ||
}; | ||
/** | ||
* Queue client exports. | ||
@@ -127,0 +151,0 @@ * @ignore |
@@ -52,2 +52,7 @@ // | ||
var fileEndpointSetting = ServiceSettings.settingWithFunc( | ||
ConnectionStringKeys.FILE_ENDPOINT_NAME, | ||
Validate.isValidHost | ||
); | ||
var validKeys = [ | ||
@@ -62,3 +67,4 @@ ConnectionStringKeys.USE_DEVELOPMENT_STORAGE_NAME, | ||
ConnectionStringKeys.QUEUE_ENDPOINT_NAME, | ||
ConnectionStringKeys.TABLE_ENDPOINT_NAME | ||
ConnectionStringKeys.TABLE_ENDPOINT_NAME, | ||
ConnectionStringKeys.FILE_ENDPOINT_NAME | ||
]; | ||
@@ -75,5 +81,6 @@ | ||
* @param {string} tableEndpoint The storage service table endpoint. | ||
* @param {string} fileEndpoint The storage service file endpoint. | ||
* @param {bool} usePathStyleUri Boolean value indicating wether to use path style uri or not. | ||
*/ | ||
function StorageServiceSettings(name, key, sasToken, blobEndpoint, queueEndpoint, tableEndpoint, usePathStyleUri) { | ||
function StorageServiceSettings(name, key, sasToken, blobEndpoint, queueEndpoint, tableEndpoint, fileEndpoint, usePathStyleUri) { | ||
this._name = name; | ||
@@ -86,2 +93,3 @@ this._key = key; | ||
this._tableEndpoint = tableEndpoint; | ||
this._fileEndpoint = fileEndpoint; | ||
@@ -131,2 +139,3 @@ if (usePathStyleUri) { | ||
addIfNotNullOrEmpty('queueendpoint', host); | ||
addIfNotNullOrEmpty('fileendpoint', host); | ||
} else { | ||
@@ -197,3 +206,4 @@ addIfNotNullOrEmpty('defaultendpointsprotocol', ServiceSettings.DEFAULT_PROTOCOL.split(':', 1)[0]); | ||
queueEndpointSetting, | ||
tableEndpointSetting | ||
tableEndpointSetting, | ||
fileEndpointSetting | ||
) | ||
@@ -216,3 +226,4 @@ ); | ||
queueEndpointSetting, | ||
tableEndpointSetting | ||
tableEndpointSetting, | ||
fileEndpointSetting | ||
) | ||
@@ -234,3 +245,4 @@ ); | ||
queueEndpointSetting, | ||
tableEndpointSetting | ||
tableEndpointSetting, | ||
fileEndpointSetting | ||
) | ||
@@ -251,3 +263,3 @@ ); | ||
ServiceSettings.optional( | ||
blobEndpointSetting, | ||
fileEndpointSetting, | ||
queueEndpointSetting, | ||
@@ -304,2 +316,3 @@ tableEndpointSetting | ||
tableEndpoint, | ||
null, | ||
true | ||
@@ -317,2 +330,3 @@ ); | ||
* @param {string} tableEndpointUri The table endpoint uri. | ||
* @param {string} fileEndpointUri The file endpoint uri. | ||
* @return {StorageServiceSettings} | ||
@@ -376,3 +390,9 @@ */ | ||
var fileEndpoint = standardizeHost( | ||
util.tryGetValueInsensitive(ConnectionStringKeys.FILE_ENDPOINT_NAME, settings), | ||
accountName, | ||
scheme, | ||
StorageServiceClientConstants.CLOUD_FILE_HOST); | ||
return new StorageServiceSettings( | ||
@@ -384,3 +404,4 @@ accountName, | ||
queueEndpoint, | ||
tableEndpoint | ||
tableEndpoint, | ||
fileEndpoint | ||
); | ||
@@ -387,0 +408,0 @@ }; |
@@ -35,9 +35,3 @@ // | ||
/** | ||
* Concurrent execute batch operation and call operation callback randomly or in sequence. | ||
* Random mode is for uploading. | ||
* 1. Fire user callback when the operation is done. | ||
* Sequence mode is for downloading. | ||
* 1. Fire user callback when the operation is done and all previous operation and callback has finished. | ||
* 2. BatchOperation guarantee the user callback is fired one by one. | ||
* 3. The next user callback can't be fired util the current one complete. | ||
* Concurrently execute batch operations. | ||
*/ | ||
@@ -52,18 +46,20 @@ function BatchOperation(name, options) { | ||
this.operationMemoryUsage = options.operationMemoryUsage || DEFAULT_OPERATION_MEMORY_USAGE; | ||
this.callbackInorder = options.callbackInorder === true; | ||
this._currentOperationId = this.callbackInorder ? 1 : -1; | ||
this.concurrency = DEFAULT_GLOBAL_CONCURRENCY; | ||
this._emitter = new EventEmitter(); | ||
this._enableComplete = false; | ||
this._ended = false; | ||
this._error = null; | ||
//Total operations count(queued and active and connected) | ||
this._totalOperation = 0; | ||
//Action operations count(The operations which are connecting to remote or executing callback or queued for executing) | ||
this._activeOperation = 0; | ||
//Queued operations count(The operations which are connecting to remote or queued for executing) | ||
this._queuedOperation = 0; | ||
//finished operation should be removed from this array | ||
this._operations = []; | ||
this._emitter = null; | ||
this._enableComplete = false; | ||
this._ended = false; | ||
this._error = null; | ||
this._paused = false; | ||
} | ||
@@ -78,3 +74,2 @@ | ||
RUNNING : 'running', | ||
COMPLETE : 'complete', | ||
CALLBACK : 'callback', | ||
@@ -128,3 +123,3 @@ ERROR : 'error' | ||
operation.status = OperationState.QUEUED; | ||
operation.operationId = this._getActiveOperationId(); | ||
operation.operationId = ++this._totalOperation; | ||
this._queuedOperation++; | ||
@@ -147,23 +142,6 @@ this.logger.debug(util.format('Add operation %d into batch operation %s.', operation.operationId, this.name)); | ||
/** | ||
* Stop firing user call back | ||
*/ | ||
BatchOperation.prototype.pause = function() { | ||
this._paused = true; | ||
}; | ||
/** | ||
* Start firing user call back | ||
*/ | ||
BatchOperation.prototype.resume = function() { | ||
if (this._paused) { | ||
this._paused = false; | ||
this._fireOperationUserCallback(); | ||
} | ||
}; | ||
/** | ||
* Add event listener | ||
*/ | ||
BatchOperation.prototype.on = function(event, listener) { | ||
if(!this._emitter) this._emitter = new EventEmitter(); | ||
// only emit end if the batch has completed all operations | ||
if(this._ended && event === 'end') { | ||
@@ -201,27 +179,14 @@ listener(); | ||
if (error) { | ||
operation.status = OperationState.ERROR; | ||
self.logger.debug(util.format('Operation %d failed. Error %s', operation.operationId, error)); | ||
//Abort the batch operation if one of them failed | ||
self.abort(error); | ||
this._error = error; | ||
} else { | ||
operation.status = OperationState.CALLBACK; | ||
self.logger.debug(util.format('Operation %d succeed', operation.operationId)); | ||
} | ||
operation._callbackArguments = arguments; | ||
if (self._paused) { | ||
operation.status = OperationState.CALLBACK; | ||
self.logger.debug(util.format('Batch operation pasued and Operation %d wait for firing callback', operation.operationId)); | ||
} else if (self.callbackInorder) { | ||
if(self._currentOperationId === operation.operationId) { | ||
self._fireOperationUserCallback(operation); | ||
} else if (self._currentOperationId > operation.operationId) { | ||
throw new Error('Debug error: current callback operation id can\'t larger than oepration id'); | ||
} else { | ||
operation.status = OperationState.CALLBACK; | ||
self.logger.debug(util.format('Operation %d wait for firing callback %s', operation.operationId, self._currentOperationId)); | ||
} | ||
} else { | ||
self._fireOperationUserCallback(operation); | ||
} | ||
self._fireOperationUserCallback(operation); | ||
self._tryEmitDrainEvent(); | ||
operation = null; | ||
self = null; | ||
}; | ||
@@ -231,44 +196,18 @@ }; | ||
/** | ||
* Abort all the operation | ||
*/ | ||
BatchOperation.prototype.abort = function (abortError) { | ||
if (abortError) { | ||
//Can't really abort all operations now. | ||
this._error = abortError; | ||
} | ||
}; | ||
/** | ||
* Fire user's call back | ||
*/ | ||
BatchOperation.prototype._fireOperationUserCallback = function (operation) { | ||
var index = -1; | ||
if (operation) { | ||
index = this._operations.indexOf(operation); | ||
} else if (this.callbackInorder) { | ||
index = this._getCurrentOperationIndex(); | ||
} | ||
if(index != -1 && !this._paused) { | ||
operation = this._operations[index]; | ||
if(operation._userCallback) { | ||
this.logger.debug(util.format('Fire user call back for operation %d', operation.operationId)); | ||
//Make sure UserCallback is a sync operation in sequence mode. | ||
//Both async and sync operations are available for random mode. | ||
operation._fireUserCallback(); | ||
} | ||
this._operations.splice(index, 1); | ||
this._activeOperation--; | ||
operation.status = OperationState.COMPLETE; | ||
index = operation = null; | ||
if(this._operations.length === 0) { | ||
// Emit end event with callbackInorder | ||
this._tryEmitEndEvent(); | ||
} | ||
if(this.callbackInorder) { | ||
this._currentOperationId++; | ||
} | ||
this._fireOperationUserCallback(); | ||
} else { | ||
this._tryEmitDrainEvent(); | ||
} | ||
// fire the callback, if exists | ||
if(operation._userCallback) { | ||
this.logger.debug(util.format('Fire user call back for operation %d', operation.operationId)); | ||
operation._fireUserCallback(); | ||
} | ||
// remove the operation from the array and decrement the counter | ||
var index = this._operations.indexOf(operation); | ||
this._operations.splice(index, 1); | ||
this._activeOperation--; | ||
// check if batch has ended and if so emit end event | ||
this._tryEmitEndEvent(); | ||
}; | ||
@@ -281,9 +220,6 @@ | ||
BatchOperation.prototype._tryEmitEndEvent = function () { | ||
if(this._enableComplete && this._activeOperation === 0 && this._operations.length === 0) { | ||
if(this._enableComplete && this._activeOperation === 0 && this._operations.length === 0) { | ||
this._ended = true; | ||
if(this._emitter) { | ||
this.logger.debug(util.format('Batch operation %s emit the end event', this.name)); | ||
var retValue = null; | ||
this._emitter.emit('end', this._error, retValue); | ||
} | ||
this.logger.debug(util.format('Batch operation %s emit the end event', this.name)); | ||
this._emitter.emit('end', this._error, null); | ||
return true; | ||
@@ -298,3 +234,2 @@ } | ||
BatchOperation.prototype._tryEmitDrainEvent = function () { | ||
if(!this._emitter) return false; | ||
if(!this.IsWorkloadHeavy() || this._activeOperation < this.concurrency) { | ||
@@ -308,40 +243,2 @@ this._emitter.emit('drain'); | ||
/** | ||
* Get the current active operation index. | ||
* Only the active operation could call user's callback in sequence model. | ||
* The other finished but not active operations should wait for wake up. | ||
*/ | ||
BatchOperation.prototype._getCurrentOperationIndex = function () { | ||
var operation = null; | ||
for(var i = 0, len = this._operations.length; i < len; i++) { | ||
operation = this._operations[i]; | ||
if (this.callbackInorder) { | ||
//Sequence mode | ||
if(operation.operationId == this._currentOperationId) { | ||
if (operation.status === OperationState.CALLBACK) { | ||
return i; | ||
} else if (operation.status === OperationState.COMPLETE) { | ||
this._currentOperationId ++; | ||
return this._getCurrentOperationIndex(); | ||
} else { | ||
break; | ||
} | ||
} | ||
} else { | ||
//Random mode | ||
if (operation.status === OperationState.CALLBACK) { | ||
return i; | ||
} | ||
} | ||
} | ||
return -1; | ||
}; | ||
/** | ||
* Get an operation id | ||
*/ | ||
BatchOperation.prototype._getActiveOperationId = function() { | ||
return ++this._totalOperation; | ||
}; | ||
/** | ||
* Rest operation in sdk | ||
@@ -352,4 +249,6 @@ */ | ||
this.operationId = -1; | ||
this._callbackArguments = null; | ||
// setup callback and args | ||
this._userCallback = arguments[arguments.length - 1]; | ||
this._callbackArguments = null; | ||
var sliceEnd = arguments.length; | ||
@@ -362,3 +261,3 @@ if(azureutil.objectIsFunction(this._userCallback)) { | ||
var operationArguments = Array.prototype.slice.call(arguments).slice(2, sliceEnd); | ||
sliceEnd = null; | ||
this.run = function(cb) { | ||
@@ -373,3 +272,3 @@ var func = serviceClient[operation]; | ||
func.apply(serviceClient, operationArguments); | ||
operationArguments = serviceClient = operation = null; | ||
operationArguments = operation = null; | ||
} | ||
@@ -382,3 +281,2 @@ }; | ||
} | ||
this._userCallback = this._callbackArguments = null; | ||
}; | ||
@@ -389,44 +287,2 @@ } | ||
/** | ||
* Common operation wrapper | ||
*/ | ||
function CommonOperation(operationFunc, callback) { | ||
this.status = OperationState.Inited; | ||
this.operationId = -1; | ||
this._callbackArguments = null; | ||
var sliceStart = 2; | ||
if(azureutil.objectIsFunction(callback)) { | ||
this._userCallback = callback; | ||
} else { | ||
this._userCallback = null; | ||
sliceStart = 1; | ||
} | ||
var operationArguments = Array.prototype.slice.call(arguments).slice(sliceStart); | ||
this.run = function(cb) { | ||
if(!cb) cb = this._userCallback; | ||
operationArguments.push(cb); | ||
this.status = OperationState.RUNNING; | ||
operationFunc.apply(null, operationArguments); | ||
operationArguments = operationFunc = null; | ||
}; | ||
this._fireUserCallback = function () { | ||
if(this._userCallback) { | ||
this._userCallback.apply(null, this._callbackArguments); | ||
} | ||
this._userCallback = this._callbackArguments = null; | ||
}; | ||
} | ||
BatchOperation.CommonOperation = CommonOperation; | ||
/** | ||
* Do nothing and directly call the callback. | ||
* In random mode, the user callback will be called immediately | ||
* In sequence mode, the user callback will be called after the previous callback has been called | ||
*/ | ||
BatchOperation.noOperation = function(cb) { | ||
cb(); | ||
}; | ||
module.exports = BatchOperation; |
@@ -22,8 +22,13 @@ // | ||
function ChunkAllocator(chunkSize, maxCount) { | ||
// Track the unused buffers and number of used buffers | ||
this._pool = []; | ||
this._requests = []; | ||
//There is no need to track all allocated buffer for now. | ||
this._inuse = 0; | ||
// Buffer size | ||
this._chunkSize = chunkSize; | ||
// If total memory is larger than this._chunkSize * this._maxCount, the buffer pool is not used. | ||
this._maxCount = maxCount || 10; | ||
// Immediately add a buffer to the pool. | ||
this._extendMemoryPool(); | ||
@@ -33,28 +38,13 @@ } | ||
/** | ||
* Asynchronously require a buffer. | ||
* Caller should be aware of that the content of buffer is random since the Buffer.fill is Time-consumed opreation. | ||
* @param {function(error, buffer)} callback | ||
*/ | ||
ChunkAllocator.prototype.getBuffer = function(size, callback) { | ||
var buffer = this._getBufferFromPool(size); | ||
if (buffer) { | ||
//There is no need to run this callback in nextTick since it's an internal api for now. | ||
//TODO do some benchmarkes about nextTick; | ||
this._inuse++; | ||
callback(null, buffer); | ||
} else { | ||
this._requests.push(callback); | ||
} | ||
}; | ||
/** | ||
* Synchronously require a buffer | ||
* Caller should be aware of that the content of buffer is random since the Buffer.fill is Time-consumed opreation. | ||
*/ | ||
ChunkAllocator.prototype.getBufferSync = function(size) { | ||
ChunkAllocator.prototype.getBuffer = function(size) { | ||
var buffer = this._getBufferFromPool(size); | ||
if (buffer === null) { | ||
//It means the total memory could larger than this._chunkSize * this._maxCount. | ||
buffer = this._allocateBuffer(size); | ||
// Either the total memory is larger than this._chunkSize * this._maxCount | ||
// Or, the size does not match the chunk size of the pool | ||
buffer = new Buffer(size); | ||
} | ||
this._inuse++; | ||
@@ -68,9 +58,13 @@ return buffer; | ||
ChunkAllocator.prototype._getBufferFromPool = function(size) { | ||
// Return null if the given size does not match the chunk size of the buffer pool. | ||
if(size !== this._chunkSize) { | ||
//Create a buffer outside of the memory pool. | ||
return this._allocateBuffer(size); | ||
} else if(this._pool.length === 0) { | ||
return null; | ||
} | ||
// Extend the memory pool if it is empty. | ||
if(this._pool.length === 0) { | ||
this._extendMemoryPool(); | ||
} | ||
// If the pool is not empty, return a buffer. | ||
if(this._pool.length !== 0) { | ||
@@ -84,21 +78,17 @@ return this._pool.pop(); | ||
/** | ||
* Extend the memory pool | ||
* Extend the memory pool if the maximum size has not been reached. | ||
*/ | ||
ChunkAllocator.prototype._extendMemoryPool = function() { | ||
var total = this._pool.length + this._inuse; | ||
if(total >= this._maxCount || total < 0) return; | ||
var nextSize = Math.min(total * 2, this._maxCount); | ||
if(nextSize === 0) { | ||
nextSize = 1; | ||
} | ||
// If the total is larger than the max, do not allocate more memory. | ||
if(total >= this._maxCount) return; | ||
// Calculate the new number of buffers, equal to the total*2 bounded by 1 and the maxCount | ||
var nextSize = Math.min(total * 2, this._maxCount) || 1; | ||
// Add more buffers. | ||
var increment = nextSize - total; | ||
this._addChunk(increment); | ||
}; | ||
/** | ||
* Add more chunks into memory pool | ||
*/ | ||
ChunkAllocator.prototype._addChunk = function(increment) { | ||
for(var i = 0; i < increment; i++) { | ||
var buffer = this._allocateBuffer(this._chunkSize); | ||
var buffer = new Buffer(this._chunkSize); | ||
this._pool.push(buffer); | ||
@@ -109,7 +99,7 @@ } | ||
/** | ||
* Release buffer | ||
* Release the buffer. | ||
*/ | ||
ChunkAllocator.prototype.releaseBuffer = function(buffer) { | ||
if(buffer.length !== this._chunkSize) { | ||
//Directly delete the buffer if bufferSize is invalid and wait for GC. | ||
// Directly delete the buffer if bufferSize is invalid and wait for GC. | ||
buffer = null; | ||
@@ -119,16 +109,13 @@ return; | ||
if (this._requests.length) { | ||
//Wake up the buffer request. | ||
var bufferRequest = this._requests.shift(); | ||
bufferRequest(null, buffer); | ||
return; | ||
} | ||
// Add the buffer to the pool if it is not full, otherwise delete it | ||
if (this._pool.length < this._maxCount) { | ||
this._pool.push(buffer); | ||
} else { | ||
//The pool is full and wait for GC | ||
buffer = null; | ||
} | ||
// Decrement _inuse | ||
this._inuse--; | ||
// _inuse could be below zero if a buffer is released which was not returned by getBuffer | ||
if(this._inuse < 0) { | ||
@@ -139,10 +126,2 @@ this._inuse = 0; | ||
/** | ||
* Allocate a new buffer | ||
*/ | ||
ChunkAllocator.prototype._allocateBuffer = function(size) { | ||
var buffer = new Buffer(size); | ||
return buffer; | ||
}; | ||
module.exports = ChunkAllocator; |
@@ -36,2 +36,6 @@ // | ||
if (!options) { | ||
options = {}; | ||
} | ||
this._highWaterMark = options.highWaterMark || bufferSize; | ||
@@ -58,2 +62,9 @@ | ||
/** | ||
* Set the memory allocator. | ||
*/ | ||
ChunkStream.prototype.setMemoryAllocator = function(allocator) { | ||
this._allocator = allocator; | ||
}; | ||
/** | ||
* Internal stream ended | ||
@@ -102,3 +113,3 @@ */ | ||
/**f | ||
/** | ||
* Buffer the data into a chunk and emit it | ||
@@ -116,3 +127,5 @@ */ | ||
var targetSize = this._internalBufferSize + dataSize; | ||
if (targetSize < this._highWaterMark) { | ||
// add the data to the internal buffer and return as it is not yet full | ||
this._copyToInternalBuffer(data, dataOffset, data.length); | ||
@@ -122,5 +135,6 @@ return; | ||
if(this._internalBufferSize === 0 && data.length === this._highWaterMark) { | ||
//Don't create the buffer | ||
// set the buffer to the data passed in to avoid creating a new buffer | ||
buffer = data; | ||
} else { | ||
// add the data to the internal buffer and pop that buffer | ||
this._copyToInternalBuffer(data, dataOffset, data.length); | ||
@@ -131,2 +145,4 @@ buffer = this._popInternalBuffer(); | ||
} else { | ||
// add data to the internal buffer until its full, then return it | ||
// set the dataSize parameter so that additional data is not lost | ||
var copySize = this._highWaterMark - this._internalBufferSize; | ||
@@ -212,4 +228,4 @@ this._copyToInternalBuffer(data, dataOffset, dataOffset + copySize); | ||
var size = this._highWaterMark; | ||
if(this._allocator && this._allocator.getBufferSync) { | ||
return this._allocator.getBufferSync(size); | ||
if(this._allocator && this._allocator.getBuffer) { | ||
return this._allocator.getBuffer(size); | ||
} else { | ||
@@ -235,3 +251,3 @@ var buffer = new Buffer(size); | ||
} else { | ||
throw new Error('FileReadStream still don\'t end'); | ||
throw new Error('Stream has not ended.'); | ||
} | ||
@@ -238,0 +254,0 @@ } |
@@ -17,4 +17,6 @@ // | ||
var stream = require('stream'); | ||
var crypto = require('crypto'); | ||
var util = require('util'); | ||
var fs = require('fs'); | ||
var util = require('util'); | ||
@@ -27,42 +29,38 @@ var Constants = require('../util/constants'); | ||
/** | ||
* File read stream | ||
* 1. Calculate md5 | ||
* 2. Track reading offset | ||
* 3. Could with customize memory allocator | ||
* 4. Buffer data from file or stream. | ||
* @param {object} options stream.Readable options | ||
* File read stream | ||
* 1. Calculate md5 | ||
* 2. Track reading offset | ||
* 3. Work with customize memory allocator | ||
* 4. Buffer data from stream. | ||
* @param {object} options stream.Readable options | ||
*/ | ||
function FileReadStream(path, options) { | ||
EventEmitter.call(this); | ||
stream.Stream.call(this); | ||
this.readable = true; | ||
if(!options) { | ||
options = {}; | ||
} | ||
this._streamEnded = false; | ||
this._fd = null; | ||
this._fileName = undefined; | ||
this._highWaterMark = bufferSize; | ||
this._highWaterMark = options.highWaterMark || bufferSize; | ||
this._offset = 0; | ||
this._paused = undefined; | ||
this._allocator = null; | ||
this._init(path, options); | ||
} | ||
this._allocator = options.allocator; | ||
this._fileName = path; | ||
util.inherits(FileReadStream, EventEmitter); | ||
this._md5hash = null; | ||
this._md5sum = undefined; | ||
/** | ||
* File Read Stream init | ||
*/ | ||
FileReadStream.prototype._init = function(path, options) { | ||
if(!options) { | ||
options = {}; | ||
if (options.calcContentMd5) { | ||
this._md5hash = crypto.createHash('md5'); | ||
} | ||
this._fileName = path; | ||
if(options.allocator) { | ||
this._allocator = options.allocator; | ||
} | ||
if(options.highWaterMark) { | ||
this._highWaterMark = options.highWaterMark; | ||
} | ||
this._open(); | ||
}; | ||
} | ||
util.inherits(FileReadStream, stream.Stream); | ||
/** | ||
@@ -85,6 +83,11 @@ * Open file | ||
/** | ||
* Set memory allocator | ||
* Add event listener | ||
*/ | ||
FileReadStream.prototype.setMemoryAllocator = function(allocator) { | ||
this._allocator = allocator; | ||
FileReadStream.prototype.on = function(event, listener) { | ||
if (event === 'data' && this._paused === undefined) { | ||
this._paused = false; | ||
this._emitData(); | ||
} | ||
return EventEmitter.prototype.on.call(this, event, listener); | ||
}; | ||
@@ -96,4 +99,4 @@ | ||
FileReadStream.prototype._getBuffer = function(size) { | ||
if(this._allocator && this._allocator.getBufferSync) { | ||
return this._allocator.getBufferSync(size); | ||
if(this._allocator && this._allocator.getBuffer) { | ||
return this._allocator.getBuffer(size); | ||
} else { | ||
@@ -144,11 +147,26 @@ var buffer = new Buffer(size); | ||
self._offset += bytesRead; | ||
var range = { | ||
start : self._offset, | ||
end : self._offset + bytesRead - 1, | ||
size : bytesRead | ||
}; | ||
var data; | ||
if(bytesRead == self._highWaterMark) { | ||
self.emit('data', readBuffer); | ||
data = readBuffer; | ||
} else { | ||
self.emit('data', readBuffer.slice(0, bytesRead)); | ||
data = readBuffer.slice(0, bytesRead); | ||
//Release the current buffer since we created a new one | ||
self._releaseBuffer(readBuffer); | ||
} | ||
buffer = readBuffer = null; | ||
if(self._md5hash) { | ||
self._md5hash.update(data); | ||
} | ||
self.emit('data', data, range); | ||
// cleanup | ||
self._offset += bytesRead; | ||
buffer = readBuffer = data = null; | ||
self._emitData(); | ||
@@ -159,19 +177,22 @@ }); | ||
/** | ||
* Add event listener | ||
* Get file content md5 when read completely. | ||
*/ | ||
FileReadStream.prototype.on = function(event, listener) { | ||
if(event === 'end' && this._streamEnded) { | ||
listener(); //Directly call the end listener when stream already ended | ||
FileReadStream.prototype.getContentMd5 = function(encoding) { | ||
if (!encoding) encoding = 'base64'; | ||
if(!this._md5hash) { | ||
throw new Error('Can\'t get content md5, please set the calcContentMd5 option for FileReadStream.'); | ||
} else { | ||
EventEmitter.prototype.on.call(this, event, listener); | ||
if (this._streamEnded) { | ||
if (!this._md5sum) { | ||
this._md5sum = this._md5hash.digest(encoding); | ||
} | ||
return this._md5sum; | ||
} else { | ||
throw new Error('FileReadStream has not ended.'); | ||
} | ||
} | ||
if (event === 'data' && this._paused === undefined) { | ||
this._paused = false; | ||
this._emitData(); | ||
} | ||
}; | ||
/** | ||
* Pause read stream | ||
* Pause chunk stream | ||
*/ | ||
@@ -187,9 +208,21 @@ FileReadStream.prototype.pause = function() { | ||
var previousState = this._paused; | ||
this._paused = false; | ||
if(previousState === true) { | ||
//Only start to emit data when it's in pause state | ||
this._emitData(); | ||
if (this._paused) { | ||
this._paused = false; | ||
if(previousState === true) { | ||
//Only start to emit data when it's in pause state | ||
this._emitData(); | ||
} | ||
} | ||
}; | ||
FileReadStream.prototype.finish = function () { | ||
this.destroy(); | ||
}; | ||
FileReadStream.prototype.destroy = function () { | ||
this.readable = false; | ||
this.emit('close'); | ||
}; | ||
module.exports = FileReadStream; |
@@ -34,5 +34,13 @@ // | ||
*/ | ||
USER_AGENT_PRODUCT_VERSION: '0.2.1', | ||
USER_AGENT_PRODUCT_VERSION: '0.3', | ||
/** | ||
* The number of default concurrent requests for parallel operation. | ||
* | ||
* @const | ||
* @type {string} | ||
*/ | ||
DEFAULT_PARALLEL_OPERATION_THREAD_COUNT: 1, | ||
/** | ||
* Constant representing a kilobyte (Non-SI version). | ||
@@ -387,10 +395,2 @@ * | ||
/** | ||
* The number of default concurrent requests for parallel operation. | ||
* | ||
* @const | ||
* @type {string} | ||
*/ | ||
DEFAULT_PARALLEL_OPERATION_THREAD_COUNT: 1, | ||
/** | ||
* The default write page size, in bytes, used by blob streams. | ||
@@ -502,2 +502,36 @@ * | ||
/** | ||
* Defines constants for use with file operations. | ||
*/ | ||
FileConstants: { | ||
/** | ||
* The default write size, in bytes, used by file streams. | ||
* | ||
* @const | ||
* @type {string} | ||
*/ | ||
DEFAULT_WRITE_SIZE_IN_BYTES: 4 * 1024 * 1024, | ||
/** | ||
* The maximum range size when requesting for a contentMD5. | ||
*/ | ||
MAX_RANGE_GET_SIZE_WITH_MD5 : 4 * 1024 * 1024, | ||
/** | ||
* The maximum range size for a file update operation. | ||
*/ | ||
MAX_UPDATE_FILE_SIZE : 4 * 1024 * 1024, | ||
/** | ||
* Put range write options | ||
* | ||
* @const | ||
* @enum {string} | ||
*/ | ||
RangeWriteOptions: { | ||
UPDATE: 'update', | ||
CLEAR: 'clear' | ||
} | ||
}, | ||
/** | ||
* Defines constants for use with queue storage. | ||
@@ -759,3 +793,3 @@ * | ||
/** | ||
* The header that specifies blob content MD5. | ||
* The header that specifies public access to blobs. | ||
* | ||
@@ -765,6 +799,6 @@ * @const | ||
*/ | ||
BLOB_CONTENT_MD5_HEADER: 'x-ms-blob-content-md5', | ||
BLOB_PUBLIC_ACCESS_HEADER: 'x-ms-blob-public-access', | ||
/** | ||
* The header that specifies public access to blobs. | ||
* The header for the blob type. | ||
* | ||
@@ -774,6 +808,6 @@ * @const | ||
*/ | ||
BLOB_PUBLIC_ACCESS_HEADER: 'x-ms-blob-public-access', | ||
BLOB_TYPE_HEADER: 'x-ms-blob-type', | ||
/** | ||
* The header for the blob type. | ||
* The header for the type. | ||
* | ||
@@ -783,3 +817,3 @@ * @const | ||
*/ | ||
BLOB_TYPE_HEADER: 'x-ms-blob-type', | ||
TYPE_HEADER: 'x-ms-type', | ||
@@ -808,5 +842,13 @@ /** | ||
*/ | ||
CACHE_CONTROL_HEADER: 'x-ms-blob-cache-control', | ||
BLOB_CACHE_CONTROL_HEADER: 'x-ms-blob-cache-control', | ||
/** | ||
* The header that specifies caching control. | ||
* | ||
* @const | ||
* @type {string} | ||
*/ | ||
CACHE_CONTROL_HEADER: 'x-ms-cache-control', | ||
/** | ||
* The copy status. | ||
@@ -881,5 +923,13 @@ * | ||
*/ | ||
CONTENT_ENCODING_HEADER: 'x-ms-blob-content-encoding', | ||
BLOB_CONTENT_ENCODING_HEADER: 'x-ms-blob-content-encoding', | ||
/** | ||
* The header that specifies content encoding. | ||
* | ||
* @const | ||
* @type {string} | ||
*/ | ||
CONTENT_ENCODING_HEADER: 'x-ms-content-encoding', | ||
/** | ||
* The ContentLangauge header. | ||
@@ -898,5 +948,13 @@ * | ||
*/ | ||
CONTENT_LANGUAGE_HEADER: 'x-ms-blob-content-language', | ||
BLOB_CONTENT_LANGUAGE_HEADER: 'x-ms-blob-content-language', | ||
/** | ||
* The header that specifies content language. | ||
* | ||
* @const | ||
* @type {string} | ||
*/ | ||
CONTENT_LANGUAGE_HEADER: 'x-ms-content-language', | ||
/** | ||
* The ContentLength header. | ||
@@ -915,5 +973,13 @@ * | ||
*/ | ||
CONTENT_LENGTH_HEADER: 'x-ms-blob-content-length', | ||
BLOB_CONTENT_LENGTH_HEADER: 'x-ms-blob-content-length', | ||
/** | ||
* The header that specifies content length. | ||
* | ||
* @const | ||
* @type {string} | ||
*/ | ||
CONTENT_LENGTH_HEADER: 'x-ms-content-length', | ||
/** | ||
* The ContentDisposition header. | ||
@@ -931,5 +997,13 @@ * @const | ||
*/ | ||
CONTENT_DISPOSITION_HEADER: 'x-ms-blob-content-disposition', | ||
BLOB_CONTENT_DISPOSITION_HEADER: 'x-ms-blob-content-disposition', | ||
/** | ||
* The header that specifies content disposition. | ||
* | ||
* @const | ||
* @type {string} | ||
*/ | ||
CONTENT_DISPOSITION_HEADER: 'x-ms-content-disposition', | ||
/** | ||
* The ContentMD5 header. | ||
@@ -943,2 +1017,19 @@ * | ||
/** | ||
* The header that specifies blob content MD5. | ||
* | ||
* @const | ||
* @type {string} | ||
*/ | ||
BLOB_CONTENT_MD5_HEADER: 'x-ms-blob-content-md5', | ||
/** | ||
* The header that specifies content MD5. | ||
* | ||
* @const | ||
* @type {string} | ||
*/ | ||
CONTENT_MD5_HEADER: 'x-ms-content-md5', | ||
/** | ||
* The ContentRange header. | ||
@@ -965,5 +1056,13 @@ * | ||
*/ | ||
CONTENT_TYPE_HEADER: 'x-ms-blob-content-type', | ||
BLOB_CONTENT_TYPE_HEADER: 'x-ms-blob-content-type', | ||
/** | ||
* The header that specifies content type. | ||
* | ||
* @const | ||
* @type {string} | ||
*/ | ||
CONTENT_TYPE_HEADER: 'x-ms-content-type', | ||
/** | ||
* The header for copy source. | ||
@@ -1138,2 +1237,10 @@ * | ||
/** | ||
* The header that specifies file range write mode. | ||
* | ||
* @const | ||
* @type {string} | ||
*/ | ||
FILE_WRITE: 'x-ms-write', | ||
/** | ||
* The header that specifies whether the response should include the inserted entity. | ||
@@ -1795,3 +1902,4 @@ * | ||
CLOUD_QUEUE_HOST: 'queue.' + storageDnsSuffix, | ||
CLOUD_TABLE_HOST: 'table.' + storageDnsSuffix | ||
CLOUD_TABLE_HOST: 'table.' + storageDnsSuffix, | ||
CLOUD_FILE_HOST: 'file.' + storageDnsSuffix | ||
}, | ||
@@ -1865,2 +1973,7 @@ | ||
FileErrorCodeStrings: { | ||
SHARE_ALREADY_EXISTS: 'ShareAlreadyExists', | ||
SHARE_NOT_FOUND: 'ShareNotFound' | ||
}, | ||
QueueErrorCodeStrings: { | ||
@@ -1905,2 +2018,3 @@ QUEUE_NOT_FOUND: 'QueueNotFound', | ||
RESOURCE_NOT_FOUND: 'ResourceNotFound', | ||
RESOURCE_ALREADY_EXISTS: 'ResourceAlreadyExists', | ||
INVALID_METADATA: 'InvalidMetadata', | ||
@@ -1957,2 +2071,3 @@ METADATA_TOO_LARGE: 'MetadataTooLarge', | ||
BLOB_ENDPOINT_NAME: 'BlobEndpoint', | ||
FILE_ENDPOINT_NAME: 'FileEndpoint', | ||
QUEUE_ENDPOINT_NAME: 'QueueEndpoint', | ||
@@ -1962,4 +2077,5 @@ TABLE_ENDPOINT_NAME: 'TableEndpoint', | ||
BLOB_BASE_DNS_NAME: 'blob.core.windows.net', | ||
FILE_BASE_DNS_NAME: 'file.core.windows.net', | ||
QUEUE_BASE_DNS_NAME: 'queue.core.windows.net', | ||
TABLE_BASE_DNS_NAME: 'table.core.windows.net', | ||
TABLE_BASE_DNS_NAME: 'table.core.windows.net' | ||
} | ||
@@ -1966,0 +2082,0 @@ }; |
@@ -27,3 +27,3 @@ // | ||
BATCH_TOO_LARGE: 'Batches must not contain more than 100 operations.', | ||
BLOB_HASH_MISMATCH: 'Blob hash mismatch (integrity check failed), Expected value is %s, retrieved %s.', | ||
HASH_MISMATCH: 'Hash mismatch (integrity check failed), Expected value is %s, retrieved %s.', | ||
BLOB_INVALID_SEQUENCE_NUMBER: 'The sequence number may not be specified for an increment operation.', | ||
@@ -36,4 +36,6 @@ BLOB_TYPE_MISMATCH: 'Blob type of the blob reference doesn\'t match blob type of the blob.', | ||
EMPTY_BATCH: 'Batch must not be empty.', | ||
EXCEEDED_SIZE_LIMITATION: 'Upload exceeds the size limitation. Max size is %s but the current size is %s', | ||
INCORRECT_ENTITY_KEYS: 'PartitionKey and RowKey must be specified as strings in the entity object.', | ||
INVALID_BLOB_LENGTH: 'createBlockBlobFromText requires the size of text to be less than 64MB. Please use createBlockBlobFromFile or createBlockBlobFromStream to upload large blobs.', | ||
INVALID_BLOB_LENGTH: 'createBlockBlobFromText requires the size of text to be less than 64MB. Please use createBlockBlobFromLocalFile or createBlockBlobFromStream to upload large blobs.', | ||
INVALID_FILE_LENGTH: 'createFileFromText requires the size of text to be less than 4MB. Please use createFileFromLocalFile or createFileFromStream to upload large files.', | ||
INVALID_CONNECTION_STRING: 'Connection strings must be of the form "key1=value1;key2=value2".', | ||
@@ -49,2 +51,3 @@ INVALID_CONNECTION_STRING_BAD_KEY: 'Connection string contains unrecognized key: "%s"', | ||
INVALID_PAGE_START_OFFSET: 'Page start offset must be multiple of 512.', | ||
INVALID_FILE_RANGE_FOR_UPDATE: 'Range size should be less than 4MB for a file range update operation.', | ||
INVALID_PAGE_RANGE_FOR_UPDATE: 'Page range size should be less than 4MB for a page update operation.', | ||
@@ -51,0 +54,0 @@ INVALID_POP_RECEIPT: 'Pop Receipt cannot be null or undefined for deleteMessage and updateMessage operations.', |
@@ -336,2 +336,25 @@ // | ||
/** | ||
* Calculate md5sum for the stream | ||
* @ignore | ||
*/ | ||
exports.calculateMD5 = function(readStream, bufferLength, options, callback) { | ||
var internalBuff = new Buffer(bufferLength); | ||
var index = 0; | ||
var internalHash = crypto.createHash('md5'); | ||
readStream.on('data', function(data) { | ||
if (index + data.length > bufferLength) { | ||
throw new Error(SR.INVALID_STREAM_LENGTH); | ||
} | ||
else { | ||
data.copy(internalBuff, index); | ||
internalHash.update(data); | ||
index += data.length; | ||
} | ||
}).on('end', function() { | ||
internalBuff.length = index; | ||
options.contentMD5 = internalHash.digest('base64'); | ||
callback(internalBuff); | ||
}); | ||
}; | ||
@@ -338,0 +361,0 @@ /** |
@@ -18,2 +18,3 @@ // | ||
var _ = require('underscore'); | ||
var util = require('util'); | ||
@@ -128,2 +129,21 @@ var azureutil = require('./util'); | ||
var getNameError = function(name) { | ||
// checks if name is null, undefined or empty | ||
if (azureutil.stringIsEmpty(name)) { | ||
return '%s name must be a non empty string.'; | ||
} | ||
// check if name is between 3 and 63 characters | ||
if (name.length < 3 || name.length > 63) { | ||
return '%s name must be between 3 and 63 characters long.'; | ||
} | ||
// check if name follows naming rules | ||
if (name.match('^([a-z0-9]+(-[a-z0-9]+)*)$') === null) { | ||
return '%s name format is incorrect.'; | ||
} | ||
return null; | ||
} | ||
/** | ||
@@ -142,17 +162,33 @@ * Validates a container name. | ||
// checks if containerName is null, undefined or empty | ||
if (azureutil.stringIsEmpty(containerName)) { | ||
return fail('Container name must be a non empty string.'); | ||
} | ||
var nameErrorString = getNameError(containerName); | ||
if (containerName.length < 3 || containerName.length > 63) { | ||
return fail('Container name must be between 3 and 63 characters long.'); | ||
if (!nameErrorString || containerName.match('^(\$root|\$logs)')) { | ||
callback(); | ||
return true; | ||
} else { | ||
return fail(util.format(nameErrorString, 'Container')); | ||
} | ||
}; | ||
if (containerName.match('^(\$root|\$logs|[a-z0-9]+(-[a-z0-9]+)*)$') === null) { | ||
return fail('Container name format is incorrect.'); | ||
/** | ||
* Validates a share name. | ||
* | ||
* @param {string} shareName The share name. | ||
* @return {undefined} | ||
*/ | ||
exports.shareNameIsValid = function (shareName, callback) { | ||
var fail; | ||
initCallback(callback, function (f, cb) { | ||
fail = f; | ||
callback = cb; | ||
}); | ||
var nameErrorString = getNameError(shareName); | ||
if (!nameErrorString) { | ||
callback(); | ||
return true; | ||
} else { | ||
return fail(util.format(nameErrorString, 'Share')); | ||
} | ||
callback(); | ||
return true; | ||
}; | ||
@@ -163,6 +199,6 @@ | ||
* | ||
* @param {string} queue The queue name. | ||
* @param {string} queueName The queue name. | ||
* @return {undefined} | ||
*/ | ||
exports.queueNameIsValid = function (queue, callback) { | ||
exports.queueNameIsValid = function (queueName, callback) { | ||
var fail; | ||
@@ -175,20 +211,12 @@ | ||
// checks if containerName is null, undefined or empty | ||
if (azureutil.stringIsEmpty(queue)) { | ||
return fail('Queue name must be a non empty string.'); | ||
} | ||
var nameErrorString = getNameError(queueName); | ||
if (queue.length < 3 || queue.length > 63) { | ||
return fail('Queue name must be between 3 and 63 characters long.'); | ||
if (!nameErrorString) { | ||
callback(); | ||
return true; | ||
} else { | ||
return fail(util.format(nameErrorString, 'Queue')); | ||
} | ||
if (queue.match('^([a-z0-9]+(-[a-z0-9]+)*)$') === null) { | ||
return fail('Queue name format is incorrect.'); | ||
} | ||
callback(); | ||
return true; | ||
}; | ||
/** | ||
@@ -327,2 +355,3 @@ * Validates a table name. | ||
containerNameIsValid: exports.containerNameIsValid, | ||
shareNameIsValid: exports.shareNameIsValid, | ||
blobNameIsValid: exports.blobNameIsValid, | ||
@@ -329,0 +358,0 @@ pageRangesAreValid: exports.pageRangesAreValid, |
@@ -63,13 +63,13 @@ // | ||
'contentRange': 'CONTENT_RANGE', | ||
'contentTypeHeader': 'CONTENT_TYPE_HEADER', | ||
'contentEncodingHeader': 'CONTENT_ENCODING_HEADER', | ||
'contentLanguageHeader': 'CONTENT_LANGUAGE_HEADER', | ||
'contentTypeHeader': 'BLOB_CONTENT_TYPE_HEADER', | ||
'contentEncodingHeader': 'BLOB_CONTENT_ENCODING_HEADER', | ||
'contentLanguageHeader': 'BLOB_CONTENT_LANGUAGE_HEADER', | ||
'contentMD5Header': 'BLOB_CONTENT_MD5_HEADER', | ||
'cacheControlHeader': 'CACHE_CONTROL_HEADER', | ||
'cacheControlHeader': 'BLOB_CACHE_CONTROL_HEADER', | ||
'contentLength': 'CONTENT_LENGTH', | ||
'contentLengthHeader': 'CONTENT_LENGTH_HEADER', | ||
'contentLengthHeader': 'BLOB_CONTENT_LENGTH_HEADER', | ||
'contentDisposition': 'CONTENT_DISPOSITION', | ||
'contentDispositionHeader': 'CONTENT_DISPOSITION_HEADER', | ||
'contentDispositionHeader': 'BLOB_CONTENT_DISPOSITION_HEADER', | ||
@@ -186,6 +186,6 @@ 'range': 'RANGE', | ||
// Content-Type | ||
setHeaderPropertyFromBlob(HeaderConstants.CONTENT_TYPE_HEADER, 'contentType'); | ||
setHeaderPropertyFromBlob(HeaderConstants.BLOB_CONTENT_TYPE_HEADER, 'contentType'); | ||
// Content-Encoding | ||
setHeaderPropertyFromBlob(HeaderConstants.CONTENT_ENCODING_HEADER, 'contentEncoding'); | ||
setHeaderPropertyFromBlob(HeaderConstants.BLOB_CONTENT_ENCODING_HEADER, 'contentEncoding'); | ||
@@ -196,9 +196,9 @@ // Content-MD5 | ||
// Content-Language | ||
setHeaderPropertyFromBlob(HeaderConstants.CONTENT_LANGUAGE_HEADER, 'contentLanguage'); | ||
setHeaderPropertyFromBlob(HeaderConstants.BLOB_CONTENT_LANGUAGE_HEADER, 'contentLanguage'); | ||
// Content-Disposition | ||
setHeaderPropertyFromBlob(HeaderConstants.CONTENT_DISPOSITION_HEADER, 'contentDisposition'); | ||
setHeaderPropertyFromBlob(HeaderConstants.BLOB_CONTENT_DISPOSITION_HEADER, 'contentDisposition'); | ||
// Cache-Control | ||
setHeaderPropertyFromBlob(HeaderConstants.CACHE_CONTROL_HEADER, 'cacheControl'); | ||
setHeaderPropertyFromBlob(HeaderConstants.BLOB_CACHE_CONTROL_HEADER, 'cacheControl'); | ||
@@ -205,0 +205,0 @@ // Lease id |
@@ -18,2 +18,4 @@ // | ||
// Module dependencies. | ||
var _ = require('underscore'); | ||
var azureCommon = require('./../../../common/common'); | ||
@@ -23,16 +25,2 @@ var xmlbuilder = azureCommon.xmlbuilder; | ||
function BlockListResult(committedBlocks, uncommittedBlocks, latestBlocks) { | ||
if (latestBlocks) { | ||
this.LatestBlocks = latestBlocks; | ||
} | ||
if (committedBlocks) { | ||
this.CommittedBlocks = committedBlocks; | ||
} | ||
if (uncommittedBlocks) { | ||
this.UncommittedBlocks = uncommittedBlocks; | ||
} | ||
} | ||
/** | ||
@@ -44,3 +32,3 @@ * Builds an XML representation for a block list. | ||
*/ | ||
BlockListResult.serialize = function (blockListJs) { | ||
exports.serialize = function (blockListJs) { | ||
var blockListDoc = xmlbuilder.create(); | ||
@@ -76,20 +64,20 @@ blockListDoc = blockListDoc.begin(Constants.BlobConstants.BLOCK_LIST_ELEMENT, { version: '1.0', encoding: 'utf-8' }); | ||
BlockListResult.parse = function (blockListXml) { | ||
var blockListResult = new BlockListResult(); | ||
exports.parse = function (blockListXml) { | ||
var blockListResult = {}; | ||
if (blockListXml.CommittedBlocks && blockListXml.CommittedBlocks.Block && blockListXml.CommittedBlocks.Block.length > 0) { | ||
if (blockListXml.CommittedBlocks && blockListXml.CommittedBlocks.Block) { | ||
blockListResult.CommittedBlocks = blockListXml.CommittedBlocks.Block; | ||
if (!_.isArray(blockListResult.CommittedBlocks)) { | ||
blockListResult.CommittedBlocks = [blockListResult.CommittedBlocks]; | ||
} | ||
} | ||
if (blockListXml.UncommittedBlocks && blockListXml.UncommittedBlocks.Block && blockListXml.UncommittedBlocks.Block.length > 0) { | ||
if (blockListXml.UncommittedBlocks && blockListXml.UncommittedBlocks.Block) { | ||
blockListResult.UncommittedBlocks = blockListXml.UncommittedBlocks.Block; | ||
if (!_.isArray(blockListResult.UncommittedBlocks)) { | ||
blockListResult.UncommittedBlocks = [blockListResult.UncommittedBlocks]; | ||
} | ||
} | ||
if (blockListXml.LatestBlocks && blockListXml.LatestBlocks.Block && blockListXml.LatestBlocks.Block.length > 0) { | ||
blockListResult.LatestBlocks = blockListXml.LatestBlocks.Block; | ||
} | ||
return blockListResult; | ||
}; | ||
module.exports = BlockListResult; | ||
}; |
{ | ||
"name": "azure-storage", | ||
"author": "Microsoft Corporation", | ||
"version": "0.2.1", | ||
"version": "0.3.0", | ||
"description": "Microsoft Azure Storage Client Library for Node.js", | ||
@@ -6,0 +6,0 @@ "tags": [ |
@@ -18,2 +18,5 @@ # Microsoft Azure Storage SDK for Node.js | ||
- Create/Read/Update/Delete Blobs | ||
- Files | ||
- Create/Update/Delete Directories | ||
- Create/Read/Update/Delete Files | ||
- Queues | ||
@@ -132,3 +135,3 @@ - Create/Delete Queues | ||
To upload a file (assuming it is called task1-upload.txt and it is placed in the same folder as the script below), the method **createBlockBlobFromFile** can be used. | ||
To upload a file (assuming it is called task1-upload.txt and it is placed in the same folder as the script below), the method **createBlockBlobFromLocalFile** can be used. | ||
@@ -139,3 +142,3 @@ ```Javascript | ||
blobService.createBlockBlobFromFile('mycontainer', 'taskblob', 'task1-upload.txt', function(error, result, response){ | ||
blobService.createBlockBlobFromLocalFile('mycontainer', 'taskblob', 'task1-upload.txt', function(error, result, response){ | ||
if(!error){ | ||
@@ -148,3 +151,3 @@ // file uploaded | ||
For page blobs, use **createPageBlobFromFile**. There are other methods for uploading blobs also, such as **createBlockBlobFromText** or **createPageBlobFromStream**. | ||
For page blobs, use **createPageBlobFromLocalFile**. There are other methods for uploading blobs also, such as **createBlockBlobFromText** or **createPageBlobFromStream**. | ||
@@ -231,2 +234,59 @@ There are also several ways to download block and page blobs. For example, **getBlockBlobToStream** downloads the blob to a stream: | ||
### File Storage | ||
The **createShareIfNotExists** method can be used to create a | ||
share in which to store a file or a directory of files: | ||
```Javascript | ||
var azure = require('azure-storage'); | ||
var fileService = azure.createFileService(); | ||
fileService.createShareIfNotExists('taskshare', function(error, result, response){ | ||
if(!error){ | ||
// if result = true, share was created. | ||
// if result = false, share already existed. | ||
} | ||
}); | ||
``` | ||
To create a directory, the method **createDirectoryIfNotExists** can be used. | ||
```Javascript | ||
var azure = require('azure-storage'); | ||
var fileService = azure.createFileService(); | ||
fileService.createDirectoryIfNotExists('taskshare', 'taskdirectory', function(error, result, response){ | ||
if(!error){ | ||
// if result = true, share was created. | ||
// if result = false, share already existed. | ||
} | ||
}); | ||
``` | ||
To upload a file (assuming it is called task1-upload.txt and it is placed in the same folder as the script below), the method **createFileFromLocalFile** can be used. | ||
```Javascript | ||
var azure = require('azure-storage'); | ||
var fileService = azure.createFileService(); | ||
fileService.createFileFromLocalFile('taskshare', 'taskdirectory', 'taskfile', 'task1-upload.txt', function(error, result, response){ | ||
if(!error){ | ||
// file uploaded | ||
} | ||
}); | ||
``` | ||
There are other methods for uploading files also, such as **createFileFromText** or **createFileFromStream**. | ||
There are also several ways to download files. For example, **getFileToStream** downloads the file to a stream: | ||
```Javascript | ||
var fileService = azure.createFileService(); | ||
var fs = require('fs'); | ||
fileService.getFileToStream('taskshare', 'taskdirectory', 'taskfile', fs.createWriteStream('output.txt'), function(error, result, response){ | ||
if(!error) { | ||
// file retrieved | ||
} | ||
}); | ||
``` | ||
## Code Samples | ||
@@ -244,3 +304,3 @@ | ||
In order to run the tests, the following environment variables need to be set up using an admin command prompt: | ||
In order to run the tests, the following environment variables need to be set up: | ||
@@ -247,0 +307,0 @@ AZURE_STORAGE_CONNECTION_STRING="valid storage connection string" |
@@ -861,3 +861,3 @@ // | ||
var blobOptions = {blockIdPrefix : 'blockId' }; | ||
blobService.createBlockBlobFromFile(containerName, blobName, fileNameSource, blobOptions, function (err) { | ||
blobService.createBlockBlobFromLocalFile(containerName, blobName, fileNameSource, blobOptions, function (err) { | ||
assert.equal(err, null); | ||
@@ -887,3 +887,3 @@ | ||
// Create the empty page blob | ||
blobService.createBlockBlobFromFile(containerName, blobName, fileNameSource, { contentType: null, contentTypeHeader: null, blockIdPrefix : 'blockId' }, function (err) { | ||
blobService.createBlockBlobFromLocalFile(containerName, blobName, fileNameSource, { contentType: null, contentTypeHeader: null, blockIdPrefix : 'blockId' }, function (err) { | ||
assert.equal(err, null); | ||
@@ -1335,3 +1335,3 @@ | ||
assert.notEqual(container1, null); | ||
blobService.createPageBlobFromFile(containerName, blobName, fileName, localOptions, function (err) { | ||
blobService.createPageBlobFromLocalFile(containerName, blobName, fileName, localOptions, function (err) { | ||
assert.notEqual(err, null); | ||
@@ -1338,0 +1338,0 @@ assert.equal(err.message, SR.MAXIMUM_EXECUTION_TIMEOUT_EXCEPTION); |
@@ -36,6 +36,8 @@ // | ||
after(function (done) { | ||
done(); | ||
blobService.deleteContainer(containerName, function(error) { | ||
done(); | ||
}); | ||
}); | ||
var apis = ['createBlockBlobFromFile', 'createPageBlobFromFile']; | ||
var apis = ['createBlockBlobFromLocalFile', 'createPageBlobFromLocalFile']; | ||
var sizes = [0, 1024, 1024 * 1024, 4 * 1024 * 1024, 32 * 1024 * 1024, 64 * 1024 * 1024, 148 * 1024 * 1024 - 512, 148 * 1024 * 1024, 148 * 1024 * 1024 + 512]; | ||
@@ -57,3 +59,3 @@ for(var i = 0; i < apis.length; i++) { | ||
uploadFunc.call(blobService, containerName, blobName, fileInfo.name, uploadOptions, function(error) { | ||
if(api === 'createPageBlobFromFile' && size !== 0 && size % 512 !== 0) { | ||
if(api === 'createPageBlobFromLocalFile' && size !== 0 && size % 512 !== 0) { | ||
assert.equal(error.message, util.format('The page blob size must be aligned to a 512-byte boundary. The current stream length is %s', size)); | ||
@@ -68,3 +70,3 @@ done(); | ||
var downloadOptions = {validateContentMD5: true}; | ||
blobService.getBlobToFile(containerName, blobName, downloadFileName, downloadOptions, function(error, blob) { | ||
blobService.getBlobToLocalFile(containerName, blobName, downloadFileName, downloadOptions, function(error, blob) { | ||
assert.equal(error, null); | ||
@@ -71,0 +73,0 @@ assert.equal(blob.contentMD5, fileInfo.contentMD5); |
@@ -54,2 +54,3 @@ // | ||
var zeroSizeFileName = 'blobservice_zero_size_file.tmp'; | ||
var downloadName = 'blobservice_download.tmp'; | ||
@@ -62,3 +63,2 @@ var fileText = 'Hello World!'; | ||
var pageBlob2KContentMD5 = ''; | ||
var downloadName = 'blobservice_download.tmp'; | ||
var uploadOptions = { | ||
@@ -108,2 +108,40 @@ blockIdPrefix : blockIdPrefix | ||
it('CreateBlobWithBars', function (done) { | ||
var blobName = 'blobs/' + testutil.generateId(blobNamesPrefix, blobNames, false); | ||
var blobText = 'Hello World!'; | ||
// Create the empty page blob | ||
blobService.createBlockBlobFromText(containerName, blobName, blobText, function (err) { | ||
assert.equal(err, null); | ||
blobService.getBlobProperties(containerName, blobName, function (error, properties) { | ||
assert.equal(error, null); | ||
assert.equal(properties.container, containerName); | ||
assert.equal(properties.blob, blobName); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
// This test ensures that blocks can be created from files correctly | ||
// and was created to ensure that the request module does not magically add | ||
// a content type to the request when the user did not specify one. | ||
it('works with files without specifying content type', function (done) { | ||
fs.writeFileSync(blockFileName, fileText); | ||
var callback = function (webresource) { | ||
assert.notEqual(webresource.headers[HeaderConstants.CONTENT_MD5], null); | ||
}; | ||
blobService.on('sendingRequestEvent', callback); | ||
blobService.createBlockFromStream('test', containerName, blockBlobName, fs.createReadStream(blockFileName), fileText.length, {useTransactionalMD5: true}, function (error) { | ||
assert.equal(error, null); | ||
blobService.removeAllListeners('sendingRequestEvent'); | ||
done(); | ||
}); | ||
}); | ||
describe('blob-piping-tests', function() { | ||
@@ -395,49 +433,2 @@ it('should be able to upload block blob from piped stream', function (done) { | ||
}); | ||
it('CreateBlobWithBars', function (done) { | ||
var blobName = 'blobs/' + testutil.generateId(blobNamesPrefix, blobNames, false); | ||
var blobText = 'Hello World!'; | ||
// Create the empty page blob | ||
blobService.createBlockBlobFromText(containerName, blobName, blobText, function (err) { | ||
assert.equal(err, null); | ||
blobService.getBlobProperties(containerName, blobName, function (error, properties) { | ||
assert.equal(error, null); | ||
assert.equal(properties.container, containerName); | ||
assert.equal(properties.blob, blobName); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('works with files without specifying content type', function (done) { | ||
// This test ensures that blocks can be created from files correctly | ||
// and was created to ensure that the request module does not magically add | ||
// a content type to the request when the user did not specify one. | ||
var blobName = testutil.generateId(blobNamesPrefix, blobNames, false); | ||
var fileName= testutil.generateId('prefix') + '.txt'; | ||
var blobText = 'Hello World!'; | ||
try { fs.unlinkSync(fileName); } catch (e) {} | ||
fs.writeFileSync(fileName, blobText); | ||
var stat = fs.statSync(fileName); | ||
var callback = function (webresource) { | ||
assert.notEqual(webresource.headers[HeaderConstants.CONTENT_MD5], null); | ||
}; | ||
blobService.on('sendingRequestEvent', callback); | ||
blobService.createBlockFromStream('test', containerName, blobName, fs.createReadStream(fileName), stat.size, {useTransactionalMD5: true}, function (error) { | ||
try { fs.unlinkSync(fileName); } catch (e) {} | ||
assert.equal(error, null); | ||
blobService.removeAllListeners('sendingRequestEvent'); | ||
done(); | ||
}); | ||
}); | ||
@@ -476,6 +467,4 @@ describe('CreateBlock', function() { | ||
}); | ||
}); | ||
describe('CommitBlockList', function() { | ||
it('should work', function (done) { | ||
it('CommitBlockList', function (done) { | ||
var blobName = testutil.generateId(blobNamesPrefix, blobNames, false); | ||
@@ -509,57 +498,38 @@ | ||
}); | ||
}); | ||
describe('getBlobToStream', function() { | ||
it('should work', function (done) { | ||
it('should work with a single block', function (done) { | ||
var blobName = testutil.generateId(blobNamesPrefix, blobNames, false); | ||
var fileNameTarget = testutil.generateId('getBlobFile', [], false) + '.test'; | ||
var blobText = 'Hello World'; | ||
blobService.createBlockBlobFromText(containerName, blobName, blobText, function (error1) { | ||
assert.equal(error1, null); | ||
blobService.createBlockFromText('id1', containerName, blobName, 'id1', function (error2) { | ||
assert.equal(error2, null); | ||
blobService.getBlobToFile(containerName, blobName, fileNameTarget, function (error2) { | ||
assert.equal(error2, null); | ||
blobService.createBlockFromText('id2', containerName, blobName, 'id2', function (error3) { | ||
assert.equal(error3, null); | ||
var exists = azureutil.pathExistsSync(fileNameTarget); | ||
assert.equal(exists, true); | ||
var blockList = { | ||
LatestBlocks: ['id1'], | ||
}; | ||
fs.readFile(fileNameTarget, function (err, fileText) { | ||
assert.equal(blobText, fileText); | ||
blobService.commitBlocks(containerName, blobName, blockList, function (error4) { | ||
assert.equal(error4, null); | ||
try { fs.unlinkSync(fileNameTarget); } catch (e) {} | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
it('should be able to upload small blob from file', function (done) { | ||
var blobName = testutil.generateId(blobNamesPrefix, blobNames, false); | ||
var fileNameSource = testutil.generateId('getBlobFile', [], false) + '.test'; | ||
var blobText = 'Hello World'; | ||
blobService.listBlocks(containerName, blobName, BlobUtilities.BlockListFilter.ALL, function (error5, list) { | ||
assert.equal(error5, null); | ||
assert.notEqual(list, null); | ||
assert.notEqual(list.CommittedBlocks, null); | ||
assert.equal(list.CommittedBlocks.length, 1); | ||
fs.writeFile(fileNameSource, blobText, function () { | ||
var blobOptions = { contentType: 'text', blockIdPrefix : 'blockId' }; | ||
blobService.createBlockBlobFromFile(containerName, blobName, fileNameSource, blobOptions, function (uploadError, blobResponse, uploadResponse) { | ||
assert.equal(uploadError, null); | ||
assert.notEqual(blobResponse, null); | ||
assert.ok(uploadResponse.isSuccessful); | ||
blobService.getBlobToText(containerName, blobName, function (downloadErr, blobTextResponse) { | ||
assert.equal(downloadErr, null); | ||
assert.equal(blobTextResponse, blobText); | ||
blobService.getBlobProperties(containerName, blobName, function (getBlobPropertiesErr, blobGetResponse) { | ||
assert.equal(getBlobPropertiesErr, null); | ||
assert.notEqual(blobGetResponse, null); | ||
if (blobGetResponse) { | ||
assert.equal(blobOptions.contentType, blobGetResponse.contentType); | ||
} | ||
try { fs.unlinkSync(fileNameSource); } catch (e) {} | ||
done(); | ||
blobService.createBlockFromText('id3', containerName, blobName, 'id3', function (error6) { | ||
assert.equal(error6, null); | ||
blobService.listBlocks(containerName, blobName, BlobUtilities.BlockListFilter.ALL, function (error7, list) { | ||
assert.equal(error7, null); | ||
assert.notEqual(list, null); | ||
assert.notEqual(list.CommittedBlocks, null); | ||
assert.notEqual(list.UncommittedBlocks, null); | ||
assert.equal(list.CommittedBlocks.length, 1); | ||
assert.equal(list.UncommittedBlocks.length, 1); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
@@ -570,43 +540,3 @@ }); | ||
}); | ||
it('should be able to upload small blob from stream', function (done) { | ||
var blobName = testutil.generateId(blobNamesPrefix, blobNames, false); | ||
var fileNameSource = testutil.generateId('getBlobFile', [], false) + '.test'; | ||
var blobText = 'Hello World'; | ||
fs.writeFile(fileNameSource, blobText, function () { | ||
var callback = function (webresource) { | ||
assert.notEqual(webresource.headers[HeaderConstants.CONTENT_MD5], null); | ||
}; | ||
blobService.on('sendingRequestEvent', callback); | ||
var blobOptions = { contentType: 'text', blockIdPrefix : 'blockId', useTransactionalMD5 : true }; | ||
blobService.createBlockBlobFromStream(containerName, blobName, fs.createReadStream(fileNameSource), '11', blobOptions, function (uploadError, blobResponse, uploadResponse) { | ||
assert.equal(uploadError, null); | ||
assert.notEqual(blobResponse, null); | ||
assert.ok(uploadResponse.isSuccessful); | ||
blobService.removeAllListeners('sendingRequestEvent'); | ||
blobService.getBlobToText(containerName, blobName, function (downloadErr, blobTextResponse) { | ||
assert.equal(downloadErr, null); | ||
assert.equal(blobTextResponse, blobText); | ||
blobService.getBlobProperties(containerName, blobName, function (getBlobPropertiesErr, blobGetResponse) { | ||
assert.equal(getBlobPropertiesErr, null); | ||
assert.notEqual(blobGetResponse, null); | ||
if (blobGetResponse) { | ||
assert.equal(blobOptions.contentType, blobGetResponse.contentType); | ||
} | ||
try { fs.unlinkSync(fileNameSource); } catch (e) {} | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe('blob-MD5Validation-tests', function() { | ||
@@ -624,3 +554,3 @@ it('Upload/Download with MD5 validation should work', function (done) { | ||
var blobOptions = { contentType: 'text', blockIdPrefix : 'blockId'}; | ||
blobService.createBlockBlobFromFile(containerName, blobName, fileNameSource, blobOptions, function (uploadError, blobResponse, uploadResponse) { | ||
blobService.createBlockBlobFromLocalFile(containerName, blobName, fileNameSource, blobOptions, function (uploadError, blobResponse, uploadResponse) { | ||
assert.equal(uploadError, null); | ||
@@ -670,3 +600,3 @@ assert.notEqual(blobResponse, null); | ||
var blobOptions = { contentType: 'text', blockIdPrefix : 'blockId'}; | ||
blobService.createBlockBlobFromFile(containerName, blobName, fileNameSource, blobOptions, function (uploadError, blobResponse, uploadResponse) { | ||
blobService.createBlockBlobFromLocalFile(containerName, blobName, fileNameSource, blobOptions, function (uploadError, blobResponse, uploadResponse) { | ||
assert.equal(uploadError, null); | ||
@@ -685,7 +615,7 @@ assert.notEqual(blobResponse, null); | ||
assert.notEqual(downloadErr, null); | ||
assert.equal(downloadErr.message, util.format(SR.BLOB_HASH_MISMATCH, 'MDAwMDAwMDA=', 'ndpxhuSh0PPmMvK74fkYvg==')); | ||
assert.equal(downloadErr.message, util.format(SR.HASH_MISMATCH, 'MDAwMDAwMDA=', 'ndpxhuSh0PPmMvK74fkYvg==')); | ||
blobService.getBlobToStream(containerName, blobName, fs.createWriteStream('task2-download.txt'), function (downloadErr2) { | ||
assert.notEqual(downloadErr2, null); | ||
assert.equal(downloadErr2.message, util.format(SR.BLOB_HASH_MISMATCH, 'MDAwMDAwMDA=', 'ndpxhuSh0PPmMvK74fkYvg==')); | ||
assert.equal(downloadErr2.message, util.format(SR.HASH_MISMATCH, 'MDAwMDAwMDA=', 'ndpxhuSh0PPmMvK74fkYvg==')); | ||
@@ -717,3 +647,3 @@ blobService.getBlobToStream(containerName, blobName, fs.createWriteStream('task3-download.txt'), { disableContentMD5Validation: true }, function (downloadErr3) { | ||
var blobOptions = { contentType: 'text'}; | ||
blobService.createPageBlobFromFile(containerName, blobName, fileNameSource, blobOptions, function (uploadError, blobResponse, uploadResponse) { | ||
blobService.createPageBlobFromLocalFile(containerName, blobName, fileNameSource, blobOptions, function (uploadError, blobResponse, uploadResponse) { | ||
assert.equal(uploadError, null); | ||
@@ -732,7 +662,7 @@ assert.notEqual(blobResponse, null); | ||
assert.notEqual(downloadErr, null); | ||
assert.equal(downloadErr.message, util.format(SR.BLOB_HASH_MISMATCH, 'MDAwMDAwMDA=', 'ndpxhuSh0PPmMvK74fkYvg==')); | ||
assert.equal(downloadErr.message, util.format(SR.HASH_MISMATCH, 'MDAwMDAwMDA=', 'ndpxhuSh0PPmMvK74fkYvg==')); | ||
blobService.getBlobToStream(containerName, blobName, fs.createWriteStream('task2-download.txt'), function (downloadErr2) { | ||
assert.notEqual(downloadErr2, null); | ||
assert.equal(downloadErr2.message, util.format(SR.BLOB_HASH_MISMATCH, 'MDAwMDAwMDA=', 'ndpxhuSh0PPmMvK74fkYvg==')); | ||
assert.equal(downloadErr2.message, util.format(SR.HASH_MISMATCH, 'MDAwMDAwMDA=', 'ndpxhuSh0PPmMvK74fkYvg==')); | ||
@@ -766,3 +696,3 @@ blobService.getBlobToStream(containerName, blobName, fs.createWriteStream('task3-download.txt'), { disableContentMD5Validation: true }, function (downloadErr3) { | ||
var blobOptions = { contentType: 'text', blockIdPrefix : 'blockId'}; | ||
blobService.createBlockBlobFromFile(containerName, blobName, fileNameSource, blobOptions, function (uploadError, blobResponse, uploadResponse) { | ||
blobService.createBlockBlobFromLocalFile(containerName, blobName, fileNameSource, blobOptions, function (uploadError, blobResponse, uploadResponse) { | ||
assert.equal(uploadError, null); | ||
@@ -798,3 +728,3 @@ assert.notEqual(blobResponse, null); | ||
var blobOptions = { contentType: 'text', blockIdPrefix : 'blockId'}; | ||
blobService.createPageBlobFromFile(containerName, blobName, fileNameSource, blobOptions, function (uploadError, blobResponse, uploadResponse) { | ||
blobService.createPageBlobFromLocalFile(containerName, blobName, fileNameSource, blobOptions, function (uploadError, blobResponse, uploadResponse) { | ||
assert.equal(uploadError, null); | ||
@@ -846,10 +776,8 @@ assert.notEqual(blobResponse, null); | ||
it('should upload a small blob from file', function (done) { | ||
var blobName = testutil.generateId(blobNamesPrefix, blobNames); | ||
var fileNameSource = testutil.generateId('getBlobFile') + '.test'; | ||
var blobText = 'Hello World'; | ||
describe('createBlockBlobFromText', function () { | ||
it('should work for small size from text', function (done) { | ||
var blobName = testutil.generateId(blobNamesPrefix, blobNames, false) + ' a'; | ||
var blobText = 'Hello World'; | ||
fs.writeFile(fileNameSource, blobText, function () { | ||
var blobOptions = { contentType: 'text' }; | ||
blobService.createBlockBlobFromFile(containerName, blobName, fileNameSource, blobOptions, function (uploadError, blobResponse, uploadResponse) { | ||
blobService.createBlockBlobFromText(containerName, blobName, blobText, function (uploadError, blobResponse, uploadResponse) { | ||
assert.equal(uploadError, null); | ||
@@ -863,19 +791,27 @@ assert.notEqual(blobResponse, null); | ||
blobService.getBlobProperties(containerName, blobName, function (getBlobPropertiesErr, blobGetResponse) { | ||
assert.equal(getBlobPropertiesErr, null); | ||
assert.notEqual(blobGetResponse, null); | ||
if (blobGetResponse) { | ||
assert.equal(blobOptions.contentType, blobGetResponse.contentType); | ||
} | ||
done(); | ||
}); | ||
}); | ||
}); | ||
try { fs.unlinkSync(fileNameSource); } catch (e) {} | ||
it('should automatically store md5', function (done) { | ||
var blobName = testutil.generateId(blobNamesPrefix, blobNames, false) + ' a'; | ||
var blobText = 'Hello World'; | ||
var blobMD5 = azureutil.getContentMd5(blobText); | ||
done(); | ||
}); | ||
blobService.createBlockBlobFromText(containerName, blobName, blobText, function (uploadError, blobResponse, uploadResponse) { | ||
assert.equal(uploadError, null); | ||
assert.notEqual(blobResponse, null); | ||
assert.ok(uploadResponse.isSuccessful); | ||
blobService.getBlobProperties(containerName, blobName, function (error4, blobProperties) { | ||
assert.equal(error4, null); | ||
assert.notEqual(blobProperties, null); | ||
assert.equal(blobProperties.contentMD5, blobMD5); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe('createBlockBlobFromText', function () { | ||
it('should work with access condition', function (done) { | ||
@@ -902,20 +838,2 @@ var blobName = testutil.generateId(blobNamesPrefix, blobNames, false); | ||
it('should work for small size from text', function (done) { | ||
var blobName = testutil.generateId(blobNamesPrefix, blobNames, false) + ' a'; | ||
var blobText = 'Hello World'; | ||
blobService.createBlockBlobFromText(containerName, blobName, blobText, function (uploadError, blobResponse, uploadResponse) { | ||
assert.equal(uploadError, null); | ||
assert.notEqual(blobResponse, null); | ||
assert.ok(uploadResponse.isSuccessful); | ||
blobService.getBlobToText(containerName, blobName, function (downloadErr, blobTextResponse) { | ||
assert.equal(downloadErr, null); | ||
assert.equal(blobTextResponse, blobText); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('should work with storeBlobContentMD5', function (done) { | ||
@@ -959,7 +877,12 @@ var blobName = testutil.generateId(blobNamesPrefix, blobNames, false) + ' a'; | ||
it('should work with basic file', function(done) { | ||
blobService.createBlockBlobFromFile(containerName, blockBlobName, blockFileName, uploadOptions, function (err) { | ||
blobService.createBlockBlobFromLocalFile(containerName, blockBlobName, blockFileName, uploadOptions, function (err) { | ||
assert.equal(err, null); | ||
blobService.getBlobProperties(containerName, blockBlobName, function (err, blob) { | ||
assert.equal(blob.contentMD5, blockBlobContentMD5); | ||
done(); | ||
blobService.getBlobToText(containerName, blockBlobName, function (downloadErr, blobTextResponse) { | ||
assert.equal(downloadErr, null); | ||
assert.equal(blobTextResponse, fileText); | ||
done(); | ||
}); | ||
}); | ||
@@ -970,7 +893,12 @@ }); | ||
it('should overwrite the existing blob', function(done) { | ||
blobService.createBlockBlobFromFile(containerName, blockBlobName, blockFileName, uploadOptions, function (err) { | ||
blobService.createBlockBlobFromText(containerName, blockBlobName, 'garbage', uploadOptions, function (err) { | ||
assert.equal(err, null); | ||
blobService.createBlockBlobFromFile(containerName, blockBlobName, blockFileName, uploadOptions, function (err) { | ||
blobService.createBlockBlobFromLocalFile(containerName, blockBlobName, blockFileName, uploadOptions, function (err) { | ||
assert.equal(err, null); | ||
done(); | ||
blobService.getBlobToText(containerName, blockBlobName, function (downloadErr, blobTextResponse) { | ||
assert.equal(downloadErr, null); | ||
assert.equal(blobTextResponse, fileText); | ||
done(); | ||
}); | ||
}); | ||
@@ -981,4 +909,5 @@ }); | ||
it('should work with zero size file', function(done) { | ||
blobService.createBlockBlobFromFile(containerName, blockBlobName, zeroSizeFileName, uploadOptions, function (err) { | ||
blobService.createBlockBlobFromLocalFile(containerName, blockBlobName, zeroSizeFileName, uploadOptions, function (err) { | ||
assert.equal(err, null); | ||
blobService.getBlobProperties(containerName, blockBlobName, function(err, blob) { | ||
@@ -992,7 +921,28 @@ assert.equal(blob.contentLength, 0); | ||
it('should work with content type', function (done) { | ||
var blobOptions = { contentType: 'text' }; | ||
blobService.createBlockBlobFromLocalFile(containerName, blockBlobName, blockFileName, blobOptions, function (uploadError, blobResponse, uploadResponse) { | ||
assert.equal(uploadError, null); | ||
assert.notEqual(blobResponse, null); | ||
assert.ok(uploadResponse.isSuccessful); | ||
blobService.getBlobProperties(containerName, blockBlobName, function (getBlobPropertiesErr, blobGetResponse) { | ||
assert.equal(getBlobPropertiesErr, null); | ||
assert.notEqual(blobGetResponse, null); | ||
assert.equal(blobOptions.contentType, blobGetResponse.contentType); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('should work with not existing file', function(done) { | ||
blobService.createBlockBlobFromFile(containerName, blockBlobName, notExistFileName, uploadOptions, function (err) { | ||
blobService.createBlockBlobFromLocalFile(containerName, blockBlobName, notExistFileName, uploadOptions, function (err) { | ||
assert.notEqual(err, null); | ||
assert.equal(path.basename(err.path), notExistFileName); | ||
done(); | ||
blobService.doesBlobExist(containerName, blockBlobName, function (existsErr, exists) { | ||
assert.equal(existsErr, null); | ||
assert.equal(exists, false); | ||
done(); | ||
}); | ||
}); | ||
@@ -1003,2 +953,5 @@ }); | ||
describe('CreateBlockBlobFromStream', function() { | ||
var len; | ||
var stream; | ||
before(function (done) { | ||
@@ -1011,2 +964,8 @@ blockBlobContentMD5 = writeFile(blockFileName, fileText); | ||
beforeEach(function (done) { | ||
len = Buffer.byteLength(fileText); | ||
stream = fs.createReadStream(blockFileName); | ||
done(); | ||
}); | ||
afterEach(function (done) { | ||
@@ -1019,4 +978,2 @@ blobService.deleteBlobIfExists(containerName, blockBlobName, function(error) { | ||
it('should work with basic file stream', function(done) { | ||
var len = Buffer.byteLength(fileText); | ||
var stream = fs.createReadStream(blockFileName); | ||
blobService.createBlockBlobFromStream(containerName, blockBlobName, stream, len, uploadOptions, function (err) { | ||
@@ -1026,3 +983,8 @@ assert.equal(err, null); | ||
assert.equal(blob.contentMD5, blockBlobContentMD5); | ||
done(); | ||
blobService.getBlobToText(containerName, blockBlobName, function (downloadErr, blobTextResponse) { | ||
assert.equal(downloadErr, null); | ||
assert.equal(blobTextResponse, fileText); | ||
done(); | ||
}); | ||
}); | ||
@@ -1038,4 +1000,2 @@ }); | ||
var len = Buffer.byteLength(fileText); | ||
var stream = fs.createReadStream(blockFileName); | ||
blobService.createBlockBlobFromStream(containerName, blockBlobName, stream, len, options, function (err) { | ||
@@ -1056,4 +1016,3 @@ assert.equal(err, null); | ||
}; | ||
var len = Buffer.byteLength(fileText); | ||
var stream = fs.createReadStream(blockFileName); | ||
blobService.createBlockBlobFromStream(containerName, blockBlobName, stream, len, options, function (err) { | ||
@@ -1068,2 +1027,25 @@ assert.equal(err, null); | ||
it('should work with content type', function (done) { | ||
var blobOptions = { contentType: 'text'}; | ||
blobService.createBlockBlobFromStream(containerName, blockBlobName, fs.createReadStream(blockFileName), fileText.length, blobOptions, function (uploadError, blobResponse, uploadResponse) { | ||
assert.equal(uploadError, null); | ||
assert.notEqual(blobResponse, null); | ||
assert.ok(uploadResponse.isSuccessful); | ||
blobService.getBlobToText(containerName, blockBlobName, function (downloadErr, blobTextResponse) { | ||
assert.equal(downloadErr, null); | ||
assert.equal(blobTextResponse, fileText); | ||
blobService.getBlobProperties(containerName, blockBlobName, function (getBlobPropertiesErr, blobGetResponse) { | ||
assert.equal(getBlobPropertiesErr, null); | ||
assert.notEqual(blobGetResponse, null); | ||
assert.equal(blobOptions.contentType, blobGetResponse.contentType); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
it('should work with parallelOperationsThreadCount in options', function(done) { | ||
@@ -1075,9 +1057,20 @@ var options = { | ||
var buffer = new Buffer(65 * 1024 * 1024); | ||
var buffer = new Buffer(30 * 1024 * 1024); | ||
buffer.fill(1); | ||
blockBlobContentMD5 = writeFile(blockFileName, buffer); | ||
var writeStream = fs.createWriteStream(blockFileName); | ||
writeStream.write(buffer); | ||
writeStream.write(buffer); | ||
writeStream.write(buffer); | ||
var stream = fs.createReadStream(blockFileName); | ||
blobService.createBlockBlobFromStream(containerName, blockBlobName, stream, buffer.length, options, function (err) { | ||
blobService.createBlockBlobFromStream(containerName, blockBlobName, stream, buffer.length * 3, options, function (err) { | ||
assert.equal(err, null); | ||
done(); | ||
blobService.getBlobProperties(containerName, blockBlobName, function (getBlobPropertiesErr, blobGetResponse) { | ||
assert.equal(getBlobPropertiesErr, null); | ||
assert.notEqual(blobGetResponse, null); | ||
assert.equal(blobGetResponse.contentLength, buffer.length * 3); | ||
done(); | ||
}); | ||
}); | ||
@@ -1096,3 +1089,2 @@ }); | ||
zeroFileContentMD5 = writeFile(zeroSizeFileName, zeroBuffer); | ||
blockBlobContentMD5 = writeFile(blockFileName, fileText); | ||
done(); | ||
@@ -1108,7 +1100,13 @@ }); | ||
it('should work with basic file', function(done) { | ||
blobService.createPageBlobFromFile(containerName, pageBlobName, pageFileName, function (err) { | ||
blobService.createPageBlobFromLocalFile(containerName, pageBlobName, pageFileName, function (err) { | ||
assert.equal(err, null); | ||
blobService.getBlobProperties(containerName, pageBlobName, function(err, blob) { | ||
blobService.getBlobProperties(containerName, pageBlobName, function (err1, blob) { | ||
assert.equal(err1, null); | ||
assert.equal(blob.contentMD5, undefined); | ||
done(); | ||
blobService.getBlobToText(containerName, pageBlobName, function (downloadErr, blobTextResponse) { | ||
assert.equal(downloadErr, null); | ||
assert.equal(blobTextResponse, pageBlobBuffer.toString()); | ||
done(); | ||
}); | ||
}); | ||
@@ -1119,6 +1117,7 @@ }); | ||
it('should work with speed summary', function(done) { | ||
var speedSummary = blobService.createPageBlobFromFile(containerName, pageBlobName, pageFileName, function (err) { | ||
var speedSummary = blobService.createPageBlobFromLocalFile(containerName, pageBlobName, pageFileName, function (err) { | ||
assert.equal(err, null); | ||
blobService.getBlobProperties(containerName, pageBlobName, function(err, blob) { | ||
assert.equal(err, null); | ||
blobService.getBlobProperties(containerName, pageBlobName, function (err1, blob) { | ||
assert.equal(err1, null); | ||
assert.equal(blob.contentMD5, undefined); | ||
@@ -1139,5 +1138,7 @@ assert.equal(speedSummary.getTotalSize(false), 1024); | ||
blobService.createPageBlobFromFile(containerName, pageBlobName, pageFileName, options, function (err) { | ||
blobService.createPageBlobFromLocalFile(containerName, pageBlobName, pageFileName, options, function (err) { | ||
assert.equal(err, null); | ||
blobService.getBlobProperties(containerName, pageBlobName, function(err, blob) { | ||
blobService.getBlobProperties(containerName, pageBlobName, function (getErr, blob) { | ||
assert.equal(getErr, null); | ||
assert.equal(blob.contentMD5, pageBlobContentMD5); | ||
@@ -1155,13 +1156,20 @@ done(); | ||
blobService.createPageBlobFromFile(containerName, pageBlobName, pageFileName, options, function (err) { | ||
blobService.createPageBlobFromLocalFile(containerName, pageBlobName, pageFileName, options, function (err) { | ||
assert.equal(err, null); | ||
blobService.getBlobProperties(containerName, pageBlobName, function(err, blob) { | ||
assert.equal(blob.contentMD5, pageBlobContentMD5); | ||
assert.equal(blob.contentLength, 1024); | ||
blobService.getBlobProperties(containerName, pageBlobName, function (err2, blob1) { | ||
assert.equal(err2, null); | ||
assert.notEqual(blob1, null); | ||
assert.equal(blob1.contentMD5, pageBlobContentMD5); | ||
assert.equal(blob1.contentLength, 1024); | ||
options.contentMD5Header = null; | ||
blobService.createPageBlobFromFile(containerName, pageBlobName, page2KFileName, options, function (err) { | ||
assert.equal(err, null); | ||
blobService.getBlobProperties(containerName, pageBlobName, function(err, blob) { | ||
assert.equal(blob.contentLength, 2 * 1024); | ||
assert.equal(blob.contentMD5, pageBlob2KContentMD5); | ||
blobService.createPageBlobFromLocalFile(containerName, pageBlobName, page2KFileName, options, function (err3) { | ||
assert.equal(err3, null); | ||
blobService.getBlobProperties(containerName, pageBlobName, function (err4, blob2) { | ||
assert.equal(err4, null); | ||
assert.notEqual(blob2, null); | ||
assert.equal(blob2.contentLength, 2 * 1024); | ||
assert.equal(blob2.contentMD5, pageBlob2KContentMD5); | ||
done(); | ||
@@ -1176,5 +1184,7 @@ }); | ||
uploadOptions.storeBlobContentMD5 = true; | ||
blobService.createPageBlobFromFile(containerName, pageBlobName, zeroSizeFileName, uploadOptions, function (err) { | ||
assert.equal(err, null); | ||
blobService.getBlobProperties(containerName, pageBlobName, function(err, blob) { | ||
blobService.createPageBlobFromLocalFile(containerName, pageBlobName, zeroSizeFileName, uploadOptions, function (err1) { | ||
assert.equal(err1, null); | ||
blobService.getBlobProperties(containerName, pageBlobName, function (err2, blob) { | ||
assert.equal(err2, null); | ||
assert.equal(blob.contentLength, 0); | ||
@@ -1188,6 +1198,11 @@ assert.equal(blob.contentMD5, zeroFileContentMD5); | ||
it('should work with not existing file', function(done) { | ||
blobService.createPageBlobFromFile(containerName, pageBlobName, notExistFileName, uploadOptions, function (err) { | ||
blobService.createPageBlobFromLocalFile(containerName, pageBlobName, notExistFileName, uploadOptions, function (err) { | ||
assert.notEqual(err, null); | ||
assert.equal(path.basename(err.path), notExistFileName); | ||
done(); | ||
blobService.doesBlobExist(containerName, pageBlobName, function (existsErr, exists) { | ||
assert.equal(existsErr, null); | ||
assert.equal(exists, false); | ||
done(); | ||
}); | ||
}); | ||
@@ -1198,2 +1213,7 @@ }); | ||
describe('CreatePageBlobFromStream', function() { | ||
before(function (done) { | ||
pageBlobContentMD5 = writeFile(pageBlobName, fileText); | ||
done(); | ||
}); | ||
//Most cases are in CreatePageBlobFromFile | ||
@@ -1204,5 +1224,12 @@ it('should work with basic file', function(done) { | ||
assert.equal(err, null); | ||
blobService.getBlobProperties(containerName, pageBlobName, function(err, blob) { | ||
blobService.getBlobProperties(containerName, pageBlobName, function (err1, blob) { | ||
assert.equal(err1, null); | ||
assert.equal(blob.contentMD5, undefined); | ||
done(); | ||
blobService.getBlobToText(containerName, pageBlobName, function (downloadErr, blobTextResponse) { | ||
assert.equal(downloadErr, null); | ||
assert.equal(blobTextResponse, pageBlobBuffer.toString()); | ||
done(); | ||
}); | ||
}); | ||
@@ -1214,3 +1241,2 @@ }); | ||
var options = { | ||
blockIdPrefix : blockIdPrefix, | ||
parallelOperationThreadCount : 4 | ||
@@ -1221,7 +1247,14 @@ }; | ||
buffer.fill(1); | ||
blockBlobContentMD5 = writeFile(pageFileName, buffer); | ||
var stream = fs.createReadStream(pageFileName); | ||
blobService.createPageBlobFromStream(containerName, pageBlobName, stream, buffer.length, options, function (err) { | ||
assert.equal(err, null); | ||
done(); | ||
blobService.getBlobProperties(containerName, pageBlobName, function (getBlobPropertiesErr, blobGetResponse) { | ||
assert.equal(getBlobPropertiesErr, null); | ||
assert.notEqual(blobGetResponse, null); | ||
assert.equal(blobGetResponse.contentLength, buffer.length); | ||
done(); | ||
}); | ||
}); | ||
@@ -1231,14 +1264,19 @@ }); | ||
describe('GetBlobToFile', function() { | ||
describe('BlockBlob', function() { | ||
var blockBlobName = 'blockblob-test-getblob'; | ||
describe('GetBlockBlobToFile', function() { | ||
var blockBlobName = 'blockblob-test-getblob'; | ||
it('should work with basic block blob', function(done) { | ||
blockBlobContentMD5 = writeFile(blockFileName, fileText); | ||
blobService.createBlockBlobFromFile(containerName, blockBlobName, blockFileName, uploadOptions, function (err) { | ||
it('should work with basic block blob', function(done) { | ||
blockBlobContentMD5 = writeFile(blockFileName, fileText); | ||
blobService.createBlockBlobFromLocalFile(containerName, blockBlobName, blockFileName, uploadOptions, function (err) { | ||
assert.equal(err, null); | ||
blobService.getBlobToLocalFile(containerName, blockBlobName, downloadName, function(err, blob) { | ||
assert.equal(err, null); | ||
assert.equal(blob.contentMD5, blockBlobContentMD5); | ||
blobService.getBlobToFile(containerName, blockBlobName, downloadName, function(err, blob) { | ||
assert.equal(err, null); | ||
assert.equal(blob.contentMD5, blockBlobContentMD5); | ||
var exists = azureutil.pathExistsSync(downloadName); | ||
assert.equal(exists, true); | ||
fs.readFile(downloadName, function (err, text) { | ||
assert.equal(text, fileText); | ||
done(); | ||
@@ -1248,12 +1286,19 @@ }); | ||
}); | ||
}); | ||
it('should calculate content md5', function(done) { | ||
blockBlobContentMD5 = writeFile(blockFileName, fileText); | ||
blobService.createBlockBlobFromFile(containerName, blockBlobName, blockFileName, uploadOptions, function (err) { | ||
it('should calculate content md5', function(done) { | ||
blockBlobContentMD5 = writeFile(blockFileName, fileText); | ||
blobService.createBlockBlobFromLocalFile(containerName, blockBlobName, blockFileName, uploadOptions, function (err) { | ||
assert.equal(err, null); | ||
var options = {disableContentMD5Validation : false}; | ||
blobService.getBlobToLocalFile(containerName, blockBlobName, downloadName, options, function(err, blob) { | ||
assert.equal(err, null); | ||
assert.equal(blob.contentMD5, blockBlobContentMD5); | ||
var options = {disableContentMD5Validation : false}; | ||
blobService.getBlobToFile(containerName, blockBlobName, downloadName, options, function(err, blob) { | ||
assert.equal(err, null); | ||
assert.equal(blob.contentMD5, blockBlobContentMD5); | ||
var exists = azureutil.pathExistsSync(downloadName); | ||
assert.equal(exists, true); | ||
fs.readFile(downloadName, function (err, text) { | ||
assert.equal(text, fileText); | ||
done(); | ||
@@ -1264,14 +1309,21 @@ }); | ||
}); | ||
}); | ||
describe('PageBlob', function() { | ||
var pageBlobName = 'pageblob-test-getblob'; | ||
describe('GetPageBlobToFile', function() { | ||
var pageBlobName = 'pageblob-test-getblob'; | ||
it('should work with basic page blob', function(done) { | ||
pageBlobBuffer.fill(1); | ||
pageBlobContentMD5 = writeFile(pageFileName, pageBlobBuffer); | ||
blobService.createPageBlobFromFile(containerName, pageBlobName, pageFileName, {storeBlobContentMD5: true}, function (err) { | ||
it('should work with basic page blob', function(done) { | ||
pageBlobBuffer.fill(1); | ||
pageBlobContentMD5 = writeFile(pageFileName, pageBlobBuffer); | ||
blobService.createPageBlobFromLocalFile(containerName, pageBlobName, pageFileName, {storeBlobContentMD5: true}, function (err) { | ||
assert.equal(err, null); | ||
blobService.getBlobToLocalFile(containerName, pageBlobName, downloadName, function(err, blob) { | ||
assert.equal(err, null); | ||
blobService.getBlobToFile(containerName, pageBlobName, downloadName, function(err, blob) { | ||
assert.equal(err, null); | ||
assert.equal(blob.contentMD5, pageBlobContentMD5); | ||
assert.equal(blob.contentMD5, pageBlobContentMD5); | ||
var exists = azureutil.pathExistsSync(downloadName); | ||
assert.equal(exists, true); | ||
fs.readFile(downloadName, function (err, text) { | ||
assert.equal(text.toString(), pageBlobBuffer.toString()); | ||
done(); | ||
@@ -1281,12 +1333,19 @@ }); | ||
}); | ||
}); | ||
it('should calculate content md5', function(done) { | ||
pageBlobBuffer.fill(1); | ||
pageBlobContentMD5 = writeFile(pageFileName, pageBlobBuffer); | ||
blobService.createPageBlobFromFile(containerName, pageBlobName, pageFileName, {storeBlobContentMD5: true}, function (err) { | ||
assert.equal(err, null); | ||
var options = {disableContentMD5Validation : false}; | ||
blobService.getBlobToFile(containerName, pageBlobName, downloadName, options, function(err, blob) { | ||
assert.equal(err, null); | ||
assert.equal(blob.contentMD5, pageBlobContentMD5); | ||
it('should calculate content md5', function(done) { | ||
pageBlobBuffer.fill(1); | ||
pageBlobContentMD5 = writeFile(pageFileName, pageBlobBuffer); | ||
blobService.createPageBlobFromLocalFile(containerName, pageBlobName, pageFileName, {storeBlobContentMD5: true}, function (err) { | ||
assert.equal(err, null); | ||
var options = {disableContentMD5Validation : false}; | ||
blobService.getBlobToLocalFile(containerName, pageBlobName, downloadName, options, function(err, blob) { | ||
assert.equal(err, null); | ||
assert.equal(blob.contentMD5, pageBlobContentMD5); | ||
var exists = azureutil.pathExistsSync(downloadName); | ||
assert.equal(exists, true); | ||
fs.readFile(downloadName, function (err, text) { | ||
assert.equal(text.toString(), pageBlobBuffer.toString()); | ||
done(); | ||
@@ -1293,0 +1352,0 @@ }); |
Sorry, the diff of this file is too big to display
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
1505371
107
29576
334
3
76