繁体   English   中英

为什么 nodeJs 不从磁盘读取整个二进制文件?

[英]Why is nodeJs not reading entire binary file from disk?

我有一个 PDF 文件,我想使用 NodeJS 将其读入 memory。 理想情况下,我想使用base64进行编码以进行传输。 但不知何故, read function 似乎没有读取完整的 PDF 文件,这对我来说毫无意义。 原始 PDF 是使用pdfKit生成的,并且可以使用 PDF 阅读器程序查看。

原始文件test.pdf在磁盘上有 90kB。 但是,如果我将其读写回磁盘,则只有 82kB 和新的 PDF test-out.pdf不行。 pdf 查看器说:

无法打开文档。 pdf 文档已损坏。

因此,base64 编码也无法正常工作。 我使用这个webservice对其进行了测试。 有人知道为什么以及这里发生了什么吗? 以及如何解决。

我已经找到了这个帖子。

fs = require('fs');
let buf = fs.readFileSync('test.pdf'); // returns raw buffer binary data
// buf = fs.readFileSync('test.pdf', {encoding:'base64'}); // for the base64 encoded data
// ...transfer the base64 data...
fs.writeFileSync('test-out.pdf', buf); // should be pdf again

编辑 MCVE:

const fs = require('fs');
const PDFDocument = require('pdfkit');

let filepath = 'output.pdf';

class PDF {
  constructor() {
    this.doc = new PDFDocument();
    this.setupdocument();
    this.doc.pipe(fs.createWriteStream(filepath));
  }

  setupdocument() {
    var pageNumber = 1;
    this.doc.on('pageAdded', () => {
        this.doc.text(++pageNumber, 0.5 * (this.doc.page.width - 100), 40, {width: 100, align: 'center'});
      }
    );

    this.doc.moveDown();
    // draw some headline text
    this.doc.fontSize(25).text('Some Headline');
    this.doc.fontSize(15).text('Generated: ' + new Date().toUTCString());
    this.doc.moveDown();
    this.doc.font('Times-Roman', 11);
  }

  report(object) {

    this.doc.moveDown();
    this.doc
      .text(object.location+' '+object.table+' '+Date.now())
      .font('Times-Roman', 11)
      .moveDown()
      .text(object.name)
      .font('Times-Roman', 11);

    this.doc.end();
    let report = fs.readFileSync(filepath);
    return report;
  }
}

let pdf = new PDF();
let buf = pdf.report({location: 'athome', table:'wood', name:'Bob'});
fs.writeFileSync('outfile1.pdf', buf);

fs.readFileSync()encoding选项是让您告诉readFile function 文件已经是什么编码,因此读取文件的代码知道如何解释它读取的数据。 它不会将其转换为该编码。

在这种情况下,您的 PDF 是二进制的 - 它不是base64所以您告诉它尝试将其从base64转换为二进制,这会导致数据混乱。

您根本不应该传递encoding选项,然后您将获得 RAW 二进制缓冲区(这是 PDF 文件的内容 - 原始二进制文件)。 如果您出于某种原因想要将其转换为 base64 ,则可以对其执行buf.toString('base64') 但是,这不是它的原生格式,如果你将转换后的数据写回磁盘,它就不是合法的 PDF 文件。

要仅将同一文件读取和写入到不同的文件名,请完全取消编码选项:

const fs = require('fs');
let buf = fs.readFileSync('test.pdf'); // get raw buffer binary data
fs.writeFileSync('test-out.pdf', buf); // write out raw buffer binary data

经过大量搜索,我发现了这个Github 问题。 我的问题中的问题似乎是doc.end()的调用,由于某种原因它不会等待 stream 完成(写入流的finish事件)。 因此,正如 Github 问题中所建议的,以下方法有效:

  • 基于回调:
doc = new PDFDocument();
writeStream = fs.createWriteStream('filename.pdf');
doc.pipe(writeStream);
doc.end()
writeStream.on('finish', function () {
    // do stuff with the PDF file
});
  • 或基于 promise:
const stream = fs.createWriteStream(localFilePath);
doc.pipe(stream);
.....
doc.end();
await new Promise<void>(resolve => {
  stream.on("finish", function() {
    resolve();
  });
});
  • 甚至更好,而不是直接调用doc.end() ,而是调用下面的 function savePdfToFile
function savePdfToFile(pdf : PDFKit.PDFDocument, fileName : string) : Promise<void> {
  return new Promise<void>((resolve, reject) => {

    //  To determine when the PDF has finished being written sucessfully 
    //  we need to confirm the following 2 conditions:
    //
    //  1. The write stream has been closed
    //  2. PDFDocument.end() was called syncronously without an error being thrown

    let pendingStepCount = 2;

    const stepFinished = () => {
      if (--pendingStepCount == 0) {
        resolve();
      }
    };

    const writeStream = fs.createWriteStream(fileName);
    writeStream.on('close', stepFinished);
    pdf.pipe(writeStream);

    pdf.end();

    stepFinished();
  }); 
}

此 function 应正确处理以下情况:

  • PDF 生成成功
  • 在写入 stream 之前在pdf.end()中抛出错误
  • 写入 stream 后在pdf.end()中引发错误已关闭

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM