Skip to content

Commit e6b4c00

Browse files
committed
Merge pull request #30 from js3692/custom-filenames
Custom file names
2 parents 74f3ac7 + e5e6fd5 commit e6b4c00

File tree

10 files changed

+178
-69
lines changed

10 files changed

+178
-69
lines changed

lib/handlers/HeadHandler.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class HeadHandler extends BaseHandler {
1111
* @return {function}
1212
*/
1313
send(req, res) {
14-
const re = new RegExp('\\' + this.store.path + '\\/(\\w+)\/?'); // eslint-disable-line prefer-template
14+
const re = new RegExp('\\' + this.store.path + '\\/(\\S+)\/?'); // eslint-disable-line prefer-template
1515
const match = req.url.match(re);
1616
if (!match) {
1717
return super.send(res, 404);

lib/handlers/PatchHandler.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class PatchHandler extends BaseHandler {
1111
* @return {function}
1212
*/
1313
send(req, res) {
14-
const re = new RegExp('\\' + this.store.path + '\\/(\\w+)\/?'); // eslint-disable-line prefer-template
14+
const re = new RegExp('\\' + this.store.path + '\\/(\\S+)\/?'); // eslint-disable-line prefer-template
1515
const match = req.url.match(re);
1616
if (!match) {
1717
return super.send(res, 404);

lib/handlers/PostHandler.js

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
'use strict';
22

3-
const File = require('../models/File');
43
const BaseHandler = require('./BaseHandler');
54

65
class PostHandler extends BaseHandler {
@@ -12,24 +11,17 @@ class PostHandler extends BaseHandler {
1211
* @return {function}
1312
*/
1413
send(req, res) {
15-
16-
const upload_length = req.headers['upload-length'];
17-
const upload_defer_length = req.headers['upload-defer-length'];
18-
const upload_metadata = req.headers['upload-metadata'];
19-
20-
// The request MUST include a Upload-Length or Upload-Defer-Length header
21-
if (upload_length === undefined && upload_defer_length === undefined) {
22-
return super.send(res, 412, {}, 'Upload-Length or Upload-Defer-Length Required\n');
23-
}
24-
25-
const file = new File(upload_length, upload_defer_length, upload_metadata);
26-
return this.store.create(file)
27-
.then((location) => {
28-
const url = `http://${req.headers.host}${this.store.path}/${file.id}`;
14+
return this.store.create(req)
15+
.then((File) => {
16+
const url = `http://${req.headers.host}${this.store.path}/${File.id}`;
2917
return super.send(res, 201, { Location: url });
3018
})
3119
.catch((error) => {
3220
if (Number.isInteger(error)) {
21+
if (error === 412) {
22+
return super.send(res, error, {}, 'Upload-Length or Upload-Defer-Length Required\n');
23+
}
24+
3325
return super.send(res, error);
3426
}
3527

lib/models/File.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
'use strict';
2-
const Uid = require('./Uid');
32

43
/**
54
* @fileOverview
@@ -9,15 +8,19 @@ const Uid = require('./Uid');
98
*/
109

1110
class File {
12-
constructor(upload_length, upload_defer_length, upload_metadata) {
11+
constructor(file_id, upload_length, upload_defer_length, upload_metadata) {
12+
if (!file_id) {
13+
throw new Error('[File] constructor must be given a file_id');
14+
}
15+
1316
if (upload_length === undefined && upload_defer_length === undefined) {
1417
throw new Error('[File] constructor must be given either a upload_length or upload_defer_length');
1518
}
1619

20+
this.id = `${file_id}`;
1721
this.upload_length = upload_length;
1822
this.upload_defer_length = upload_defer_length;
1923
this.upload_metadata = upload_metadata;
20-
this.id = Uid.rand();
2124
}
2225
}
2326

lib/stores/DataStore.js

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,19 @@
77
* @author Ben Stahl <[email protected]>
88
*/
99

10+
const Uid = require('../models/Uid');
1011
const File = require('../models/File');
1112

1213
class DataStore {
1314
constructor(options) {
1415
if (!options || !options.path) {
1516
throw new Error('Store must have a path');
1617
}
18+
if (options.namingFunction && typeof options.namingFunction !== 'function') {
19+
throw new Error('namingFunction must be a function');
20+
}
1721
this.path = options.path;
22+
this.generateFileName = options.namingFunction || Uid.rand;
1823
}
1924

2025
get extensions() {
@@ -37,13 +42,26 @@ class DataStore {
3742
*
3843
* http://tus.io/protocols/resumable-upload.html#creation
3944
*
40-
* @param {object} file File model.
45+
* @param {object} req http.incomingMessage
46+
* @return {Promise}
4147
*/
42-
create(file) {
43-
if (!(file instanceof File)) {
44-
throw new Error(`${file} is not a File`);
45-
}
46-
console.log(`[DataStore] create: ${file.id}`);
48+
create(req) {
49+
console.log('[DataStore] create');
50+
51+
return new Promise((resolve, reject) => {
52+
const upload_length = req.headers['upload-length'];
53+
const upload_defer_length = req.headers['upload-defer-length'];
54+
const upload_metadata = req.headers['upload-metadata'];
55+
56+
if (upload_length === undefined && upload_defer_length === undefined) {
57+
return reject(412);
58+
}
59+
60+
const file_id = this.generateFileName(req);
61+
const file = new File(file_id, upload_length, upload_defer_length, upload_metadata);
62+
63+
return resolve(file);
64+
});
4765
}
4866

4967
/**

lib/stores/FileStore.js

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
const DataStore = require('./DataStore');
4+
const File = require('../models/File');
45
const fs = require('fs');
56
const Configstore = require('configstore');
67
const pkg = require('../../package.json');
@@ -41,13 +42,32 @@ class FileStore extends DataStore {
4142
/**
4243
* Create an empty file.
4344
*
45+
* @param {object} req http.incomingMessage
4446
* @param {File} file
4547
* @return {Promise}
4648
*/
47-
create(file) {
48-
super.create(file);
49+
create(req) {
4950
return new Promise((resolve, reject) => {
50-
fs.open(`${this.directory}/${file.id}`, 'w', (err, fd) => {
51+
const upload_length = req.headers['upload-length'];
52+
const upload_defer_length = req.headers['upload-defer-length'];
53+
const upload_metadata = req.headers['upload-metadata'];
54+
55+
if (upload_length === undefined && upload_defer_length === undefined) {
56+
return reject(412);
57+
}
58+
59+
let file_id;
60+
try {
61+
file_id = this.generateFileName(req);
62+
}
63+
catch (generateError) {
64+
console.warn('[FileStore] create: check your namingFunction. Error', generateError);
65+
return reject(500);
66+
}
67+
68+
const file = new File(file_id, upload_length, upload_defer_length, upload_metadata);
69+
70+
return fs.open(`${this.directory}/${file.id}`, 'w', (err, fd) => {
5171
if (err) {
5272
console.warn('[FileStore] create: Error', err);
5373
return reject(err);
@@ -61,7 +81,7 @@ class FileStore extends DataStore {
6181
return reject(exception);
6282
}
6383

64-
return resolve();
84+
return resolve(file);
6585
});
6686
});
6787
});

test/Test-DataStore.js

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
/* eslint-env node, mocha */
2+
/* eslint no-unused-vars: ["error", { "vars": "none" }] */
3+
14
'use strict';
25
const assert = require('assert');
36
const should = require('should');
@@ -12,6 +15,11 @@ describe('DataStore', () => {
1215
done();
1316
});
1417

18+
it('constructor must require the namingFunction to be a function, if it is provided', (done) => {
19+
assert.throws(() => { new DataStore({ path: '/files', namingFunction: {} }) }, Error);
20+
done();
21+
});
22+
1523
it('should provide extensions', (done) => {
1624
datastore.should.have.property('extensions');
1725
assert.equal(datastore.extensions, null);
@@ -30,14 +38,7 @@ describe('DataStore', () => {
3038

3139
it('must have a create method', (done) => {
3240
datastore.should.have.property('create');
33-
datastore.create(new File(1));
34-
done();
35-
});
36-
37-
it('create() must require a File', (done) => {
38-
assert.throws(() => {
39-
datastore.create();
40-
}, Error);
41+
datastore.create();
4142
done();
4243
});
4344

@@ -46,6 +47,7 @@ describe('DataStore', () => {
4647
datastore.write();
4748
done();
4849
});
50+
4951
it('must have a getOffset method', (done) => {
5052
datastore.should.have.property('getOffset');
5153
datastore.getOffset();

test/Test-File.js

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,33 @@
11
/* eslint-env node, mocha */
2+
23
'use strict';
34

45
const assert = require('assert');
56
const should = require('should');
67
const File = require('../lib/models/File');
8+
const Uid = require('../lib/models/Uid');
79

810
describe('File', () => {
911

1012
describe('constructor', () => {
11-
it('must be given either a upload_length or upload_defer_length', (done) => {
13+
it('must require a file_name', () => {
1214
assert.throws(() => { new File(); }, Error);
13-
done();
1415
});
1516

16-
it('should generate an the ID for the file', (done) => {
17-
const file = new File(1);
18-
file.should.have.property('id');
19-
assert.equal(typeof file.id, 'string');
20-
done();
17+
it('must be given either a upload_length or upload_defer_length', () => {
18+
assert.throws(() => { new File(Uid.rand()); }, Error);
2119
});
2220

23-
it('should set properties given', (done) => {
21+
it('should set properties given', () => {
22+
const file_id = Uid.rand();
2423
const upload_length = 1234;
2524
const upload_defer_length = 1;
2625
const upload_metadata = 'metadata';
27-
const file = new File(upload_length, upload_defer_length, upload_metadata);
26+
const file = new File(file_id, upload_length, upload_defer_length, upload_metadata);
27+
assert.equal(file.id, file_id);
2828
assert.equal(file.upload_length, upload_length);
2929
assert.equal(file.upload_defer_length, upload_defer_length);
3030
assert.equal(file.upload_metadata, upload_metadata);
31-
done();
3231
});
3332
});
3433
});

0 commit comments

Comments
 (0)