【问题标题】:Node.js - exec command for tar files works correctly first time, but produces corrupted tar contents upon subsequent executionNode.js - tar 文件的 exec 命令第一次正常工作,但在后续执行时会产生损坏的 tar 内容
【发布时间】:2016-12-14 02:15:25
【问题描述】:

我正在使用 Node.js 构建一个 Web 应用程序,现在我需要生成 PDF 目录的 tar 存档。该应用程序在运行 Ubuntu 14.04 服务器的 VM 上运行。我的代码如下所示:

function tarDirectory(path, token, callback) {
  var exec = require('child_process').exec;
  var cmd = 'cd ' + path + ' && tar -cvf genericName-' + token + '.tar' + ' ' + token;

  exec(cmd, function(error, stdout, stderr) {
    console.log(stdout);
    console.log(stderr);
    if (error) {
      console.error(error);
    }
    if(callback) callback();
  });
}

这个 tarDirectory 函数由以下代码调用:

router.post('/files/generate', function(req, res, next) {
  IDList = req.body['IDs[]'];
  token = req.body['token'];

  // if just a single file being generated
  if (typeof req.body['IDs[]'] === "string"){
      filehelper.generateFile(IDList[0], req.app.locals.site.basedir + "temp/", token);
  }
  // if multiple files being generated
  else {
    IDList.forEach(function(id) {
      filehelper.generateFile(id, req.app.locals.site.basedir + "temp/", token);
    });
  }
  filehelper.tarDirectory(req.app.locals.site.basedir + "temp/", token, res.end);
});

代码需要一个带有动态数据的发布请求,该请求是通过在我的网络应用程序中单击按钮生成的,然后将基于数据创建文件并将其 tar 到一个目录中。这一切都很好……第一次。当我第一次单击该按钮时,会生成 tar,当我打开它时,客户端 PDF 与服务器上的 PDF 相同。但是,当我在大约一个小时内再次单击时,我会收到一个 tar 文件,但是当我打开存档并解压缩它时,PDF 全部损坏并且大约是预期字节大小的一半。我在这里不知所措...我怀疑这可能与对流关闭的不当处理有关,但我不确定。

这是将 PDF 生成到目录中的代码,然后在生成后将其打包:

function generateFile(id, path, token) {
  var dirPath = path + token;
  var filePath = path + token + "/file" + id + ".pdf";

  console.log("creating file for: " + id);

  try{
    fs.statSync(dirPath).isDirectory();
  } catch (err) {
    fs.mkdirSync(dirPath);
  }
  // start the file pdf generation
  file = new PDFDocument();
  output = fs.createWriteStream(filePath);
  output.on('close', function(){
    return;
  });

  file.pipe(output);

  // handle the intricacies of the file generation
  file.text("file" + id + ".pdf");

  // end the file
  file.end();
}

