如何在Meteor中执行服务器端文件处理操作?

时间:2017-07-24 22:01:22

标签: node.js meteor server gridfs

我在服务器上使用GridFS存储Word(.docx)文件。我希望能够使用docx-builder NPM包将文档合并到一个Word文件中。

以下是我上传文件的方式:

Meteor.methods({
    uploadFiles: function (files) {
      check(files, [Object]);

      if (files.length < 1)
        throw new Meteor.Error("invalid-files", "No files were uploaded");

      var documentPaths = [];

      _.each(files, function (file) {
        ActivityFiles.insert(file, function (error, fileObj) {
          if (error) {
            console.log("Could not upload file");
          } else {
            documentPaths.push("/cfs/files/activities/" + fileObj._id);
          }
        });
      });

      return documentPaths;
    }
})

如何在服务器端执行此操作?我只能做这个服务器端,因为我使用的软件包需要fs软件包,而不能在客户端执行。

此处我正在努力解决这个问题。我从客户端调用以下方法(声明为Meteor.method):

print: function(programId) {
  // Get the program by ID.
  var program = Programs.findOne(programId);
  // Create a new document.
  var docx = new docxbuilder.Document();
  // Go through all activities in the program.
  program.activityIds.forEach(function(activityId) {
    // Create a temporary server side folder to store activity files.
    const tempDir = fs.mkdtempSync('/tmp/');
    // Get the activity by ID.
    var activity = Activities.findOne(activityId);
    // Get the document by ID.
    var document = ActivityFiles.findOne(activity.documents.pop()._id);
    // Declare file path to where file will be read.
    const filePath = tempDir + sep + document.name();
    // Create stream to write to path.
    const fileStream = fs.createWriteStream(filePath);
    // Read from document, write to file.
    document.createReadStream().pipe(fileStream);
    // Insert into final document when finished writinf to file.
    fileStream.on('finish', () => {
      docx.insertDocxSync(filePath);
      // Delete file when operation is completed.
      fs.unlinkSync(filePath);
    });
  });
  // Save the merged document.
  docx.save('/tmp' + sep + 'output.docx', function (error) {
    if (error) {
      console.log(error);
    }
    // Insert into Collection so client can access merged document.
    Fiber = Npm.require('fibers');
    Fiber(function() {
      ProgramFiles.insert('/tmp' + sep + 'output.docx');
    }).run();
  });
}

但是,当我从客户端的ProgramFiles集合下载最终文档时,该文档是一个空的Word文档。

这里出了什么问题?

我已将@ FrederickStark的答案纳入我的代码中。现在就停留在这个部分。

这是另一次尝试:

