UNPKG

3.36 kBJavaScriptView Raw
1var crypto = require('crypto')
2var stream = require('stream')
3var fileType = require('file-type')
4
5function staticValue (value) {
6 return function (req, file, cb) {
7 cb(null, value)
8 }
9}
10
11function defaultKey (req, file, cb) {
12 crypto.randomBytes(16, function (err, raw) {
13 cb(err, err ? undefined : raw.toString('hex'))
14 })
15}
16
17function defaultContentType (req, file, cb) {
18 setImmediate(function () { cb(null, 'application/octet-stream') })
19}
20
21function autoContentType (req, file, cb) {
22 file.stream.once('data', function (firstChunk) {
23 var type = fileType(firstChunk)
24 var mime = (type === null ? 'application/octet-stream' : type.mime)
25 var outStream = new stream.PassThrough()
26
27 outStream.write(firstChunk)
28 file.stream.pipe(outStream)
29
30 cb(null, mime, outStream)
31 })
32}
33
34function collect (storage, req, file, cb) {
35 storage.getBucket(req, file, function (err, bucket) {
36 if (err) return cb(err)
37
38 storage.getKey(req, file, function (err, key) {
39 if (err) return cb(err)
40
41 storage.getContentType(req, file, function (err, contentType, replacementStream) {
42 if (err) return cb(err)
43
44 cb.call(storage, null, {
45 bucket: bucket,
46 key: key,
47 contentType: contentType,
48 replacementStream: replacementStream
49 })
50 })
51 })
52 })
53}
54
55function S3Storage (opts) {
56 switch (typeof opts.s3) {
57 case 'object': this.s3 = opts.s3; break
58 default: throw new TypeError('Expected opts.s3 to be object')
59 }
60
61 switch (typeof opts.bucket) {
62 case 'function': this.getBucket = opts.bucket; break
63 case 'string': this.getBucket = staticValue(opts.bucket); break
64 case 'undefined': throw new Error('bucket is required')
65 default: throw new TypeError('Expected opts.bucket to be undefined, string or function')
66 }
67
68 switch (typeof opts.key) {
69 case 'function': this.getKey = opts.key; break
70 case 'undefined': this.getKey = defaultKey; break
71 default: throw new TypeError('Expected opts.key to be undefined or function')
72 }
73
74 switch (typeof opts.contentType) {
75 case 'function': this.getContentType = opts.contentType; break
76 case 'undefined': this.getContentType = defaultContentType; break
77 default: throw new TypeError('Expected opts.contentType to be undefined or function')
78 }
79}
80
81S3Storage.prototype._handleFile = function (req, file, cb) {
82 collect(this, req, file, function (err, opts) {
83 if (err) return cb(err)
84
85 var currentSize = 0
86 var upload = this.s3.upload({
87 Bucket: opts.bucket,
88 Key: opts.key,
89 ContentType: opts.contentType,
90 Body: (opts.replacementStream || file.stream)
91 })
92
93 upload.on('httpUploadProgress', function (ev) {
94 if (ev.total) currentSize = ev.total
95 })
96
97 upload.send(function (err, result) {
98 if (err) return cb(err)
99
100 cb(null, {
101 size: currentSize,
102 bucket: opts.bucket,
103 key: opts.key,
104 contentType: opts.contentType,
105 location: result.Location,
106 etag: result.ETag
107 })
108 })
109 })
110}
111
112S3Storage.prototype._removeFile = function (req, file, cb) {
113 this.s3.deleteObject({ Bucket: file.bucket, Key: file.key }, cb)
114}
115
116module.exports = function (opts) {
117 return new S3Storage(opts)
118}
119
120module.exports.AUTO_CONTENT_TYPE = autoContentType
121module.exports.DEFAULT_CONTENT_TYPE = defaultContentType