【问题讨论】:

    标签: node.js linux tar


    【解决方案1】:

    所以我实现了 Nazar 建议的承诺。如果我只生成一个文件,整个操作现在基本上可以正常工作,但如果我生成更多文件,我会得到相同的损坏 PDF。

    我生成单个文件的代码:

    function generateFile(id, path, token) {
      return new Promise(function(resolve, reject){
        var dirPath = path + token;
        var filePath = path + token + "/file" + id + ".pdf";
    
        console.log("creating file for: " + id);
    
        try{
          fs.statSync(dirPath).isDirectory();
        } catch (err) {
          fs.mkdirSync(dirPath);
        }
        // start the file pdf generation
        file = new PDFDocument();
        output = fs.createWriteStream(filePath);
    
        // stream handling
        output.on('finish', function(){
          console.log(fs.statSync(filePath)["size"]);
          return resolve();
        });
    
        output.on('error', function(error) {
          return reject(error);
        });
    
        // pipe the generated PDF to the output file
        file.pipe(output);
    
        // handle the intricacies of the transcript generation
        file.text("file" + id + ".pdf");
    
        // end the file
        file.end();
      });
    }
    

    我的 tar 目录代码:

    function tarDirectory(path, token) {
      return new Promise(function(resolve, reject){
        var exec = require('child_process').exec;
        var cmd = 'cd ' + path + ' && tar -cvf Files-' + token + '.tar' + ' ' + token;
    
        exec(cmd, function(error, stdout, stderr) {
          if (stdout != "") console.log(stdout);
          if (stderr != "") console.log(stderr);
          if (error) return reject(error);
          return resolve();
        });
      });
    }
    

    以及调用这两个辅助函数的代码:

    // submit request to generate files
    router.post('/files/generate', function(req, res, next) {
      IDList = req.body['IDs[]'];
      token = req.body['token'];
    
      // convert single fileID into list because Promise.map() needs iterable
      if (typeof IDList === "string") {
        IDList = [IDList];
      }
    
      Promise.map(IDList, function(id) {
        filehelper.generateFile(id, req.app.locals.site.basedir + "temp/", token);
      })
      .then(function() {
        return filehelper.tarDirectory(req.app.locals.site.basedir + "temp/", token);
      })
      .then(function() {
        res.end();
      })
      .catch(function(error) {
        throw new Error('Something went wrong while generating the tar file! :(\n' + error);
      });
    });
    

    非常感谢任何关于我在这里可能做错的进一步见解。

    【讨论】:

    • 执行下一个:function generateFile,将resolve() 替换为resolve({id:id, path: filePath})。在Promise.map - 第一个.then(function()then(function(files)。打印此文件,以验证名称是否正确。没有足够的信息。我认为你试图写入同一个文件。
    • 我解决了这个问题。这是因为我没有返回 Promise.map() 中生成的承诺。为你所有的帮助 Nazar 干杯。
    【解决方案2】:
    1. pdf 文件在压缩之前一切正常吗?
    2. 在您的generateFile 函数中,您有WriteStream,它是异步的。但是,您将此函数称为 sync.,并在 pdf 生成完成之前启动 .tar 压缩,这可能会导致此问题。
    3. 建议:尝试使用 promise 包装 generateFile,或迭代异步。只有在所有文件生成完成后才开始压缩。

    以蓝鸟为例:

    var Promise = require('bluebird');
    
    function generateFile(id, path, token) {
      return new Promise(function(resolve, reject) {
      var dirPath = path + token;
      var filePath = path + token + "/file" + id + ".pdf";
    
      console.log("creating file for: " + id);
    
      try{
        fs.statSync(dirPath).isDirectory();
      } catch (err) {
        fs.mkdirSync(dirPath);
      }
      // start the file pdf generation
      file = new PDFDocument();
      output = fs.createWriteStream(filePath);
      output.on('close', function(){
        return resolve();
      });
    
      output.on('error', function(error) {
        return reject(error);
      });
    
      file.pipe(output);
    
      // handle the intricacies of the file generation
      file.text("file" + id + ".pdf");
    
      // end the file
      file.end();
      });
    }
    

    Pdfs 生成和压缩。

     var Promise = require('bluebird');
    
        ....
    
        //IDList.forEach(function(id) {
        //      filehelper.generateFile(id, req.app.locals.site.basedir + "temp/", //token);});
    
        //replace with
    
        Promise.map(IDList, function(id) {
          return filehelper.generateFile(id, req.app.locals.site.basedir + "temp/", token);
        })
        .then(function() {
        //all files are ready, start compressing
        })
        .catch(function(error) {
        //we have error
        });
    

    【讨论】:

    • 1.是的,PDF 在服务器上的原始文件夹中是完整的,因此它们肯定是正确生成的。 2. 我认为这是在正确的轨道上......我会考虑为此使用 Promise。 3.谢谢你的例子:)
    • 因此,在实施了您对承诺的建议之后,如果它一次只生成一个 PDF,我能够让它正确生成 PDF。正在调查...会尽快回复您
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-03
    • 1970-01-01
    • 1970-01-01
    • 2016-10-08
    • 1970-01-01
    • 2018-12-11
    • 1970-01-01
    相关资源
    最近更新 更多