Skip to content

Commit 14b48b5

Browse files
add validation choices to createreadstream
1 parent e8764df commit 14b48b5

2 files changed

Lines changed: 83 additions & 14 deletions

File tree

lib/storage/file.js

Lines changed: 82 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
'use strict';
2222

23+
var crc = require('fast-crc32c');
2324
var crypto = require('crypto');
2425
var duplexify = require('duplexify');
2526
var request = require('request');
@@ -213,6 +214,13 @@ File.prototype.copy = function(destination, callback) {
213214
* 911. If you receive this error, the best recourse is to try downloading the
214215
* file again.
215216
*
217+
* @param {object=} options - Configuration object.
218+
* @param {string|boolean} options.validation - Possible values: `"md5"`,
219+
* `"crc32c"`, or `false`. By default, data integrity is validated with an
220+
* MD5 checksum for maximum reliability. CRC32c will provide better
221+
* performance with less reliability. You may also choose to skip validation
222+
* completely, however this is **not recommended**.
223+
*
216224
* @example
217225
* //-
218226
* // <h4>Downloading a File</h4>
@@ -228,19 +236,45 @@ File.prototype.copy = function(destination, callback) {
228236
* .pipe(fs.createWriteStream('/Users/stephen/Photos/image.png'))
229237
* .on('error', function(err) {});
230238
*/
231-
File.prototype.createReadStream = function() {
239+
File.prototype.createReadStream = function(options) {
240+
options = options || {};
241+
232242
var that = this;
233243
var throughStream = through();
234244

235-
this.getMetadata(function(err, metadata) {
236-
if (err) {
237-
throughStream.emit('error', err);
238-
throughStream.end();
239-
return;
245+
var validations = ['crc32c', 'md5'];
246+
var validation;
247+
248+
if (util.is(options.validation, 'string')) {
249+
options.validation = options.validation.toLowerCase();
250+
251+
if (validations.indexOf(options.validation) > -1) {
252+
validation = options.validation;
253+
} else {
254+
validation = 'all';
240255
}
256+
}
241257

242-
createAuthorizedReq(metadata.mediaLink);
243-
});
258+
if (util.is(options.validation, 'undefined')) {
259+
validation = 'all';
260+
}
261+
262+
var crc32c = validation === 'crc32c' || validation === 'all';
263+
var md5 = validation === 'md5' || validation === 'all';
264+
265+
if (this.metadata.mediaLink) {
266+
createAuthorizedReq(this.metadata.mediaLink);
267+
} else {
268+
this.getMetadata(function(err, metadata) {
269+
if (err) {
270+
throughStream.emit('error', err);
271+
throughStream.end();
272+
return;
273+
}
274+
275+
createAuthorizedReq(metadata.mediaLink);
276+
});
277+
}
244278

245279
return throughStream;
246280

@@ -261,6 +295,7 @@ File.prototype.createReadStream = function() {
261295

262296
// For data integrity, hash the contents of the stream as we receive it
263297
// from the server.
298+
var localCrc32cHash;
264299
var localMd5Hash = crypto.createHash('md5');
265300

266301
request(authorizedReqOpts)
@@ -270,15 +305,46 @@ File.prototype.createReadStream = function() {
270305
})
271306

272307
.on('data', function(chunk) {
273-
localMd5Hash.update(chunk);
308+
if (crc32c) {
309+
localCrc32cHash = crc.calculate(chunk, localCrc32cHash);
310+
}
311+
312+
if (md5) {
313+
localMd5Hash.update(chunk);
314+
}
274315
})
275316

276-
.on('complete', function() {
277-
localMd5Hash = localMd5Hash.digest('base64');
317+
.on('complete', function(res) {
318+
var failed = false;
278319

279-
if (that.metadata.md5Hash === localMd5Hash) {
280-
throughStream.emit('complete');
281-
} else {
320+
if (crc32c) {
321+
localCrc32cHash = new Buffer([localCrc32cHash]);
322+
localCrc32cHash = localCrc32cHash.toString('base64');
323+
}
324+
325+
if (md5) {
326+
localMd5Hash = localMd5Hash.digest('base64');
327+
}
328+
329+
var hashes = {};
330+
res.headers['x-goog-hash'].split(',').forEach(function (hash) {
331+
var hashType = hash.split('=')[0];
332+
hashes[hashType] = hash.substr(hash.indexOf('=') + 1);
333+
});
334+
335+
if (validation === 'all') {
336+
if (hashes.md5) {
337+
failed = localMd5Hash !== hashes.md5;
338+
} else if (hashes.crc32) {
339+
failed = localCrc32cHash !== hashes.crc32c.substr(4);
340+
}
341+
} else if (md5) {
342+
failed = localMd5Hash !== hashes.md5;
343+
} else if (crc32c) {
344+
failed = localCrc32cHash !== hashes.crc32c.substr(4);
345+
}
346+
347+
if (failed) {
282348
var error = new Error({
283349
code: 911,
284350
message: [
@@ -289,6 +355,8 @@ File.prototype.createReadStream = function() {
289355
});
290356

291357
throughStream.emit('error', error);
358+
} else {
359+
throughStream.emit('complete');
292360
}
293361

294362
throughStream.end();

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"dependencies": {
4747
"duplexify": "^3.1.2",
4848
"extend": "^1.3.0",
49+
"fast-crc32c": "^0.1.3",
4950
"google-service-account": "^1.0.3",
5051
"mime": "^1.2.11",
5152
"node-uuid": "^1.4.1",

0 commit comments

Comments
 (0)