为什么fs.readFileSync在serveide上的promise中没有返回任何内容?

时间:2017-05-05 18:51:40

标签: javascript node.js meteor promise

我在themeteorchef上找到了关于在Meteor服务器端创建PDF文件然后将它们发送回客户端的"Rendering PDFs with React components" 教程。我并不真的需要PDF文件,而是使用docx文件,并认为在使用 officegen

创建docx文件时,我可能会采用类似的方法

我创建了非常相似的服务器端模块,它从客户端的输入生成docx文件,然后尝试将它们转换为base64字符串,然后将其发送到客户端。但是,永远不会创建base64字符串。

这是模块:



let myModule;

const getBase64String = (loc) => {
  try {
    const file = fs.readFileSync(loc);
    return new Buffer(file).toString('base64');
  } catch (exception) {
    myModule.reject(exception);
  }
}

const generateBase64Docx = (path, fileName) => {
  try {
    myModule.resolve({fileName, base64: getBase64String(path+fileName)});
    fs.unlink(loc);
  } catch (exception) {
    myModule.reject(exception);
  }

}

const formatComponentAsDocx = (props, fileName) => {
  try {
    var docxFile = officegen({
      'type': 'docx',
      'orientation': 'portrait',
      'title': props.title,
    });

    var pObj = docxFile.createP();
    pObj.addText(props.body);

    var path = './';
    output = fs.createWriteStream(path+fileName);

    docxFile.generate(output);
    return path;

  } catch (exception) {
    myModule.reject(exception);
  }

}

const handler = ({props, fileName}, promise) => {
  myModule = promise;
  const path = formatComponentAsDocx(props, fileName);
  if (path) {
    generateBase64Docx(path, fileName);
  }
}

export const generateComponentAsDocx = (options) => {
  return new Promise((resolve, reject) => {
    return handler(options, { resolve, reject });
  });
};




这里的问题是 fs.readFileSync 部分。它总是返回空缓冲区,这就是为什么文件永远不会转换为base64字符串而且也永远不会发送回客户端的原因。为什么?文件本身始终在服务器上创建,并且始终可以找到。

如果我将const file = fs.readFileSync(loc);部分更改为例如此

fs.readFile(loc, (err, data) => {
 if(err) myModule.reject(err);
 console.log(JSON.stringify(data));
}

我可以在数据中看到一些数据,但对于整个文件来说还不够。

我在这里做错了什么?我错过了什么吗?

3 个答案:

答案 0 :(得分:2)

您的问题是docxFile.generate(output); 不同步。因此,虽然您的本地路径存在(它是由fs.createWriteStream()调用创建的),但它是空的,而您的同步 fs.readFileSync正在捕获该空文件。

您应订阅docxFile的{​​{1}}事件以捕获文件生成结束:

finalize

因此,重写代码:

docxFile.on('finalize, function (writtenBytes) {
  // do you work with generated file here
});

答案 1 :(得分:2)

您需要等到officegen生成的文件完成后才能尝试从其中取出base64。这是您需要做出的最小改变。我不建议等待finalize生成的officegen事件,因为此事件为buggy。我建议等待输出流的finish事件。但是,您显示的代码存在其他问题

  1. 由于您有使用它后立即取消链接文件的代码,因此我推断您不需要文件。因此,您只需在内存中创建数据并从中获取base64字符串。

  2. myModule的整个过程非常糟糕。如果我的一位同事提出了这样的代码,那么就会交换强有力的话语。是的, 不好。将整个代码库转换为使用promises更好。

  3. 整个模块可以简化为以下内容。我已对此代码进行了一些测试,但我并未声称它可以处理所有可能性。

    import * as stream from "stream";
    import officegen from "officegen";
    
    function formatComponentAsDocx(props) {
      return new Promise((resolve, reject) => {
        // There's no need to wrap this in try...catch only to call reject. If any
        // exception is raised in this function, the promise is automatically
        // rejected.
        const docxFile = officegen({
          'type': 'docx',
          'orientation': 'portrait',
          'title': props.title,
        });
    
        const pObj = docxFile.createP();
        pObj.addText(props.body);
    
        // We record the output in our own buffer instead of writing to disk,
        // and reading later.
        let buf = Buffer.alloc(0);
        const output = new stream.Writable({
          write(chunk, encoding, callback) {
            buf = Buffer.concat([buf, chunk]);
            callback();
          },
        });
    
        docxFile.generate(output, {
          // Do propagate errors from officegen.
          error: reject,
        });
    
        // We don't use the "finalize" event that docxFile.generate would emit
        // because it is buggy. Instead, we wait for the output stream to emit
        // the "finish" event.
        output.on('finish', () => {
          resolve(buf);
        });
      });
    }
    
    export function generateComponentAsDocx({ props }) {
      return formatComponentAsDocx(props).then((data) => {
        return { base64: data.toString("base64") };
      });
    };
    

答案 2 :(得分:-2)

readFileSync是同步的,因此它不会处理承诺。

https://nodejs.org/api/fs.html#fs_fs_readfilesync_file_options

Synchronous version of fs.readFile. Returns the contents of the file.

您可能想要使用fs.readFile。

https://nodejs.org/api/fs.html#fs_fs_readfile_file_options_callback

    The callback is passed two arguments (err, data), where data is the contents of the file.

    If no encoding is specified, then the raw buffer is returned.