Browse Source

WIP: refactor attachment

* /_api/attachments.add
Yuki Takei 7 years ago
parent
commit
ebdcd5d64c

+ 1 - 2
src/client/js/components/PageComment/CommentForm.jsx

@@ -181,11 +181,10 @@ export default class CommentForm extends React.Component {
     // post
     this.props.crowi.apiPost(endpoint, formData)
       .then((res) => {
-        const url = res.url;
         const attachment = res.attachment;
         const fileName = attachment.originalName;
 
-        let insertText = `[${fileName}](${url})`;
+        let insertText = `[${fileName}](${attachment.filePathProxied})`;
         // when image
         if (attachment.fileFormat.startsWith('image/')) {
           // modify to "![fileName](url)" syntax

+ 2 - 3
src/client/js/components/PageEditor.js

@@ -141,12 +141,11 @@ export default class PageEditor extends React.Component {
       formData.append('page_id', this.state.pageId || 0);
 
       // post
-      res = await this.props.crowi.apiPost(endpoint, formData)
-      const url = res.url;
+      res = await this.props.crowi.apiPost(endpoint, formData);
       const attachment = res.attachment;
       const fileName = attachment.originalName;
 
-      let insertText = `[${fileName}](${url})`;
+      let insertText = `[${fileName}](${attachment.filePathProxied})`;
       // when image
       if (attachment.fileFormat.startsWith('image/')) {
         // modify to "![fileName](url)" syntax

+ 14 - 49
src/server/models/attachment.js

@@ -45,61 +45,26 @@ module.exports = function(crowi) {
     const filePath = urljoin('/attachment', pageId.toString(), this.fileName);
 
     return filePath;
-  });
-
-  attachmentSchema.statics.create = function(pageId, creator, filePath, originalName, fileName, fileFormat, fileSize) {
-    var Attachment = this;
-
-    return new Promise(function(resolve, reject) {
-      var newAttachment = new Attachment();
-
-      newAttachment.page = pageId;
-      newAttachment.creator = creator._id;
-      newAttachment.filePath = filePath;
-      newAttachment.originalName = originalName;
-      newAttachment.fileName = fileName;
-      newAttachment.fileFormat = fileFormat;
-      newAttachment.fileSize = fileSize;
-      newAttachment.createdAt = Date.now();
-
-      newAttachment.save(function(err, data) {
-        if (err) {
-          debug('Error on saving attachment.', err);
-          return reject(err);
-        }
-        debug('Attachment saved.', data);
-        return resolve(data);
-      });
-    });
   };
 
-  attachmentSchema.statics.guessExtByFileType = function(fileType) {
-    let ext = '';
-    const isImage = fileType.match(/^image\/(.+)/i);
+  attachmentSchema.statics.create = async function(pageId, user, fileStream, originalName, fileFormat, fileSize) {
+    const Attachment = this;
 
-    if (isImage) {
-      ext = isImage[1].toLowerCase();
-    }
+    const fileName = generateFileHash(originalName);
 
-    return ext;
-  };
+    // upload file
+    await fileUploader.uploadFile(fileStream, fileName, fileFormat);
 
-  attachmentSchema.statics.createAttachmentFilePath = function(pageId, fileName, fileType) {
-    const Attachment = this;
-    let ext = '';
-    const fnExt = fileName.match(/(.*)(?:\.([^.]+$))/);
-
-    if (fnExt) {
-      ext = '.' + fnExt[2];
-    }
-    else {
-      ext = Attachment.guessExtByFileType(fileType);
-      if (ext !== '') {
-        ext = '.' + ext;
-      }
-    }
+    let attachment = new Attachment();
+    attachment.page = pageId;
+    attachment.creator = user._id;
+    attachment.originalName = originalName;
+    attachment.fileName = fileName;
+    attachment.fileFormat = fileFormat;
+    attachment.fileSize = fileSize;
+    attachment.createdAt = Date.now();
 
-    return 'attachment/' + pageId + '/' + generateFileHash(fileName) + ext;
+    return await attachment.save();
   };
 
   attachmentSchema.statics.removeAttachmentsByPageId = function(pageId) {

+ 27 - 35
src/server/routes/attachment.js

@@ -164,25 +164,28 @@ module.exports = function(crowi, app) {
    * @apiParam {File} file
    */
   api.add = async function(req, res) {
-    var id = req.body.page_id || 0,
-      path = decodeURIComponent(req.body.path) || null,
-      pageCreated = false,
-      page = {};
+    const pageId = req.body.page_id || null;
+    const pagePath = decodeURIComponent(req.body.path) || null;
+    const pageCreated = false;
 
-    debug('id and path are: ', id, path);
+    if (pageId == null && pagePath == null) {
+      return res.json(ApiResponse.error('Either page_id or path is required.'));
+    }
+    if (!req.file) {
+      return res.json(ApiResponse.error('File error.'));
+    }
 
-    var tmpFile = req.file || null;
+    const tmpFile = req.file;
     const isUploadable = await fileUploader.checkCapacity(tmpFile.size);
     if (!isUploadable) {
       return res.json(ApiResponse.error('MongoDB for uploading files reaches limit'));
     }
+
     debug('Uploaded tmpFile: ', tmpFile);
-    if (!tmpFile) {
-      return res.json(ApiResponse.error('File error.'));
-    }
+
     new Promise(function(resolve, reject) {
-      if (id == 0) {
-        if (path === null) {
+      if (pageId == null) {
+        if (pagePath == null) {
           throw new Error('path required if page_id is not specified.');
         }
         debug('Create page before file upload');
@@ -194,33 +197,22 @@ module.exports = function(crowi, app) {
           .catch(reject);
       }
       else {
-        Page.findById(id).then(resolve).catch(reject);
+        Page.findById(pageId).then(resolve).catch(reject);
       }
     }).then(function(pageData) {
-      page = pageData;
-      id = pageData._id;
-
-      var tmpPath = tmpFile.path,
-        originalName = tmpFile.originalname,
-        fileName = tmpFile.filename + tmpFile.originalname,
-        fileType = tmpFile.mimetype,
-        fileSize = tmpFile.size,
-        filePath = Attachment.createAttachmentFilePath(id, fileName, fileType),
-        tmpFileStream = fs.createReadStream(tmpPath, {flags: 'r', encoding: null, fd: null, mode: '0666', autoClose: true });
-
-      return fileUploader.uploadFile(filePath, fileType, tmpFileStream, {})
-        .then(function(data) {
-          debug('Uploaded data is: ', data);
-
-          // TODO size
-          return Attachment.create(id, req.user, filePath, originalName, fileName, fileType, fileSize);
-        }).then(function(data) {
-          var fileUrl = data.fileUrl;
-
-          var result = {
+      const page = pageData;
+
+      const tmpPath = tmpFile.path;
+      const originalName = tmpFile.originalname;
+      const fileType = tmpFile.mimetype;
+      const fileSize = tmpFile.size;
+      const tmpFileStream = fs.createReadStream(tmpPath, {flags: 'r', encoding: null, fd: null, mode: '0666', autoClose: true });
+
+      return Attachment.create(pageId, req.user, tmpFileStream, originalName, fileType, fileSize)
+        .then(function(attachment) {
+          const result = {
             page: page.toObject(),
-            attachment: data.toObject(),
-            url: fileUrl,
+            attachment: attachment.toObject({ virtuals: true }),
             pageCreated: pageCreated,
           };
 

+ 9 - 21
src/server/service/file-uploader/gridfs.js

@@ -1,5 +1,7 @@
 const debug = require('debug')('growi:service:fileUploaderGridfs');
+const logger = require('@alias/logger')('growi:service:fileUploaderGridfs');
 const mongoose = require('mongoose');
+const util = require('util');
 
 module.exports = function(crowi) {
   'use strict';
@@ -17,6 +19,9 @@ module.exports = function(crowi) {
   const AttachmentFile = gridfs.model;
   const Chunks = mongoose.model('Chunks', gridfs.schema, 'attachmentFiles.chunks');
 
+  // create promisified method
+  AttachmentFile.promisifiedWrite = util.promisify(AttachmentFile.write).bind(AttachmentFile);
+
   // delete a file
   lib.deleteFile = async function(fileId, filePath) {
     debug('File deletion: ' + fileId);
@@ -57,27 +62,10 @@ module.exports = function(crowi) {
     return (+process.env.MONGO_GRIDFS_TOTAL_LIMIT > usingFilesSize + +uploadFileSize);
   };
 
-  lib.uploadFile = async function(filePath, contentType, fileStream, options) {
-    debug('File uploading: ' + filePath);
-    await writeFile(filePath, contentType, fileStream);
-  };
+  lib.uploadFile = async function(fileStream, fileName, contentType) {
+    logger.debug(`File uploading: fileName=${fileName}`);
 
-  /**
-   * write file to MongoDB with GridFS (Promise wrapper)
-   */
-  const writeFile = (filePath, contentType, fileStream) => {
-    return new Promise((resolve, reject) => {
-      AttachmentFile.write({
-        filename: filePath,
-        contentType: contentType
-      }, fileStream,
-      function(error, createdFile) {
-        if (error) {
-          reject(error);
-        }
-        resolve();
-      });
-    });
+    AttachmentFile.promisifiedWrite({ filename: fileName, contentType }, fileStream);
   };
 
   /**
@@ -87,7 +75,7 @@ module.exports = function(crowi) {
    * @return {stream.Readable} readable stream
    */
   lib.findDeliveryFile = async function(attachment) {
-    const attachmentFile = await AttachmentFile.findOne({ fileName: attachment.fileName });
+    const attachmentFile = await AttachmentFile.findOne({ filename: attachment.fileName });
 
     if (attachmentFile == null) {
       throw new Error(`Any AttachmentFile that relate to the Attachment (${attachment._id.toString()}) does not exist in GridFS`);