Răsfoiți Sursa

Hello gridfs lib + fsHooks for mongodb bucket storage

David Arnold 4 ani în urmă
părinte
comite
18bd797ab9

+ 47 - 0
models/lib/fsHooks/createInterceptDownload.js

@@ -0,0 +1,47 @@
+import { createObjectId } from '../grid/createObjectId';
+
+const createInterceptDownload = bucket =>
+  function interceptDownload(http, file, versionName) {
+    const { gridFsFileId } = file.versions[versionName].meta || {};
+    if (gridFsFileId) {
+      // opens the download stream using a given gfs id
+      // see: http://mongodb.github.io/node-mongodb-native/3.2/api/GridFSBucket.html#openDownloadStream
+      const gfsId = createObjectId({ gridFsFileId });
+      const readStream = bucket.openDownloadStream(gfsId);
+
+      readStream.on('data', data => {
+        http.response.write(data);
+      });
+
+      readStream.on('end', () => {
+        http.response.end(); // don't pass parameters to end() or it will be attached to the file's binary stream
+      });
+
+      readStream.on('error', () => {
+        // not found probably
+        // eslint-disable-next-line no-param-reassign
+        http.response.statusCode = 404;
+        http.response.end('not found');
+      });
+
+      http.response.setHeader('Cache-Control', this.cacheControl);
+      http.response.setHeader(
+        'Content-Disposition',
+        getContentDisposition(file.name, http?.params?.query?.download),
+      );
+    }
+    return Boolean(gridFsFileId); // Serve file from either GridFS or FS if it wasn't uploaded yet
+  };
+
+/**
+ * Will initiate download, if links are called with ?download="true" queryparam.
+ **/
+const getContentDisposition = (name, downloadFlag) => {
+  const dispositionType = downloadFlag === 'true' ? 'attachment;' : 'inline;';
+
+  const encodedName = encodeURIComponent(fileName);
+  const dispositionName = `filename="${encodedName}"; filename=*UTF-8"${encodedName}";`;
+  const dispositionEncoding = 'charset=utf-8';
+
+  return `${dispositionType} ${dispositionName} ${dispositionEncoding}`;
+};

+ 17 - 0
models/lib/fsHooks/createOnAfterRemove.js

@@ -0,0 +1,17 @@
+import { createObjectId } from '../grid/createObjectId';
+
+const createOnAfterRemove = bucket =>
+  function onAfterRemove(files) {
+    files.forEach(file => {
+      Object.keys(file.versions).forEach(versionName => {
+        const gridFsFileId = (file.versions[versionName].meta || {})
+          .gridFsFileId;
+        if (gridFsFileId) {
+          const gfsId = createObjectId({ gridFsFileId });
+          bucket.delete(gfsId, err => {
+            // if (err) console.error(err);
+          });
+        }
+      });
+    });
+  };

+ 51 - 0
models/lib/fsHooks/createOnAfterUpload.js

@@ -0,0 +1,51 @@
+import { Meteor } from 'meteor/meteor';
+import fs from 'fs';
+
+export const createOnAfterUpload = bucket =>
+  function onAfterUpload(file) {
+    const self = this;
+
+    // here you could manipulate your file
+    // and create a new version, for example a scaled 'thumbnail'
+    // ...
+
+    // then we read all versions we have got so far
+    Object.keys(file.versions).forEach(versionName => {
+      const metadata = { ...file.meta, versionName, fileId: file._id };
+      fs.createReadStream(file.versions[versionName].path)
+
+        // this is where we upload the binary to the bucket using bucket.openUploadStream
+        // see http://mongodb.github.io/node-mongodb-native/3.2/api/GridFSBucket.html#openUploadStream
+        .pipe(
+          bucket.openUploadStream(file.name, {
+            contentType: file.type || 'binary/octet-stream',
+            metadata,
+          }),
+        )
+
+        // and we unlink the file from the fs on any error
+        // that occurred during the upload to prevent zombie files
+        .on('error', err => {
+          // console.error(err);
+          self.unlink(this.collection.findOne(file._id), versionName); // Unlink files from FS
+        })
+
+        // once we are finished, we attach the gridFS Object id on the
+        // FilesCollection document's meta section and finally unlink the
+        // upload file from the filesystem
+        .on(
+          'finish',
+          Meteor.bindEnvironment(ver => {
+            const property = `versions.${versionName}.meta.gridFsFileId`;
+
+            self.collection.update(file._id, {
+              $set: {
+                [property]: ver._id.toHexString(),
+              },
+            });
+
+            self.unlink(this.collection.findOne(file._id), versionName); // Unlink files from FS
+          }),
+        );
+    });
+  };

+ 9 - 0
models/lib/grid/createBucket.js

@@ -0,0 +1,9 @@
+import { MongoInternals } from 'meteor/mongo';
+
+export const createBucket = bucketName => {
+  const options = bucketName ? { bucketName } : void 0;
+  return new MongoInternals.NpmModule.GridFSBucket(
+    MongoInternals.defaultRemoteCollectionDriver().mongo.db,
+    options,
+  );
+};

+ 4 - 0
models/lib/grid/createObjectId.js

@@ -0,0 +1,4 @@
+import { MongoInternals } from 'meteor/mongo';
+
+export const createObjectId = ({ gridFsFileId }) =>
+  new MongoInternals.NpmModule.ObjectID(gridFsFileId);