'click .merge-icon': (e) => {
    var programId = Router.current().url.split('/').pop();
    var programObj = Programs.findOne(programId);
    var insertedDocuments = [];
    programObj.activityIds.forEach(function(activityId) {
      var activityObj = Activities.findOne(activityId);
      var documentObj = ActivityFiles.findOne(activityObj.documents.pop()._id);
      JSZipUtils.getBinaryContent(documentObj.url(), callback);
      function callback(error, content) {
        var zip = new JSZip(content);
        var doc = new Docxtemplater().loadZip(zip);
        var xml = zip.files[doc.fileTypeConfig.textPath].asText();
        xml = xml.substring(xml.indexOf("<w:body>") + 8);
        xml = xml.substring(0, xml.indexOf("</w:body>"));
        xml = xml.substring(0, xml.indexOf("<w:sectPr"));
        insertedDocuments.push(xml);
      }
    });
    JSZipUtils.getBinaryContent('/assets/template.docx', callback);
    function callback(error, content) {
      var zip = new JSZip(content);
      var doc = new Docxtemplater().loadZip(zip);
      console.log(doc);
      setData(doc);
    }


    function setData(doc) {
      doc.setData({
        // Insert blank line between contents.
        inserted_docs_formatted: insertedDocuments.join('<w:br/><w:br/>')
        // The template file must use a `{@inserted_docs_formatted}` placeholder
        // that will be replaced by the above value.
      });

      doc.render();

      useResult(doc);
    }

    function useResult(doc) {
      var out = doc.getZip().generate({
        type: 'blob',
        mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
      });
      saveAs(out, 'output.docx');
    }

2 个答案:

答案 0 :(得分:3)

查看docx-builder的文档,它只支持从文件系统中读取docx文件。调用document.url()的问题在于它为您提供了一个可以通过http访问的URL,而不是文件系统上的路径。

因此,要使用GridFS,您首先需要将文件写入临时文件夹,然后docx-builder才能读取它们。

import fs from 'fs';
import { sep } from 'path';
const tempDir = fs.mkdtempSync('/tmp/' + sep);

program.activityIds.forEach(function(activityId) {
  var activity = Activities.findOne(activityId);
  console.log(activity);
  var document = ActivityFiles.findOne(activity.documents.pop()._id);
  documents.push(document);

  // Build a file path in the temp folder
  const filePath = tempDir + sep + document.name();

  // Write the document to the file system using streams
  const fileStream = fs.createWriteStream(filePath);
  document.createReadStream().pipe(fileStream);

  // When the stream has finished writing the file, add it to your docx
  fileStream.on('finish', () => {
    console.log(filePath);
    docx.insertDocxSync(filePath);
    // Delete the file after you're done
    fs.unlinkSync(filePath);
  });

});

我怀疑你可以使用fs.writeFileSync(filePath, document.data)同步执行此操作,但不确定,所以在示例中没有使用它。

或者,您可以查找可以支持从流或缓冲区读取的docx包,然后您将不需要临时文件。

答案 1 :(得分:3)

  

我只能做这个服务器端,因为我使用的软件包需要fs软件包,而不能在客户端执行。

虽然您当前使用的docx-builder库依赖于fs(因此在Node环境中),但可以直接使用其依赖项docxtemplater并使用它它只在客户端(浏览器)端。

非常像docx-builder一样,你可以从标准开始#34;模板文件,您可以将本地docx文件合并到其中,然后生成生成的docx文件并将其保存在本地或发送到您的服务器。

实现您要做的事情(即合并docx文件)的基本步骤,但在客户端将是:

  1. 检索客户端上的文件(如果网络中已存在),或者让用户通过文件类型输入和HTML5 File API从本地文件系统中选择文件:
  2. <input type="file" id="fileInput" />
    
    1. 阅读文件内容并提取其正文,如docx-builder中所述:
    2. var insertedDocsFormatted = [];
      
      // If the file is locally provided through File type input:
      document.getElementById('fileInput').addEventListener('change', function (evt) {
        var file = evt.currentTarget.files[0],
            fr = new FileReader();
      
        fr.onload = function () {
          insertDocContent(fr.result);
        };
        fr.readAsArrayBuffer(file);
      });
      
      // Or if the file already exists on a server:
      JSZipUtils.getBinaryContent(url, function (err, content) {
        insertDocContent(content);
      });
      
      function insertDocContent(content) {
        var zip = new JSZip(content),
            doc = new Docxtemplater().loadZip(zip);
      
        var xml = zip.files[doc.fileTypeConfig.textPath].asText();
      
        // Inspired from https://github.com/raulbojalil/docx-builder
        xml = xml.substring(xml.indexOf("<w:body>") + 8);
        xml = xml.substring(0, xml.indexOf("</w:body>"));
        xml = xml.substring(0, xml.indexOf("<w:sectPr"));
      
        // Keep for later use.
        insertedDocsFormatted.push(xml);
      }
      
      1. 处理完要合并的所有文件后,加载启动器模板文件
      2. // 'template.docx' is a static file on your server (e.g. in `public/` folder)
        // But it could even be replaced by a user locally provided file,
        // as done in step 2 above.
        JSZipUtils.getBinaryContent('template.docx', callback);
        
        function callback(err, content) {
          var zip = new JSZip(content);
          var doc = new Docxtemplater().loadZip(zip);
        
          setData(doc);
        }
        
        1. 加入内容并定义带格式的数据键,以便将其插入模板文件,然后呈现文档:
        2. function setData(doc) {
            doc.setData({
              // Insert blank line between contents.
              inserted_docs_formatted: insertedDocsFormatted.join('<w:br/><w:br/>')
              // The template file must use a `{@inserted_docs_formatted}` placeholder
              // that will be replaced by the above value.
            });
          
            doc.render();
          
            useResult(doc);
          }
          
          1. 提示&#34;另存为&#34; 对话框或将文件(blob)发送到您的服务器。
          2. function useResult(doc) {
              var out = doc.getZip().generate({
                type: 'blob',
                mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
              });
              saveAs(out, 'output.docx');
            }
            

            在线演示:https://ghybs.github.io/docx-builder-demo/(根本没有服务器端处理,纯客户端逻辑)

            源代码:https://github.com/ghybs/docx-builder-demo

            库: