使用特定配置打开 Puppeteer(下载 PDF 而不是 PDF 查看器)

Posted

技术标签:

【中文标题】使用特定配置打开 Puppeteer(下载 PDF 而不是 PDF 查看器)【英文标题】:Open Puppeteer with specific configuration (download PDF instead of PDF viewer) 【发布时间】:2019-10-08 18:51:51 【问题描述】:

我想使用特定配置打开 Chromium。

我正在寻找配置to activate the following option:

设置 => 站点设置 => 权限 => PDF 文档 => “下载 PDF 文件而不是在 Chrome 中自动打开它们”

我在this command line switch page 上搜索了标签,但处理pdf 的唯一参数是--print-to-pdf,这不符合我的需要。

你有什么想法吗?

【问题讨论】:

【参考方案1】:

没有选项可以传递给 Puppeteer 来强制下载 PDF。但是,您可以使用 chrome-devtools-protocol 添加 content-disposition: attachment 响应标头来强制下载。

您需要做的事情的可视化流程:

我将在下面包含一个完整的示例代码。在下面的示例中,PDF 文件和 XML 文件将以 headful 模式下载。

const puppeteer = require('puppeteer');

(async () => 
  const browser = await puppeteer.launch(
    headless: false,
    defaultViewport: null, 
  );

  const page = await browser.newPage();

  const client = await page.target().createCDPSession();

  await client.send('Fetch.enable', 
    patterns: [
      
        urlPattern: '*',
        requestStage: 'Response',
      ,
    ],
  );

  await client.on('Fetch.requestPaused', async (reqEvent) => 
    const  requestId  = reqEvent;

    let responseHeaders = reqEvent.responseHeaders || [];
    let contentType = '';

    for (let elements of responseHeaders) 
      if (elements.name.toLowerCase() === 'content-type') 
        contentType = elements.value;
      
    

    if (contentType.endsWith('pdf') || contentType.endsWith('xml')) 

      responseHeaders.push(
        name: 'content-disposition',
        value: 'attachment',
      );

      const responseObj = await client.send('Fetch.getResponseBody', 
        requestId,
      );

      await client.send('Fetch.fulfillRequest', 
        requestId,
        responseCode: 200,
        responseHeaders,
        body: responseObj.body,
      );
     else 
      await client.send('Fetch.continueRequest',  requestId );
    
  );

  await page.goto('https://pdf-xml-download-test.vercel.app/');

  await page.waitFor(100000);

  await client.send('Fetch.disable');

  await browser.close();
)();

更详细的解释请参考我用cmets设置的Git repo。它还包括playwright 的示例代码。

【讨论】:

我已经尝试了很多解决方案,但在我的情况下只有这个工作,除了 await page.goto 抛出 net::ERR_ABORTED 错误。通过捕获错误并忽略它,然后验证本地路径中的文件以确认下载成功或不解决问题。【参考方案2】:

Puppeteer 目前不支持导航(或下载)PDF 在无头模式下很容易。引用 page.goto 函数的文档:

注意 无头模式不支持导航到 PDF 文档。请参阅upstream issue。

不过,您可以做的是检测浏览器是否正在导航到 PDF 文件,然后通过 Node.js 自行下载。

代码示例

const puppeteer = require('puppeteer');
const http = require('http');
const fs = require('fs');

(async () => 
    const browser = await puppeteer.launch();
    const page = await browser.newPage();

    page.on('request', req => 
        if (req.url() === '...') 
            const file = fs.createWriteStream('./file.pdf');
            http.get(req.url(), response => response.pipe(file));
        
    );

    await page.goto('...');
    await browser.close();
)();

这会导航到一个 URL 并监控正在进行的请求。如果找到“匹配的请求”,Node.js 将通过http.get 手动下载文件并将其通过管道传输到file.pdf。请注意,这是一个最小的工作示例。你想catch errors when downloading 并且可能还想使用比http.get 更复杂的东西,具体取决于具体情况。

未来说明

将来,可能会有更简单的方法来做到这一点。当 puppeteer 支持 response interception 时,您将能够简单地 force the browser to download 文档,但目前不支持此功能(2019 年 5 月)。

【讨论】:

以上是关于使用特定配置打开 Puppeteer(下载 PDF 而不是 PDF 查看器)的主要内容,如果未能解决你的问题,请参考以下文章

使用 puppeteer 将 PDF 保存到文件

Puppeteer 无法打开 chrome(出现错误)

使用 puppeteer 生成 PDF 而不保存

如何使用 puppeteer 和 Node js 为 pdf 页面生成屏幕截图

如何在 Android 11 Xamarin Forms 中下载和打开 PDF

玩转Puppeteer