Javascript 为什么fs.readFileSync在serveside上的承诺内不返回任何内容?

Javascript 为什么fs.readFileSync在serveside上的承诺内不返回任何内容?,javascript,node.js,meteor,promise,Javascript,Node.js,Meteor,Promise,我在MeteorChef上找到了关于在Meteor服务器端创建PDF文件并将其发送回客户端的教程。我其实不需要PDF文件,但需要docx文件,我想在使用officegen创建docx文件时也可以采用类似的方法 我创建了非常类似的服务器端模块,该模块从客户端的输入生成一个docx文件,然后尝试将它们转换为base64字符串,然后发送到客户端。但是,永远不会创建base64字符串 以下是模块: let myModule; 常量getBase64String=(loc)=>{ 试一试{ const

我在MeteorChef上找到了关于在Meteor服务器端创建PDF文件并将其发送回客户端的教程。我其实不需要PDF文件,但需要docx文件,我想在使用officegen创建docx文件时也可以采用类似的方法

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

以下是模块:

let myModule;
常量getBase64String=(loc)=>{
试一试{
const file=fs.readFileSync(loc);
返回新的缓冲区(file).toString('base64');
}捕获(例外){
myModule.reject(异常);
}
}
const generateBase64Docx=(路径、文件名)=>{
试一试{
解析({fileName,base64:getBase64String(path+fileName)});
财政司司长取消联系(loc);
}捕获(例外){
myModule.reject(异常);
}
}
常量formatComponentAsDocx=(道具,文件名)=>{
试一试{
var docxFile=officegen({
“类型”:“docx”,
“方向”:“肖像”,
“标题”:props.title,
});
var pObj=docxFile.createP();
pObj.addText(props.body);
变量路径='。/';
输出=fs.createWriteStream(路径+文件名);
docxFile.generate(输出);
返回路径;
}捕获(例外){
myModule.reject(异常);
}
}
常量处理程序=({props,fileName},promise)=>{
myModule=承诺;
常量路径=formatComponentAsDocx(道具,文件名);
如果(路径){
generateBase64Docx(路径、文件名);
}
}
导出常量generateComponentAsDocx=(选项)=>{
返回新承诺((解决、拒绝)=>{
返回处理程序(选项{resolve,reject});
});

};readFileSync是同步的,因此它不涉及承诺

您可能想使用fs.readFile


您的问题是
docxFile.generate(输出)不同步。因此,当您的本地路径存在时(它是由
fs.createWriteStream()
调用创建的),它是空的,并且您的同步
fs.readFileSync
正在捕获该空文件

您应该订阅
docxFile
finalize
事件以捕获文件生成结束:

docxFile.on('finalize,函数(writenbytes){
//您在这里处理生成的文件吗
});
因此,重写代码:

consthandler=({props,fileName},promise)=>{
myModule=承诺;
formatComponentAsDocx(道具、文件名);
}
常量formatComponentAsDocx=(道具,文件名)=>{
试一试{
var docxFile=officegen({
“类型”:“docx”,
“方向”:“肖像”,
“标题”:props.title,
});
var pObj=docxFile.createP();
pObj.addText(props.body);
变量路径='。/';
输出=fs.createWriteStream(路径+文件名);
docxFile.on('error',函数(err){
myModule.reject(错误);
});
docxFile.on('finalize',函数(){
generateBase64Docx(路径、文件名);
});
docxFile.generate(输出);
}捕获(例外){
myModule.reject(异常);
}
}

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

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

  • 使用
    myModule
    的整个繁琐设计是糟糕透顶的。如果我的一位同事提出了这样的准则,就会有激烈的言辞交流。是的,它是坏的。最好将整个代码库转换为使用承诺

  • 整个模块可简化为以下内容。我已经对这段代码做了一些测试,但我并不认为它能处理所有可能发生的事情

    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") };
      });
    };
    

    我认为这不是问题所在。我创建了一个类似于教程中创建PDF文件的模块,在那里fs.readFileSync将文件返回到一个缓冲区中。也许,我看不出该代码是从哪里调用的,但您在问题中提到了承诺,而且因为它是同步的,所以我很想知道。您是否正在尝试写入文件并在完成写入后检索它?如果是这种情况,写入方法应该是异步的。现在没有主意了。幸运的是,generateComponentAsDocx是在客户端通过meteor服务器方法使用基本meteor.call(…)调用的。只有道具被传递给模块,因为它是这个解决方案中唯一需要的外部数据。我在从Google云删除文件并尝试将其反应性地呈现给客户端时遇到了这个问题。文件被正确删除,但它们的句柄不会被反应性地渲染掉。它们只有在刷新后才会消失。所以这两种情况的根本问题是一样的,我想建议大家在黑暗中尝试一下
    fs.unlink(loc)可能在错误的时间启动,但随后意识到,
    loc
    在该时间点没有定义。。。下一步:
    formatComponentAsDocx()
    是同步调用吗?我猜不会,但如果不是,您是否会调用
    generateBase64Docx()
    来过早地编写其输出?这两个答案,以及来自Styx的答案,都很好,引导我找到了正确的方向,并实际解决了问题。但是,我会将此标记为正确答案,因为这更详细,并且还告诉我为什么我更喜欢使用“finish”而不是“finalize”。非常感谢。
        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.
    
    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") };
      });
    };