/
index.js
100 lines (84 loc) · 2.71 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
var fs = require('fs');
var crypto = require('crypto');
var thunky = require('thunky');
var events = require('events');
var noop = function() {};
var sha1 = function(data) {
return crypto.createHash('sha1').update(data).digest('hex');
};
var PartFile = function(filename, partSize, parts) {
if (!(this instanceof PartFile)) return new PartFile(filename, partSize, parts);
events.EventEmitter.call(this);
this.filename = filename;
this.parts = parts;
this.partSize = partSize;
this.verified = [];
this.verifiedParts = 0;
this.open = thunky(function(callback) {
fs.exists(filename, function(exists) {
fs.open(filename, exists ? 'r+' : 'w+', callback);
});
});
};
PartFile.prototype.__proto__ = events.EventEmitter.prototype;
PartFile.prototype.readable = function(index) {
return !!this.verified[index];
};
PartFile.prototype.complete = function() {
return this.verifiedParts === this.parts.length;
};
PartFile.prototype.verify = function(index, callback) {
if (!callback) callback = noop;
var self = this;
if (this.verified[index]) return callback(null, true);
this._read(index, function(err, buf) {
if (err) return callback(err);
if (sha1(buf) !== self.parts[index]) return callback(null, false);
self._verified(index);
callback(null, true);
});
};
PartFile.prototype.read = function(index, callback) {
if (!this.verified[index]) return callback(new Error('part is not written'));
this._read(index, callback);
};
PartFile.prototype.write = function(index, buf, callback) {
if (typeof buf === 'string') buf = new Buffer(buf);
if (!callback) callback = noop;
var self = this;
this.open(function(err, fd) {
if (err) return callback(err);
if (sha1(buf) !== self.parts[index]) return callback(new Error('part is invalid'));
fs.write(fd, buf, 0, buf.length, index * self.partSize, function(err) {
if (!err) self._verified(index);
callback(err);
});
});
};
PartFile.prototype.close = function(callback) {
if (!callback) callback = noop;
this.open(function(err, fd) {
if (err) return callback(err);
fs.close(fd, callback);
});
};
PartFile.prototype._read = function(index, callback) {
var self = this;
var partSize = this.partSize;
this.open(function(err, fd) {
if (err) return callback(err);
fs.read(fd, new Buffer(partSize), 0, partSize, index * partSize, function(err, bytesRead, buf) {
if (err) return callback(err);
if (!bytesRead) return callback(new Error('could not read any bytes'));
callback(null, buf.slice(0, bytesRead));
});
});
};
PartFile.prototype._verified = function(index) {
if (this.verified[index]) return;
this.verified[index] = true;
this.verifiedParts++;
this.emit('readable', index);
if (this.complete()) this.emit('complete');
};
module.exports = PartFile;