total5 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/filestorage.js ADDED
@@ -0,0 +1,1109 @@
1
+ // FileStorage
2
+ // The MIT License
3
+ // Copyright 2016-2023 (c) Peter Širka <petersirka@gmail.com>
4
+
5
+ 'use strict';
6
+
7
+ const IMAGES = { jpg: 1, png: 1, gif: 1, svg: 1, jpeg: 1, heic: 1, heif: 1, webp: 1, tiff: 1, bmp: 1 };
8
+ const HEADERSIZE = 2000;
9
+ const MKDIR = { recursive: true };
10
+ const READDIR = { withFileTypes: true };
11
+ const GZIP_FILE = { memLevel: 9 };
12
+
13
+ const REG_RANGE = /bytes=/;
14
+ const REG_CLEAN = /^[\s]+|[\s]+$/g;
15
+
16
+ var CONCAT = [null, null];
17
+
18
+ function FileStorage(name, directory) {
19
+
20
+ var t = this;
21
+
22
+ t.name = name;
23
+
24
+ // t.directory = directory;
25
+ // t.logger = directory + '/files.log';
26
+
27
+ t.cache = {};
28
+ t.total = 0;
29
+ t.size = 0;
30
+ t.ext = '.file';
31
+ t.pause = false;
32
+
33
+ t.retrysave = function(id, name, filename, callback, custom, expire, headers) {
34
+ t._save(id, name, filename, callback, custom, expire, headers);
35
+ };
36
+
37
+ t.retryread = function(id, callback, nostream) {
38
+ t._read(id, callback, nostream);
39
+ };
40
+
41
+ t.storage(directory);
42
+ }
43
+
44
+ const FP = FileStorage.prototype;
45
+
46
+ FP.storage = function(value) {
47
+ var self = this;
48
+ self.cache = {};
49
+ self.directory = value;
50
+ self.logger = F.Path.join(value, 'files.log');
51
+ return self;
52
+ };
53
+
54
+ FP.count = function(callback) {
55
+ var self = this;
56
+ NOSQL(self.logger).scalar('sum', 'size').callback(function(err, response) {
57
+ response.size = response.sum;
58
+ self.size = response.size;
59
+ self.total = response.count;
60
+ response.sum = undefined;
61
+ callback && callback(err, response);
62
+ });
63
+ return self;
64
+ };
65
+
66
+ FP.makedirectory = function(id) {
67
+ return F.Path.join(this.directory, F.TUtils.groupify(id));
68
+ };
69
+
70
+ FP.readfilename = function(id) {
71
+ var self = this;
72
+ var directory = self.makedirectory(id);
73
+ return F.Path.join(directory, id + '.file');
74
+ };
75
+
76
+ FP.savejson = function(id, value, callback, custom, expire) {
77
+ return this.save(id, id + '.json', Buffer.from(JSON.stringify(value), 'utf8'), callback, custom, expire);
78
+ };
79
+
80
+ FP.readjson = function(id, callback) {
81
+ return this.read(id, function(err, meta) {
82
+
83
+ if (err) {
84
+ callback(err);
85
+ return;
86
+ }
87
+
88
+ var buffer = [];
89
+ meta.stream.on('data', chunk => buffer.push(chunk));
90
+ meta.stream.on('end', function() {
91
+ meta.stream = null;
92
+ callback(null, Buffer.concat(buffer).toString('utf8').parseJSON(true), meta);
93
+ });
94
+
95
+ });
96
+ };
97
+
98
+ FP.save = FP.insert = function(id, name, filename, custom, callback, expire, headers) {
99
+ var self = this;
100
+
101
+ if (typeof(custom) === 'function') {
102
+ headers = expire;
103
+ expire = callback;
104
+ callback = custom;
105
+ custom = null;
106
+ }
107
+
108
+ if (callback)
109
+ return self._save(id, name, filename, callback, custom, expire, headers);
110
+ else
111
+ return new Promise((resolve, reject) => self._save(id, name, filename, (err, res) => err ? reject(err) : resolve(res), custom, expire, headers));
112
+ };
113
+
114
+ FP._save = function(id, name, filename, callback, custom, expire, headers) {
115
+
116
+ var self = this;
117
+
118
+ if (self.pause) {
119
+ setTimeout(self.retrysave, 500, id, name, filename, callback, custom, expire, headers);
120
+ return self;
121
+ }
122
+
123
+ if (!filename) {
124
+ filename = name;
125
+ name = F.TUtils.getName(name);
126
+ }
127
+
128
+ var directory = self.makedirectory(id);
129
+ var filenameto = F.Path.join(directory, id + '.file');
130
+
131
+ var index = name.lastIndexOf('/');
132
+ if (index !== -1)
133
+ name = name.substring(index + 1);
134
+
135
+ if (self.cache[directory]) {
136
+ if (typeof(filename) === 'string' && filename[0] === 'h' && filename[1] === 't' && filename[7] === '/') {
137
+ // URL address
138
+ var opt = {};
139
+ opt.url = filename;
140
+ opt.custom = true;
141
+ opt.headers = headers;
142
+ opt.callback = function(err, response) {
143
+
144
+ if (err) {
145
+ callback(err);
146
+ return;
147
+ }
148
+
149
+ if (response.status < 400)
150
+ self.saveforce(id, name, response.stream, filenameto, callback, custom, expire);
151
+ else
152
+ callback(F.TUtils.httpstatus(response.status));
153
+ };
154
+
155
+ F.TUtils.request(opt);
156
+ } else
157
+ self.saveforce(id, name, filename, filenameto, callback, custom, expire);
158
+
159
+ } else {
160
+ F.Fs.mkdir(directory, MKDIR, function(err) {
161
+ if (err)
162
+ callback(err);
163
+ else {
164
+ self.cache[directory] = 1;
165
+ if (typeof(filename) === 'string' && filename[0] === 'h' && filename[1] === 't' && filename[7] === '/') {
166
+ // URL address
167
+ var opt = {};
168
+ opt.url = filename;
169
+ opt.custom = true;
170
+ opt.headers = headers;
171
+ opt.callback = function(err, response) {
172
+
173
+ if (err) {
174
+ callback(err);
175
+ return;
176
+ }
177
+
178
+ if (response.status < 400)
179
+ self.saveforce(id, name, response.stream, filenameto, callback, custom, expire);
180
+ else
181
+ callback(F.TUtils.httpstatus(response.status));
182
+ };
183
+ F.TUtils.request(opt);
184
+ } else
185
+ self.saveforce(id, name, filename, filenameto, callback, custom, expire);
186
+ }
187
+ });
188
+ }
189
+
190
+ return self;
191
+ };
192
+
193
+ FP.saveforce = function(id, name, filename, filenameto, callback, custom, expire) {
194
+
195
+ if (!callback)
196
+ callback = NOOP;
197
+
198
+ F.stats.performance.open++;
199
+
200
+ var isbuffer = filename instanceof Buffer;
201
+ var self = this;
202
+ var header = Buffer.alloc(HEADERSIZE, ' ');
203
+ var reader = isbuffer ? null : filename instanceof F.Stream.Readable ? filename : F.Fs.createReadStream(filename);
204
+ var writer = F.Fs.createWriteStream(filenameto);
205
+ var ext = F.TUtils.getExtension(name);
206
+ var meta = { name: name, size: 0, ext: ext, custom: custom, type: F.TUtils.contentTypes[ext] };
207
+ var tmp;
208
+
209
+ writer.write(header, 'binary');
210
+
211
+ if (IMAGES[meta.ext]) {
212
+ if (isbuffer) {
213
+ switch (meta.ext) {
214
+ case 'gif':
215
+ tmp = F.TImages.measureGIF(filename);
216
+ break;
217
+ case 'png':
218
+ tmp = F.TImages.measurePNG(filename);
219
+ break;
220
+ case 'jpg':
221
+ case 'jpeg':
222
+ tmp = F.TImages.measureJPG(filename);
223
+ break;
224
+ case 'svg':
225
+ tmp = F.TImages.measureSVG(filename);
226
+ break;
227
+ }
228
+ } else {
229
+ reader.once('data', function(buffer) {
230
+ switch (meta.ext) {
231
+ case 'gif':
232
+ tmp = F.TImages.measureGIF(buffer);
233
+ break;
234
+ case 'png':
235
+ tmp = F.TImages.measurePNG(buffer);
236
+ break;
237
+ case 'jpg':
238
+ case 'jpeg':
239
+ tmp = F.TImages.measureJPG(buffer);
240
+ break;
241
+ case 'svg':
242
+ tmp = F.TImages.measureSVG(buffer);
243
+ break;
244
+ }
245
+ });
246
+ }
247
+ }
248
+
249
+ if (isbuffer) {
250
+ writer.end(filename);
251
+ } else {
252
+ reader.pipe(writer);
253
+ if (typeof(filename) !== 'string')
254
+ reader.resume();
255
+ }
256
+
257
+ F.cleanup(writer, function() {
258
+
259
+ F.Fs.open(filenameto, 'r+', function(err, fd) {
260
+
261
+ if (err) {
262
+ // Unhandled error
263
+ callback(err);
264
+ return;
265
+ }
266
+
267
+ if (tmp) {
268
+ meta.width = tmp.width;
269
+ meta.height = tmp.height;
270
+ }
271
+
272
+ meta.size = writer.bytesWritten - HEADERSIZE;
273
+ meta.date = NOW = new Date();
274
+
275
+ if (expire)
276
+ meta.expire = NOW.add(expire);
277
+
278
+ self.total++;
279
+ self.size += meta.size;
280
+
281
+ if (meta.name.length > 250)
282
+ meta.name = meta.name.substring(0, 250);
283
+
284
+ header.write(JSON.stringify(meta));
285
+
286
+ // Update header
287
+ F.Fs.write(fd, header, 0, header.length, 0, function(err) {
288
+ if (err) {
289
+ callback(err);
290
+ F.Fs.close(fd, NOOP);
291
+ } else {
292
+ meta.id = id;
293
+ F.Fs.appendFile(self.logger, JSON.stringify(meta) + '\n', NOOP);
294
+ F.Fs.close(fd, () => callback(null, meta));
295
+ }
296
+ });
297
+ });
298
+ });
299
+ };
300
+
301
+ FP.read = function(id, callback, nostream) {
302
+ var self = this;
303
+ if (callback)
304
+ return self._read(id, callback, nostream);
305
+ else
306
+ return new Promise((resolve, reject) => self._read(id, (err, res) => err ? reject(err) : resolve(res), nostream));
307
+ };
308
+
309
+ FP._read = function(id, callback, nostream) {
310
+
311
+ var self = this;
312
+
313
+ if (self.pause) {
314
+ setTimeout(self.retryread, 500, id, callback, nostream);
315
+ return self;
316
+ }
317
+
318
+ var filename = F.Path.join(self.makedirectory(id), id + '.file');
319
+ F.stats.performance.open++;
320
+ F.Fs.open(filename, 'r', function(err, fd) {
321
+
322
+ if (err) {
323
+ callback(err);
324
+ return;
325
+ }
326
+
327
+ var buffer = Buffer.alloc(HEADERSIZE);
328
+ F.Fs.read(fd, buffer, 0, HEADERSIZE, 0, function(err) {
329
+
330
+ if (err) {
331
+ F.Fs.close(fd, NOOP);
332
+ callback(err);
333
+ return;
334
+ }
335
+
336
+ var str = buffer.toString('utf8').replace(REG_CLEAN, '');
337
+ if (!str) {
338
+ // Invalid file
339
+ F.Fs.close(fd, function() {
340
+ if (buffer.length === HEADERSIZE)
341
+ F.Fs.unlink(filename, NOOP);
342
+ });
343
+ callback('File not found');
344
+ return;
345
+ }
346
+
347
+ var meta = str.parseJSON(true);
348
+ if (!meta) {
349
+ F.Fs.close(fd, NOOP);
350
+ callback('Invalid file');
351
+ return;
352
+ }
353
+
354
+ meta.id = id;
355
+
356
+ if (meta.expire && meta.expire < NOW) {
357
+ F.Fs.close(fd, NOOP);
358
+ callback('File is expired');
359
+ return;
360
+ }
361
+
362
+ if (!nostream) {
363
+ F.stats.performance.open++;
364
+ meta.stream = F.Fs.createReadStream(filename, { fd: fd, start: HEADERSIZE });
365
+ F.cleanup(meta.stream, () => F.Fs.close(fd, NOOP));
366
+ } else
367
+ F.Fs.close(fd, NOOP);
368
+
369
+ callback(err, meta);
370
+ });
371
+ });
372
+
373
+ return self;
374
+ };
375
+
376
+ FP.readbuffer = function(id, callback) {
377
+ var self = this;
378
+ if (callback)
379
+ return self._readbuffer(id, callback);
380
+ else
381
+ return new Promise((resolve, reject) => self._readbuffer(id, (err, res) => err ? reject(err) : resolve(res)));
382
+ };
383
+
384
+ FP._readbuffer = function(id, callback) {
385
+
386
+ var self = this;
387
+
388
+ if (self.pause) {
389
+ setTimeout(self._readbuffer, 500, id, callback);
390
+ return self;
391
+ }
392
+
393
+ var filename = F.Path.join(self.makedirectory(id), id + '.file');
394
+ F.stats.performance.open++;
395
+ F.Fs.open(filename, 'r', function(err, fd) {
396
+
397
+ if (err) {
398
+ callback(err);
399
+ return;
400
+ }
401
+
402
+ var buffer = Buffer.alloc(HEADERSIZE);
403
+ F.Fs.read(fd, buffer, 0, HEADERSIZE, 0, function(err) {
404
+
405
+ if (err) {
406
+ F.Fs.close(fd, NOOP);
407
+ callback(err);
408
+ return;
409
+ }
410
+
411
+ var meta = buffer.toString('utf8').replace(REG_CLEAN, '').parseJSON(true);
412
+ meta.id = id;
413
+
414
+ if (meta.expire && meta.expire < NOW) {
415
+ F.Fs.close(fd, NOOP);
416
+ callback('File is expired');
417
+ return;
418
+ }
419
+
420
+ buffer = [];
421
+ F.stats.performance.open++;
422
+
423
+ var stream = F.Fs.createReadStream(filename, { fd: fd, start: HEADERSIZE });
424
+ stream.on('data', chunk => buffer.push(chunk));
425
+
426
+ F.cleanup(stream, function() {
427
+ F.Fs.close(fd, NOOP);
428
+ callback(err, Buffer.concat(buffer), meta);
429
+ });
430
+ });
431
+ });
432
+
433
+ return self;
434
+ };
435
+
436
+ FP.browse = function(callback) {
437
+ var db = NOSQL(this.logger).find();
438
+ if (callback)
439
+ db.main.callback = callback;
440
+ return db;
441
+ };
442
+
443
+ FP.move = function(id, newid, callback) {
444
+ var self = this;
445
+ if (callback)
446
+ return self._move(id, newid, callback);
447
+ else
448
+ return new Promise((resolve, reject) => self._move(id, newid, (err, res) => err ? reject(err) : resolve(res)));
449
+ };
450
+
451
+ FP._move = function(id, newid, callback) {
452
+
453
+ var self = this;
454
+ var filename = F.Path.join(self.makedirectory(id), id + '.file');
455
+
456
+ F.stats.performance.open++;
457
+
458
+ F.Fs.lstat(filename, function(err) {
459
+
460
+ if (err) {
461
+ callback(err);
462
+ return;
463
+ }
464
+
465
+ var directory = self.makedirectory(newid);
466
+ var filenamenew = F.Path.join(directory, newid + '.file');
467
+
468
+ if (self.cache[directory]) {
469
+ F.Fs.rename(filename, filenamenew, err => callback && callback(err));
470
+ } else {
471
+ F.Fs.mkdir(directory, MKDIR, function(err) {
472
+
473
+ if (err) {
474
+ callback(err);
475
+ return;
476
+ }
477
+
478
+ self.cache[directory] = 1;
479
+ F.Fs.rename(filename, filenamenew, err => callback && callback(err));
480
+ });
481
+ }
482
+
483
+ });
484
+
485
+ return self;
486
+ };
487
+
488
+ FP.rename = function(id, newname, callback) {
489
+ var self = this;
490
+ if (callback)
491
+ return self._rename(id, newname, callback);
492
+ else
493
+ return new Promise((resolve, reject) => self._rename(id, newname, (err, res) => err ? reject(err) : resolve(res)));
494
+ };
495
+
496
+ FP._rename = function(id, newname, callback) {
497
+
498
+ var self = this;
499
+ var filename = F.Path.join(self.makedirectory(id), id + '.file');
500
+ F.stats.performance.open++;
501
+
502
+ F.Fs.open(filename, 0o666, function(err, fd) {
503
+
504
+ if (err) {
505
+ callback(err);
506
+ return;
507
+ }
508
+
509
+ var buffer = Buffer.alloc(HEADERSIZE);
510
+ F.Fs.read(fd, buffer, 0, HEADERSIZE, 0, function(err) {
511
+
512
+ if (err) {
513
+ F.Fs.close(fd, NOOP);
514
+ callback(err);
515
+ return;
516
+ }
517
+
518
+ var meta = buffer.toString('utf8').replace(REG_CLEAN, '').parseJSON(true);
519
+ meta.name = newname;
520
+
521
+ if (meta.name.length > 250)
522
+ meta.name = meta.name.substring(0, 250);
523
+
524
+ buffer = Buffer.alloc(HEADERSIZE, ' ');
525
+ buffer.write(JSON.stringify(meta));
526
+
527
+ // Update header
528
+ F.Fs.write(fd, buffer, 0, buffer.length, 0, function(err) {
529
+ if (err) {
530
+ callback(err);
531
+ F.Fs.close(fd, NOOP);
532
+ } else {
533
+ meta.id = id;
534
+ NOSQL(self.logger).modify(meta).id(id);
535
+ F.Fs.close(fd, () => callback(null, meta));
536
+ }
537
+ });
538
+ });
539
+ });
540
+
541
+ return self;
542
+ };
543
+
544
+ FP.remove = function(id, callback) {
545
+ var self = this;
546
+ if (callback)
547
+ return self._remove(id, callback);
548
+ else
549
+ return new Promise((resolve, reject) => self._remove(id, (err, res) => err ? reject(err) : resolve(res)));
550
+ };
551
+
552
+ FP._remove = function(id, callback) {
553
+ var self = this;
554
+ var filename = F.Path.join(self.makedirectory(id), id + '.file');
555
+ F.Fs.unlink(filename, function(err) {
556
+ NOSQL(self.logger).remove().id(id);
557
+ callback && callback(err);
558
+ });
559
+ return self;
560
+ };
561
+
562
+ FP.clean = function(callback) {
563
+ var self = this;
564
+ if (callback)
565
+ return self._clean(callback);
566
+ else
567
+ return new Promise((resolve, reject) => self._clean((err, res) => err ? reject(err) : resolve(res)));
568
+ };
569
+
570
+ FP._clean = function(callback) {
571
+
572
+ var self = this;
573
+ var db = NOSQL(self.logger);
574
+
575
+ db.find().where('expire', '<', NOW).callback(function(err, files) {
576
+
577
+ if (err || !files || !files.length) {
578
+ callback && callback(err, 0);
579
+ return;
580
+ }
581
+
582
+ var id = [];
583
+ for (let file in files)
584
+ id.push(file.id);
585
+
586
+ db.remove().in('id', id);
587
+
588
+ files.wait(function(item, next) {
589
+ var filename = F.Path.join(self.makedirectory(item.id), item.id + '.file');
590
+ F.Fs.unlink(filename, next);
591
+ }, function() {
592
+ self.count();
593
+ db.clean();
594
+ callback && callback(err, files.length);
595
+ });
596
+ });
597
+
598
+ return self;
599
+ };
600
+
601
+ FP.backup = function(filename, callback) {
602
+ var self = this;
603
+ if (callback)
604
+ return self._backup(filename, callback);
605
+ else
606
+ return new Promise((resolve, reject) => self._backup(filename, (err, res) => err ? reject(err) : resolve(res)));
607
+ };
608
+
609
+ FP._backup = function(filename, callback) {
610
+
611
+ var self = this;
612
+ var writer = typeof(filename) === 'string' ? F.Fs.createWriteStream(filename) : filename;
613
+ var totalsize = 0;
614
+ var counter = 0;
615
+ var padding = 50;
616
+
617
+ writer.on('finish', () => callback && callback(null, { filename: filename, files: counter, size: totalsize }));
618
+
619
+ F.Fs.readdir(self.directory, function(err, response) {
620
+
621
+ if (err) {
622
+ callback(err);
623
+ return;
624
+ }
625
+
626
+ for (let dir of response) {
627
+ if (dir.length === 4) {
628
+ let tmp = Buffer.from(('/' + dir + '/').padRight(padding) + ': #\n', 'utf8');
629
+ writer.write(tmp);
630
+ totalsize += tmp.length;
631
+ }
632
+ }
633
+
634
+ response.wait(function(item, next) {
635
+
636
+ if (item.length !== 4) {
637
+ next();
638
+ return;
639
+ }
640
+
641
+ var dir = F.Path.join(self.directory, item);
642
+ F.Fs.readdir(dir, function(err, response) {
643
+ response.wait(function(name, next) {
644
+
645
+ var filename = F.Path.join(dir, name);
646
+ var data = Buffer.alloc(0);
647
+ var tmp = Buffer.from(('/' + F.Path.join(item, name)).padRight(padding) + ': ');
648
+
649
+ totalsize += tmp.length;
650
+ writer.write(tmp);
651
+
652
+ F.Fs.createReadStream(filename).pipe(F.Zlib.createGzip(GZIP_FILE)).on('data', function(chunk) {
653
+
654
+ CONCAT[0] = data;
655
+ CONCAT[1] = chunk;
656
+ data = Buffer.concat(CONCAT);
657
+
658
+ var remaining = data.length % 3;
659
+ if (remaining) {
660
+ let tmp = data.slice(0, data.length - remaining).toString('base64');
661
+ writer.write(tmp, 'utf8');
662
+ data = data.slice(data.length - remaining);
663
+ totalsize += tmp.length;
664
+ }
665
+
666
+ }).on('end', function() {
667
+ let tmp = data.length ? data.toString('base64') : '';
668
+ data.length && writer.write(tmp);
669
+ writer.write('\n', 'utf8');
670
+ totalsize += tmp.length + 1;
671
+ counter++;
672
+ setImmediate(next);
673
+ }).on('error', () => setImmediate(next));
674
+
675
+ }, next);
676
+ });
677
+
678
+ }, () => writer.end());
679
+ });
680
+ };
681
+
682
+ FP.restore = function(filename, callback) {
683
+ var self = this;
684
+ if (callback)
685
+ return self._restore(filename, callback);
686
+ else
687
+ return new Promise((resolve, reject) => self._restore(filename, (err, res) => err ? reject(err) : resolve(res)));
688
+ };
689
+
690
+ FP._restore = function(filename, callback) {
691
+ var self = this;
692
+ self.pause = true;
693
+ self.clear(function() {
694
+ self.pause = true;
695
+ F.restore(filename, self.directory, function(err, meta) {
696
+ self.cache = {};
697
+ self.pause = false;
698
+ callback && callback(err, meta);
699
+ });
700
+ });
701
+ };
702
+
703
+ FP.drop = FP.clear = function(callback) {
704
+ var self = this;
705
+ if (callback)
706
+ return self._clear(callback);
707
+ else
708
+ return new Promise((resolve, reject) => self._clear((err, res) => err ? reject(err) : resolve(res)));
709
+ };
710
+
711
+ FP._clear = function(callback) {
712
+
713
+ var self = this;
714
+ var count = 0;
715
+
716
+ self.pause = true;
717
+
718
+ F.Fs.readdir(self.directory, function(err, response) {
719
+
720
+ if (err) {
721
+ callback && callback(err);
722
+ return;
723
+ }
724
+
725
+ F.Fs.unlink(self.logger, NOOP);
726
+ response.wait(function(item, next) {
727
+ var dir = F.Path.join(self.directory, item);
728
+ F.Fs.readdir(dir, function(err, response) {
729
+ if (response instanceof Array) {
730
+ count += response.length;
731
+ response.wait((file, next) => F.Fs.unlink(F.Path.join(self.directory, item, file), next), () => F.Fs.rmdir(dir, next));
732
+ } else
733
+ next();
734
+ });
735
+ }, function() {
736
+ F.Fs.unlink(self.logger, NOOP);
737
+ self.pause = false;
738
+ self.cache = {};
739
+ callback && callback(null, count);
740
+ });
741
+
742
+ });
743
+
744
+ return self;
745
+ };
746
+
747
+ FP.stream = function(onfile, callback, workers = 2) {
748
+
749
+ var self = this;
750
+
751
+ F.Fs.readdir(self.directory, READDIR, function(err, response) {
752
+
753
+ if (err) {
754
+ callback();
755
+ return;
756
+ }
757
+
758
+ var count = 0;
759
+
760
+ response.wait(function(item, next) {
761
+
762
+ if (!item.isDirectory()) {
763
+ next();
764
+ return;
765
+ }
766
+
767
+ if (item.name.length !== 4) {
768
+ next();
769
+ return;
770
+ }
771
+
772
+ F.Fs.readdir(F.Path.join(self.directory, item.name), READDIR, function(err, files) {
773
+ if (files instanceof Array) {
774
+ files.wait(function(item, next) {
775
+
776
+ if (!item.isFile()) {
777
+ next();
778
+ return;
779
+ }
780
+
781
+ let index = item.name.lastIndexOf('.');
782
+
783
+ if (item.name.substring(index) !== '.file') {
784
+ next();
785
+ return;
786
+ }
787
+
788
+ let id = item.name.substring(0, index);
789
+ self.read(id, function(err, meta) {
790
+ if (meta) {
791
+ meta.id = id;
792
+ meta.index = count++;
793
+ onfile(meta, next);
794
+ } else
795
+ next();
796
+ }, true);
797
+ }, next, workers);
798
+ } else
799
+ next();
800
+ });
801
+ }, callback);
802
+ });
803
+
804
+ return self;
805
+ };
806
+
807
+ FP.browse2 = function(callback) {
808
+ var self = this;
809
+ if (callback)
810
+ return self._browse2(callback);
811
+ else
812
+ return new Promise((resolve, reject) => self._browse2((err, res) => err ? reject(err) : resolve(res)));
813
+ };
814
+
815
+ FP._browse2 = function(callback) {
816
+ var self = this;
817
+ var files = [];
818
+ self.stream(function(item, next) {
819
+ files.push(item);
820
+ next();
821
+ }, () => callback(null, files), 5);
822
+ return self;
823
+ };
824
+
825
+ FP.rebuild = function(callback) {
826
+ var self = this;
827
+ if (callback)
828
+ return self._rebuild(callback);
829
+ else
830
+ return new Promise((resolve, reject) => self._rebuild((err, res) => err ? reject(err) : resolve(res)));
831
+ };
832
+
833
+ FP._rebuild = function(callback) {
834
+
835
+ var self = this;
836
+
837
+ self.browse2(function(err, files) {
838
+
839
+ self.pause = true;
840
+
841
+ F.Fs.unlink(self.logger, NOOP);
842
+
843
+ var builder = [];
844
+ self.size = 0;
845
+ self.total = 0;
846
+
847
+ for (var i = 0; i < files.length; i++) {
848
+ var item = files[i];
849
+ self.size += item.size;
850
+ self.total++;
851
+ builder.push(JSON.stringify(item));
852
+ }
853
+
854
+ builder.limit(500, (items, next) => F.Fs.appendFile(self.logger, items.join('\n'), next), function() {
855
+ F.Fs.appendFile(self.logger, '\n', NOOP);
856
+ self.pause = false;
857
+ callback && callback();
858
+ });
859
+ });
860
+
861
+ return self;
862
+ };
863
+
864
+ FP.count2 = function(callback) {
865
+ var self = this;
866
+ if (callback)
867
+ return self._count2(callback);
868
+ else
869
+ return new Promise((resolve, reject) => self._count2((err, res) => err ? reject(err) : resolve(res)));
870
+ };
871
+
872
+ FP._count2 = function(callback) {
873
+ var self = this;
874
+ var count = 0;
875
+ F.Fs.readdir(self.directory, function(err, response) {
876
+ response.wait(function(item, next) {
877
+ F.Fs.readdir(F.Path.join(self.directory, item), function(err, response) {
878
+ if (response instanceof Array)
879
+ count += response.length;
880
+ next();
881
+ });
882
+ }, () => callback(null, count));
883
+ });
884
+ return self;
885
+ };
886
+
887
+ function jsonparser(key, value) {
888
+ return typeof(value) === 'string' && value.isJSONDate() ? new Date(value) : value;
889
+ }
890
+
891
+ FP.readmeta = function(id, callback, keepfd) {
892
+ var self = this;
893
+ if (callback)
894
+ return self._readmeta(id, callback, keepfd);
895
+ else
896
+ return new Promise((resolve, reject) => self._readmeta(id, (err, res) => err ? reject(err) : resolve(res), keepfd));
897
+ };
898
+
899
+ FP._readmeta = function(id, callback, keepfd) {
900
+
901
+ var self = this;
902
+ var filename = F.Path.join(self.makedirectory(id), id + self.ext);
903
+
904
+ F.stats.performance.open++;
905
+
906
+ F.Fs.open(filename, function(err, fd) {
907
+
908
+ if (err) {
909
+ callback(err);
910
+ return;
911
+ }
912
+
913
+ var buffer = Buffer.alloc(HEADERSIZE);
914
+
915
+ F.Fs.read(fd, buffer, 0, buffer.length, 0, function(err, bytes, buffer) {
916
+
917
+ if (err) {
918
+ F.Fs.close(fd, NOOP);
919
+ callback(err);
920
+ return;
921
+ }
922
+
923
+ var json = buffer.toString('utf8').replace(REG_CLEAN, '');
924
+
925
+ try {
926
+ json = JSON.parse(json, jsonparser);
927
+ } catch (e) {
928
+ F.Fs.close(fd, NOOP);
929
+ callback(e, null, filename);
930
+ return;
931
+ }
932
+
933
+ if (!keepfd)
934
+ F.Fs.close(fd, NOOP);
935
+
936
+ callback(null, json, filename, fd);
937
+ });
938
+
939
+ });
940
+
941
+ return self;
942
+ };
943
+
944
+ FP.image = function(id, callback) {
945
+ var self = this;
946
+ if (callback)
947
+ return self._image(id, callback);
948
+ else
949
+ return new Promise((resolve, reject) => self._image(id, (err, res) => err ? reject(err) : resolve(res)));
950
+ };
951
+
952
+ FP._image = function(id, callback) {
953
+ var self = this;
954
+ self.readmeta(id, function(err, obj, filename, fd) {
955
+
956
+ if (err) {
957
+ callback(err);
958
+ return;
959
+ }
960
+
961
+ var stream = F.Fs.createReadStream(filename, { fd: fd, start: HEADERSIZE });
962
+ var image = Image.load(stream);
963
+ stream.$totalfd = fd;
964
+ callback(err, image, obj);
965
+ F.cleanup(stream);
966
+
967
+ }, true);
968
+
969
+ return self;
970
+ };
971
+
972
+ FP.http = function(ctrl, opt) {
973
+
974
+ var self = this;
975
+
976
+ if (F.temporary.notfound[ctrl.uri.cache]) {
977
+ ctrl.fallback(404);
978
+ return;
979
+ }
980
+
981
+ var id = opt.id || '';
982
+
983
+ self.readmeta(id, function(err, obj, filename, fd) {
984
+
985
+ fd && F.Fs.close(fd, NOOP);
986
+
987
+ if (err || (obj.expire && obj.expire < NOW) || (opt.check && opt.check(obj) == false)) {
988
+ F.temporary.notfound[ctrl.uri.cache] = true;
989
+ ctrl.fallback(404);
990
+ return;
991
+ }
992
+
993
+ F.stats.performance.open++;
994
+
995
+ var date = obj.date ? obj.date.toUTCString() : '';
996
+ var response = ctrl.response;
997
+
998
+ if (!opt.download && date && ctrl.notmodified(date))
999
+ return;
1000
+
1001
+ // Resized image?
1002
+ if (!DEBUG && F.temporary.path[ctrl.uri.cache]) {
1003
+ ctrl.resume();
1004
+ return;
1005
+ }
1006
+
1007
+ F.stats.performance.open++;
1008
+
1009
+ if (opt.download) {
1010
+ response.headers['content-disposition'] = 'attachment; filename*=utf-8\'\'' + encodeURIComponent(opt.download === true ? obj.name : typeof(opt.download) === 'function' ? opt.download(obj.name, obj.type) : opt.download);
1011
+ } else
1012
+ response.headers['last-modified'] = date;
1013
+
1014
+ if (obj.width && obj.height) {
1015
+ response.headers['x-width'] = obj.width;
1016
+ response.headers['x-height'] = obj.height;
1017
+ }
1018
+
1019
+ response.headers['x-size'] = obj.size;
1020
+
1021
+ if (opt.image) {
1022
+ /*
1023
+ res.opt.stream = { filename: filename, start: HEADERSIZE, custom: true };
1024
+ res.opt.make = opt.make;
1025
+ res.opt.cache = opt.cache !== false;
1026
+ res.opt.persistent = false;
1027
+ res.$image();
1028
+ */
1029
+ } else {
1030
+
1031
+ var range = ctrl.headers.range;
1032
+ if (range) {
1033
+
1034
+ var arr = range.replace(REG_RANGE, '').split('-');
1035
+ var beg = (arr[0] ? +arr[0] : 0);
1036
+ var end = (arr[1] ? +arr[1] : 0);
1037
+
1038
+ if (isNaN(beg) || isNaN(end)) {
1039
+ ctrl.fallback(404);
1040
+ return;
1041
+ }
1042
+
1043
+ if (end <= 0)
1044
+ end = beg + ((1024 * 1024) * 5); // 5 MB
1045
+
1046
+ if (beg > end) {
1047
+ beg = 0;
1048
+ end = obj.size - 1;
1049
+ }
1050
+
1051
+ if (end > obj.size)
1052
+ end = obj.size - 1;
1053
+
1054
+ if (beg >= end || beg < 0) {
1055
+ ctrl.fallback(404);
1056
+ return;
1057
+ }
1058
+
1059
+ var length = (end - beg) + 1;
1060
+
1061
+ response.status = 206;
1062
+ response.headers['accept-ranges'] = 'bytes';
1063
+
1064
+ if (!opt.download && !DEBUG && date)
1065
+ ctrl.httpcache(date);
1066
+
1067
+ response.headers['content-length'] = length;
1068
+ response.headers['content-range'] = 'bytes ' + beg + '-' + end + '/' + obj.size;
1069
+ response.headers['content-type'] = obj.type;
1070
+
1071
+ if (F.config.$xpoweredby)
1072
+ response.headers['x-powered-by'] = F.config.$xpoweredby;
1073
+
1074
+ ctrl.res.writeHead(response.status, response.headers);
1075
+ F.Fs.createReadStream(filename, { flags: 'r', mode: '0666', autoClose: true, start: HEADERSIZE + beg, end: end + HEADERSIZE }).pipe(ctrl.res);
1076
+ ctrl.free();
1077
+
1078
+ } else {
1079
+ var stream = F.Fs.createReadStream(filename, { start: HEADERSIZE });
1080
+
1081
+ if (!opt.download && !DEBUG && date)
1082
+ ctrl.httpcache(date);
1083
+
1084
+ ctrl.stream(obj.type, stream);
1085
+ }
1086
+ }
1087
+
1088
+ }, true);
1089
+ };
1090
+
1091
+ FP.readbase64 = function(id, callback) {
1092
+ var self = this;
1093
+ if (callback)
1094
+ return self._readbase64(id, callback);
1095
+ else
1096
+ return new Promise((resolve, reject) => self._readbase64(id, (err, res) => err ? reject(err) : resolve(res)));
1097
+ };
1098
+
1099
+ FP._readbase64 = function(id, callback) {
1100
+ var self = this;
1101
+ self._readbuffer(id, (err, buffer, meta) => callback(err, buffer ? buffer.toString('base64') : null, meta));
1102
+ return self;
1103
+ };
1104
+
1105
+ exports.create = function(name, directory) {
1106
+ if (!directory)
1107
+ directory = F.path.databases('fs-' + name + '/');
1108
+ return new FileStorage(name, directory);
1109
+ };