2121'use strict' ;
2222
2323var crypto = require ( 'crypto' ) ;
24- var Duplexify = require ( 'duplexify' ) ;
24+ var duplexify = require ( 'duplexify' ) ;
2525var extend = require ( 'extend' ) ;
26- var nodeutil = require ( 'util' ) ;
27- var through = require ( 'through2' ) ;
2826var uuid = require ( 'node-uuid' ) ;
2927
3028/**
@@ -49,60 +47,15 @@ var STORAGE_UPLOAD_BASE_URL = 'https://www.googleapis.com/upload/storage/v1/b';
4947 * extra call to `setMetadata`.
5048 */
5149/**
52- * A File object is created from your Bucket object, using
53- * {module:storage/bucket#file}. File objects are duplex streams, which means
54- * they can be read from and written to. No API requests are made until you
55- * attempt to read or write from the file, by attaching a Readable or Writable
56- * stream. This means you can create a file on the fly, but you will get an
57- * error if you try to read from a file in your bucket that does not exist.
58- *
59- * For more help with using and understanding Node.js streams, see the
60- * [Stream documentation]{@link http://nodejs.org/api/stream.html}.
50+ * A File object is created from your Bucket object using
51+ * {module:storage/bucket#file}.
6152 *
6253 * @alias module:storage/file
6354 * @constructor
6455 *
6556 * @example
66- * //-
67- * // <h4>Downloading a File</h4>
68- * //
69- * // The example below demonstrates how we can reference a remote file, then
70- * // pipe its contents to a local file. This is effectively creating a local
71- * // backup of your remote data.
72- * //-
73- * var fs = require('fs');
74- * myBucket.file('image.png')
75- * .pipe(fs.createWriteStream('/Users/stephen/Photos/image.png'))
76- * .on('error', function(err) {});
77- *
78- * //-
79- * // <h4>Uploading a File</h4>
80- * //
81- * // Now, consider a case where we want to upload a file to your bucket. You
82- * // have the option of using {module:storage/bucket#upload}, but that is just
83- * // a convenience method which will do the following.
84- * //-
85- * var fs = require('fs');
86- * fs.createReadStream('/Users/stephen/Photos/birthday-at-the-zoo/panda.jpg')
87- * .pipe(myBucket.file('panda.jpg'))
88- * .on('error', function(err) {});
89- *
90- * //-
91- * // <h4>Uploading a File with Metadata</h4>
92- * //
93- * // One last case you may run into is when you want to upload a file to your
94- * // bucket and set its metadata at the same time. Like above, you can use
95- * // {module:storage/bucket#upload} to do this, which again, is just a wrapper
96- * // around the following.
97- * //-
98- * var fs = require('fs');
99- * fs.createReadStream('/Users/stephen/Photos/birthday-at-the-zoo/panda.jpg')
100- * .pipe(myBucket.file('panda.jpg', { contentType: 'image/jpeg' }))
101- * .on('error', function(err) {});
10257 */
10358function File ( bucket , name , metadata ) {
104- Duplexify . call ( this ) ;
105-
10659 if ( ! name ) {
10760 throw Error ( 'A file name must be specified.' ) ;
10861 }
@@ -114,16 +67,8 @@ function File(bucket, name, metadata) {
11467 enumerable : true ,
11568 value : name
11669 } ) ;
117-
118- this . events_ = {
119- _read : this . _read ,
120- _write : this . _write
121- } ;
122- this . bindEvents_ ( ) ;
12370}
12471
125- nodeutil . inherits ( File , Duplexify ) ;
126-
12772/**
12873 * Copy this file to another file. By default, this will copy the file to the
12974 * same bucket, but you can choose to copy it to another Bucket by providing
@@ -133,7 +78,6 @@ nodeutil.inherits(File, Duplexify);
13378 *
13479 * @param {string|{module:storage/bucket}|{module:storage/file} } destination -
13580 * Destination file.
136- * @param {object= } metadata - Destination file metadata object.
13781 * @param {function= } callback - The callback function.
13882 *
13983 * @example
@@ -230,6 +174,115 @@ File.prototype.copy = function(destination, callback) {
230174 } ) ;
231175} ;
232176
177+
178+ /**
179+ * Create a readable stream to read the contents of the remote file. It can be
180+ * piped to a writable stream or listened to for 'data' events to read a file's
181+ * contents.
182+ *
183+ * @example
184+ * //-
185+ * // <h4>Downloading a File</h4>
186+ * //
187+ * // The example below demonstrates how we can reference a remote file, then
188+ * // pipe its contents to a local file. This is effectively creating a local
189+ * // backup of your remote data.
190+ * //-
191+ * var fs = require('fs');
192+ * var image = myBucket.file('image.png');
193+ *
194+ * image.createReadStream()
195+ * .pipe(fs.createWriteStream('/Users/stephen/Photos/image.png'))
196+ * .on('error', function(err) {});
197+ */
198+ File . prototype . createReadStream = function ( ) {
199+ var bucket = this . bucket ;
200+ var dup = duplexify ( ) ;
201+ function createAuthorizedReq ( uri ) {
202+ bucket . connection_ . createAuthorizedReq ( { uri : uri } , function ( err , req ) {
203+ if ( err ) {
204+ dup . emit ( 'error' , err ) ;
205+ return ;
206+ }
207+ dup . setReadable ( bucket . connection_ . requester ( req ) ) ;
208+ } ) ;
209+ }
210+ if ( this . metadata . mediaLink ) {
211+ createAuthorizedReq ( this . metadata . mediaLink ) ;
212+ } else {
213+ this . getMetadata ( function ( err , metadata ) {
214+ if ( err ) {
215+ dup . emit ( 'error' , err ) ;
216+ return ;
217+ }
218+ createAuthorizedReq ( metadata . mediaLink ) ;
219+ } ) ;
220+ }
221+ return dup ;
222+ } ;
223+
224+ /**
225+ * Create a writable stream to overwrite the contents of the file in your
226+ * bucket.
227+ *
228+ * A File object can also be used to create files for the first time.
229+ *
230+ * @param {object= } metadata - Set the metadata for this file.
231+ *
232+ * @example
233+ * //-
234+ * // <h4>Uploading a File</h4>
235+ * //
236+ * // Now, consider a case where we want to upload a file to your bucket. You
237+ * // have the option of using {module:storage/bucket#upload}, but that is just
238+ * // a convenience method which will do the following.
239+ * //-
240+ * var fs = require('fs');
241+ * var image = myBucket.file('image.png');
242+ *
243+ * fs.createReadStream('/Users/stephen/Photos/birthday-at-the-zoo/panda.jpg')
244+ * .pipe(image.createWriteStream())
245+ * .on('error', function(err) {});
246+ *
247+ * //-
248+ * // <h4>Uploading a File with Metadata</h4>
249+ * //
250+ * // One last case you may run into is when you want to upload a file to your
251+ * // bucket and set its metadata at the same time. Like above, you can use
252+ * // {module:storage/bucket#upload} to do this, which is just a wrapper around
253+ * // the following.
254+ * //-
255+ * var fs = require('fs');
256+ * var image = myBucket.file('image.png');
257+ *
258+ * fs.createReadStream('/Users/stephen/Photos/birthday-at-the-zoo/panda.jpg')
259+ * .pipe(image.createWriteStream({ contentType: 'image/jpeg' }))
260+ * .on('error', function(err) {});
261+ */
262+ File . prototype . createWriteStream = function ( metadata ) {
263+ var that = this ;
264+ var dup = duplexify ( ) ;
265+ this . getWritableStream_ ( metadata , function ( err , writable ) {
266+ if ( err ) {
267+ dup . emit ( 'error' , err ) ;
268+ return ;
269+ }
270+ writable . on ( 'complete' , function ( res ) {
271+ util . handleResp ( null , res , res . body , function ( err , data ) {
272+ if ( err ) {
273+ dup . emit ( 'error' , err ) ;
274+ return ;
275+ }
276+ that . metadata = data ;
277+ dup . emit ( 'complete' , data ) ;
278+ } ) ;
279+ } ) ;
280+ dup . setWritable ( writable ) ;
281+ dup . pipe ( writable ) ;
282+ } ) ;
283+ return dup ;
284+ } ;
285+
233286/**
234287 * Delete the file.
235288 *
@@ -245,7 +298,6 @@ File.prototype.delete = function(callback) {
245298 callback ( err ) ;
246299 return ;
247300 }
248- this . removeAllListeners ( ) ;
249301 callback ( ) ;
250302 } . bind ( this ) ) ;
251303} ;
@@ -378,61 +430,19 @@ File.prototype.setMetadata = function(metadata, callback) {
378430 * out.
379431 */
380432
381- /**
382- * Set up and tear down listeners for a new stream. First, overwrite the _read
383- * and _write methods for the first time a stream is accessed. This causes us to
384- * fetch an authorized connection before opening the pipe for more data. The
385- * data is not buffered into memory, rather, it ceases pulling until we say so.
386- *
387- * This method is called after a complete event, when we also destroy the
388- * Duplexify stream and re-apply its constructor to our file instance.
389- *
390- * @private
391- */
392- File . prototype . bindEvents_ = function ( ) {
393- var that = this ;
394-
395- var _read = this . events_ . _read ;
396- this . _read = function ( ) {
397- that . _read = _read . bind ( that ) ;
398- that . setReadableStream_ ( ) ;
399- _read . apply ( that , util . toArray ( arguments ) ) ;
400- } ;
401-
402- var _write = this . events_ . _write ;
403- this . _write = function ( ) {
404- that . _write = _write . bind ( that ) ;
405- that . setWritableStream_ ( ) ;
406- _write . apply ( that , util . toArray ( arguments ) ) ;
407- } ;
408-
409- this . on ( 'error' , function ( ) {
410- that . emit ( 'end' ) ;
411- } ) ;
412-
413- this . on ( 'complete' , function ( ) {
414- that . emit ( 'end' ) ;
415- } ) ;
416-
417- this . on ( 'end' , function ( ) {
418- that . removeAllListeners ( ) ;
419- that . destroy ( ) ;
420- Duplexify . call ( that ) ;
421- that . bindEvents_ ( ) ;
422- } ) ;
423- } ;
424-
425433/**
426434 * Get a remote stream to begin piping a readable stream to.
427435 *
428436 * @private
429437 */
430- File . prototype . getWritableStream_ = function ( callback ) {
438+ File . prototype . getWritableStream_ = function ( metadata , callback ) {
439+ if ( ! callback ) {
440+ callback = metadata ;
441+ metadata = { } ;
442+ }
431443 var that = this ;
432444 var boundary = uuid . v4 ( ) ;
433- var metadata = extend ( {
434- contentType : 'text/plain'
435- } , this . metadata ) ;
445+ metadata = extend ( { contentType : 'text/plain' } , metadata ) ;
436446 this . bucket . connection_ . createAuthorizedReq ( {
437447 method : 'POST' ,
438448 uri : util . format ( '{base}/{bucket}/o' , {
@@ -469,60 +479,4 @@ File.prototype.getWritableStream_ = function(callback) {
469479 } ) ;
470480} ;
471481
472- /**
473- * Set the readable stream for Duplexify after making an authorized connection.
474- *
475- * @private
476- */
477- File . prototype . setReadableStream_ = function ( ) {
478- var that = this ;
479- var bucket = this . bucket ;
480- function createAuthorizedReq ( uri ) {
481- bucket . connection_ . createAuthorizedReq ( { uri : uri } , function ( err , req ) {
482- if ( err ) {
483- that . emit ( 'error' , err ) ;
484- return ;
485- }
486- that . setReadable ( bucket . connection_ . requester ( req ) ) ;
487- } ) ;
488- }
489- if ( this . metadata . mediaLink ) {
490- createAuthorizedReq ( this . metadata . mediaLink ) ;
491- } else {
492- this . getMetadata ( function ( err , metadata ) {
493- if ( err ) {
494- that . emit ( 'error' , err ) ;
495- return ;
496- }
497- createAuthorizedReq ( metadata . mediaLink ) ;
498- } ) ;
499- }
500- } ;
501-
502- /**
503- * Set the writable stream for Duplexify after making an authorized connection.
504- *
505- * @private
506- */
507- File . prototype . setWritableStream_ = function ( ) {
508- var that = this ;
509- this . getWritableStream_ ( function ( err , writable ) {
510- if ( err ) {
511- that . emit ( 'error' , err ) ;
512- return ;
513- }
514- writable . on ( 'complete' , function ( res ) {
515- util . handleResp ( null , res , res . body , function ( err , data ) {
516- if ( err ) {
517- that . emit ( 'error' , err ) ;
518- return ;
519- }
520- that . emit ( 'complete' , data ) ;
521- } ) ;
522- } ) ;
523- that . setWritable ( writable ) ;
524- through ( ) . pipe ( writable ) ;
525- } ) ;
526- } ;
527-
528482module . exports = File ;
0 commit comments