UNPKG

3.8 kBJavaScriptView Raw
1const { extname } = require('path');
2const Proto = require('uberproto');
3const errors = require('@feathersjs/errors');
4const makeDebug = require('debug');
5
6const {
7 getBase64DataURI,
8 parseDataURI
9} = require('dauria');
10
11const toBuffer = require('concat-stream');
12const mimeTypes = require('mime-types');
13const debug = makeDebug('feathers-blob-store');
14
15const {
16 fromBuffer,
17 bufferToHash
18} = require('./util');
19
20class Service {
21 constructor (options) {
22 if (!options) {
23 throw new Error('feathers-blob-store: constructor `options` must be provided');
24 }
25
26 if (!options.Model) {
27 throw new Error('feathers-blob-store: constructor `options.Model` must be provided');
28 }
29
30 this.returnBuffer = options.returnBuffer || false;
31 this.returnUri = options.returnUri !== undefined ? options.returnUri : true;
32 this.Model = options.Model;
33 this.id = options.id || 'id';
34 }
35
36 extend (obj) {
37 return Proto.extend(obj, this);
38 }
39
40 get (id, params = {}) {
41 const s3Params = {
42 ...params.s3,
43 VersionId: (params.query && params.query.VersionId) ? params.query.VersionId : undefined
44 };
45
46 const ext = extname(id);
47 let contentType = mimeTypes.lookup(ext);
48 // Unrecognized mime type
49 if ((typeof contentType === 'boolean') && !contentType) {
50 // Fallback to binary content
51 contentType = 'application/octet-stream';
52 }
53 debug(`Retrieving blob ${id} with ext ${ext} and content type ${contentType}`);
54
55 return new Promise((resolve, reject) => {
56 this.Model.createReadStream({
57 key: id,
58 params: s3Params
59 })
60 .on('error', reject)
61 .pipe(toBuffer(buffer => {
62 resolve({
63 [this.id]: id,
64 ...(this.returnBuffer && { buffer }),
65 ...(this.returnUri && {
66 uri: getBase64DataURI(buffer, contentType)
67 }),
68 size: buffer.length,
69 contentType
70 });
71 }));
72 });
73 }
74
75 create (body, params = {}) {
76 let { id, uri, buffer, contentType } = body;
77 if (uri) {
78 const result = parseDataURI(uri);
79 contentType = result.MIME;
80 buffer = result.buffer;
81 } else {
82 uri = getBase64DataURI(buffer, contentType);
83 }
84
85 if (!uri && (!buffer || !contentType)) {
86 throw new errors.BadRequest('Buffer or URI with valid content type must be provided to create a blob');
87 }
88 let ext = mimeTypes.extension(contentType);
89
90 // Unrocognized mime type
91 if ((typeof ext === 'boolean') && !ext) {
92 // Fallback to binary content
93 ext = 'bin';
94 contentType = 'application/octet-stream';
95 }
96
97 if (!id) {
98 const hash = bufferToHash(buffer);
99 id = `${hash}.${ext}`;
100 }
101
102 debug(`Creating blob ${id} with ext ${ext} and content type ${contentType}`);
103 return new Promise((resolve, reject) => {
104 fromBuffer(buffer)
105 .pipe(this.Model.createWriteStream({
106 key: id,
107 params: params.s3
108 }, (error) =>
109 error
110 ? reject(error)
111 : resolve({
112 [this.id]: id,
113 ...(this.returnBuffer && { buffer }),
114 ...(this.returnUri && { uri }),
115 size: buffer.length,
116 contentType
117 })
118 ))
119 .on('error', reject);
120 });
121 }
122
123 remove (id) {
124 debug(`Removing blob ${id}`);
125 return new Promise((resolve, reject) => {
126 this.Model.remove({
127 key: id
128 }, error => error ? reject(error) : resolve({ [this.id]: id }));
129 });
130 }
131}
132
133module.exports = function init (options) {
134 return new Service(options);
135};
136
137module.exports.Service = Service;