简体   繁体   English

缓冲区中的 Blob 返回损坏的 pdf

[英]Blob from buffer returns corrupted pdf

Trying to make a pdf with Puppeteer on Firebase Functions: send in html snippet, get a pdf back.尝试在 Firebase 函数上使用 Puppeteer 制作 pdf:发送 html 片段,返回 pdf。

But the pdf is corrupted.但是pdf已损坏。 The problem is, I think, with returning the file / buffer.我认为问题在于返回文件/缓冲区。

// Server:

// setup
const func = require('firebase-functions');
const pptr = require('puppeteer');
const opts = { memory: '1GB', regions: ['europe-west3'] };
const call = func.runWith(opts).https.onCall
let browser = pptr.launch({ args: ['--no-sandbox'] });

// onCall
exports.makePdf = call(async (data) => {
  // this works, does open the page testing with { headless: false }
  const brws = await (await browser).createIncognitoBrowserContext();
  const page = await brws.newPage();    
  await page.setContent(data, { waitUntil: 'domcontentloaded' });
  const file = await page.pdf({ format: 'A4' });
  await brws.close();
  // the problem is returning the file
  return file
});

When file is logged on the server it's a buffer <Buffer 25 50 44 46 2d 31 2e ... 11150 more bytes> , but when logged on the client it's an object, in dec and not hex { data: {0: 37, 1: 80, 2: 68, 3: 70, ... }} .file记录在服务器上时,它是一个缓冲区<Buffer 25 50 44 46 2d 31 2e ... 11150 more bytes> ,但当登录到客户端时,它是一个对象,以十进制而不是十六进制{ data: {0: 37, 1: 80, 2: 68, 3: 70, ... }}

Convert that back to buffer?将其转换回缓冲区? Convert back to hex?转换回十六进制? Which buffer?哪个缓冲区?

// Client:

// send html and receive the file
let html = '<div><h1>Hi</h1></div>';
let file = ((await fns.makePdf(html))).data;
// also tried buffer = new Buffer.from(JSON.stringify(file));
let blob = new Blob(file, { type: 'application/pdf' });

// download the file
let a = document.createElement('a');
a.href = window.URL.createObjectURL(blob);
a.download = `${name}.pdf`;
a.click();

Or is the pdf corrupted because I'm downloading it wrong ( createObjectURL )?还是因为我下载错误( createObjectURL )而导致 pdf 损坏? Or onCall functions can't be used this way?或者 onCall 函数不能这样使用?

Point is, don't know why it's not working.重点是,不知道为什么它不起作用。 Thanks for your help谢谢你的帮助

Ok.好的。 /insert cursing the gods/ /插入诅咒众神/

Server:服务器:

So it was ok, you CAN use onCall instead of onRequest, because you CAN create a pdf in the end from the json response.所以没关系,您可以使用 onCall 而不是 onRequest,因为您最终可以从 json 响应创建一个 pdf。 The reason onCall is a bit better is because you don't need to bother with cors or headers, Google does it for you. onCall 好一点的原因是因为你不需要打扰 cors 或 headers,谷歌为你做。

// setup
const func = require('firebase-functions');
const pptr = require('puppeteer');
const opts = { memory: '1GB', regions: ['europe-west3'] };
const call = func.runWith(opts).https.onCall

// this runs outside of the function because it might not be killed off
// if the function is called quickly in succession (lookup details on 
// google cloud functions docs)
let browser = pptr.launch({ args: ['--no-sandbox'] });

// the main function
exports.makePdf = call(async (data) => {
  // prep puppeteer so it can print the pdf
  const brws = await (await browser).createIncognitoBrowserContext();
  const page = await brws.newPage();    
  await page.setContent(data);
  const file = await page.pdf({
    format: 'A4',
    printBackground: false,
    margin: { left: 0, top: 0, right: 0, bottom: 0 }
  });
  brws.close();
  // and just return the stream
  return file
});

Client:客户:

The trick was on the client.诀窍在于客户端。 I'll note with comments.我会在评论中注明。

// so this is inside some function, has to be because it needs to be async
var a, blob, buffer, d, file, html, i, result;
// this html is for testing, it would obviously be gathered in another way
html = '<div><h1>Hi</h1></div>';

// we call the function from the client
// fns is my shorthand object defined on Google Functions initialization
// it is literally: f.httpsCallable('makePdf') where f is the functions object
// you get from initialization
file = ((await fns.makePdf(html))).data;
// additional .data is needed because onCall automatically wraps

// now the point

// 1. convert object to array
result = []; for (i in file) {
  d = file[i]; result.push(d);
}
// 2. convert that to a Uint8Array
buffer = new Uint8Array(result);
// 3. create a blob, BUT the buffer needs to go inside another array
blob = new Blob([buffer], { type: 'application/pdf' });

// finally, download it
a = document.createElement('a');
a.href = window.URL.createObjectURL(blob);
a.download = `${path}.pdf`;
a.click();

I found this project similar than yours and I found a difference, in the buffer return you need to specify the headers and the HTTP code, the client browser possibly is misinterpreting the object from the server.我发现这个项目与你的相似,我发现了一个不同之处,在缓冲区返回中你需要指定标头和 HTTP 代码,客户端浏览器可能会误解来自服务器的对象。

This is the return segment of the other project这是另一个项目的返回段

const pdfBuffer = await page.pdf({ printBackground: true });

res.set("Content-Type", "application/pdf");
res.status(200).send(pdfBuffer);

Everything in your code looks right and if transforming the PDF from dec to hex return the same string, I think that could be a good workarround.代码中的所有内容看起来都正确,如果将 PDF 从 dec 转换为 hex 返回相同的字符串,我认为这可能是一个很好的解决方法。

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

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