| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 | /* global FS, _storageAdapters:true, EventEmitter */// #############################################################################//// STORAGE ADAPTER//// #############################################################################_storageAdapters = {};FS.StorageAdapter = function(storeName, options, api) {  var self = this, fileKeyMaker;  options = options || {};  // If storeName is the only argument, a string and the SA already found  // we will just return that SA  if (arguments.length === 1 && storeName === '' + storeName &&          typeof _storageAdapters[storeName] !== 'undefined')    return _storageAdapters[storeName];  // Verify that the storage adapter defines all the necessary API methods  if (typeof api === 'undefined') {    throw new Error('FS.StorageAdapter please define an api');  }    FS.Utility.each('fileKey,remove,typeName,createReadStream,createWriteStream'.split(','), function(name) {    if (typeof api[name] === 'undefined') {      throw new Error('FS.StorageAdapter please define an api. "' + name + '" ' + (api.typeName || ''));    }  });  // Create an internal namespace, starting a name with underscore is only  // allowed for stores marked with options.internal === true  if (options.internal !== true && storeName[0] === '_') {    throw new Error('A storage adapter name may not begin with "_"');  }  if (storeName.indexOf('.') !== -1) {    throw new Error('A storage adapter name may not contain a "."');  }  // store reference for easy lookup by storeName  if (typeof _storageAdapters[storeName] !== 'undefined') {    throw new Error('Storage name already exists: "' + storeName + '"');  } else {    _storageAdapters[storeName] = self;  }  // User can customize the file key generation function  if (typeof options.fileKeyMaker === "function") {    fileKeyMaker = options.fileKeyMaker;  } else {    fileKeyMaker = api.fileKey;  }  // User can provide a function to adjust the fileObj  // before it is written to the store.  var beforeWrite = options.beforeWrite;  // extend self with options and other info  FS.Utility.extend(this, options, {    name: storeName,    typeName: api.typeName  });  // Create a nicer abstracted adapter interface  self.adapter = {};  self.adapter.fileKey = function(fileObj) {    return fileKeyMaker(fileObj);  };  // Return readable stream for fileKey  self.adapter.createReadStreamForFileKey = function(fileKey, options) {    if (FS.debug) console.log('createReadStreamForFileKey ' + storeName);    return FS.Utility.safeStream( api.createReadStream(fileKey, options) );  };  // Return readable stream for fileObj  self.adapter.createReadStream = function(fileObj, options) {    if (FS.debug) console.log('createReadStream ' + storeName);    if (self.internal) {      // Internal stores take a fileKey      return self.adapter.createReadStreamForFileKey(fileObj, options);    }    return FS.Utility.safeStream( self._transform.createReadStream(fileObj, options) );  };  function logEventsForStream(stream) {    if (FS.debug) {      stream.on('stored', function() {        console.log('-----------STORED STREAM', storeName);      });      stream.on('close', function() {        console.log('-----------CLOSE STREAM', storeName);      });      stream.on('end', function() {        console.log('-----------END STREAM', storeName);      });      stream.on('finish', function() {        console.log('-----------FINISH STREAM', storeName);      });      stream.on('error', function(error) {        console.log('-----------ERROR STREAM', storeName, error && (error.message || error.code));      });    }  }  // Return writeable stream for fileKey  self.adapter.createWriteStreamForFileKey = function(fileKey, options) {    if (FS.debug) console.log('createWriteStreamForFileKey ' + storeName);    var writeStream = FS.Utility.safeStream( api.createWriteStream(fileKey, options) );    logEventsForStream(writeStream);    return writeStream;  };  // Return writeable stream for fileObj  self.adapter.createWriteStream = function(fileObj, options) {    if (FS.debug) console.log('createWriteStream ' + storeName + ', internal: ' + !!self.internal);        if (self.internal) {      // Internal stores take a fileKey      return self.adapter.createWriteStreamForFileKey(fileObj, options);    }    // If we haven't set name, type, or size for this version yet,    // set it to same values as original version. We don't save    // these to the DB right away because they might be changed    // in a transformWrite function.    if (!fileObj.name({store: storeName})) {      fileObj.name(fileObj.name(), {store: storeName, save: false});    }    if (!fileObj.type({store: storeName})) {      fileObj.type(fileObj.type(), {store: storeName, save: false});    }    if (!fileObj.size({store: storeName})) {      fileObj.size(fileObj.size(), {store: storeName, save: false});    }    // Call user function to adjust file metadata for this store.    // We support updating name, extension, and/or type based on    // info returned in an object. Or `fileObj` could be    // altered directly within the beforeWrite function.    if (beforeWrite) {      var fileChanges = beforeWrite(fileObj);      if (typeof fileChanges === "object") {        if (fileChanges.extension) {          fileObj.extension(fileChanges.extension, {store: storeName, save: false});        } else if (fileChanges.name) {          fileObj.name(fileChanges.name, {store: storeName, save: false});        }        if (fileChanges.type) {          fileObj.type(fileChanges.type, {store: storeName, save: false});        }      }    }    var writeStream = FS.Utility.safeStream( self._transform.createWriteStream(fileObj, options) );    logEventsForStream(writeStream);    // Its really only the storage adapter who knows if the file is uploaded    //    // We have to use our own event making sure the storage process is completed    // this is mainly    writeStream.safeOn('stored', function(result) {      if (typeof result.fileKey === 'undefined') {        throw new Error('SA ' + storeName + ' type ' + api.typeName + ' did not return a fileKey');      }      if (FS.debug) console.log('SA', storeName, 'stored', result.fileKey);      // Set the fileKey      fileObj.copies[storeName].key = result.fileKey;      // Update the size, as provided by the SA, in case it was changed by stream transformation      if (typeof result.size === "number") {        fileObj.copies[storeName].size = result.size;      }      // Set last updated time, either provided by SA or now      fileObj.copies[storeName].updatedAt = result.storedAt || new Date();      // If the file object copy havent got a createdAt then set this      if (typeof fileObj.copies[storeName].createdAt === 'undefined') {        fileObj.copies[storeName].createdAt = fileObj.copies[storeName].updatedAt;      }      fileObj._saveChanges(storeName);      // There is code in transform that may have set the original file size, too.      fileObj._saveChanges('_original');    });    // Emit events from SA    writeStream.once('stored', function(/*result*/) {      // XXX Because of the way stores inherit from SA, this will emit on every store.      // Maybe need to rewrite the way we inherit from SA?      var emitted = self.emit('stored', storeName, fileObj);      if (FS.debug && !emitted) {        console.log(fileObj.name() + ' was successfully stored in the ' + storeName + ' store. You are seeing this informational message because you enabled debugging and you have not defined any listeners for the "stored" event on this store.');      }    });    writeStream.on('error', function(error) {      // XXX We could wrap and clarify error      // XXX Because of the way stores inherit from SA, this will emit on every store.      // Maybe need to rewrite the way we inherit from SA?      var emitted = self.emit('error', storeName, error, fileObj);      if (FS.debug && !emitted) {        console.log(error);      }    });    return writeStream;  };  //internal  self._removeAsync = function(fileKey, callback) {    // Remove the file from the store    api.remove.call(self, fileKey, callback);  };  /**   * @method FS.StorageAdapter.prototype.remove   * @public   * @param {FS.File} fsFile The FS.File instance to be stored.   * @param {Function} [callback] If not provided, will block and return true or false   *   * Attempts to remove a file from the store. Returns true if removed or not   * found, or false if the file couldn't be removed.   */  self.adapter.remove = function(fileObj, callback) {    if (FS.debug) console.log("---SA REMOVE");    // Get the fileKey    var fileKey = (fileObj instanceof FS.File) ? self.adapter.fileKey(fileObj) : fileObj;    if (callback) {      return self._removeAsync(fileKey, FS.Utility.safeCallback(callback));    } else {      return Meteor.wrapAsync(self._removeAsync)(fileKey);    }  };  self.remove = function(fileObj, callback) {    // Add deprecation note    console.warn('Storage.remove is deprecating, use "Storage.adapter.remove"');    return self.adapter.remove(fileObj, callback);  };  if (typeof api.init === 'function') {    Meteor.wrapAsync(api.init.bind(self))();  }  // This supports optional transformWrite and transformRead  self._transform = new FS.Transform({    adapter: self.adapter,    // Optional transformation functions:    transformWrite: options.transformWrite,    transformRead: options.transformRead  });};Npm.require('util').inherits(FS.StorageAdapter, EventEmitter);
 |