【问题标题】:How to intercept downloads of blob generated in client side of website through puppeteer?如何通过 puppeteer 拦截网站客户端生成的 blob 下载?
【发布时间】:2021-04-03 15:25:24
【问题描述】:

我在此链接 (https://master.d3tei1upkyr9mb.amplifyapp.com/report) 上有一个带有 3 个导出按钮的页面。 这些导出按钮在前端生成 XLSX、CSV、PDF,因此没有 XLSX、CSV、PDF 的 URL。

我需要 puppeteer 能够在我的节点后端下载或获取或拦截这些文件的 blob 或缓冲区。

我尝试了不同的方法来实现这一点,但仍然没有弄清楚。

通过 playwright 库通过下面编写的代码可以实现。但我需要能够用 Puppeteer 做到这一点。

const {chromium} = require('playwright');
const fs = require('fs');

(async () => {
    const browser = await chromium.launch();
    const context = await browser.newContext({acceptDownloads: true});
    const page = await context.newPage();

    await page.goto('http://localhost:3000/');

    const [ download ] = await Promise.all([
        page.waitForEvent('download'), // <-- start waiting for the download
        page.click('button#expoXLSX') // <-- perform the action that directly or indirectly initiates it.
    ]);

    const path = await download.path();

    console.log(path);

    const newFile = await fs.readFileSync(path);

    console.log(newFile);

    fs.writeFile("test.xlsx", newFile,  "binary",function(err) {
        if(err) {
            console.log(err);
        } else {
            console.log("The file was saved!");
        }
    });

    await browser.close()
})();

有什么办法吗?

【问题讨论】:

  • 对于上下文,在我的情况下,直到用户单击链接(他们正在使用 react-csv)才真正生成 blob。我不能使用 fetch() 请求,因为 url/blob 是空的,因为没有点击任何链接。
  • 我删除了我的答案,因为它只适用于 xlsx,但你看到了我希望的总体思路;-;

标签: node.js puppeteer google-chrome-headless


【解决方案1】:

有什么理由不模拟前端的点击并允许puppeteer 将文件下载到您选择的位置?您可以通过以下方式轻松下载文件:

编辑:您可以通过侦听Page.downloadProgress 事件并检查completed 状态来确定文件下载何时完成。使用此方法不能 100% 保证将实际文件名保存到磁盘,但是您可以从 Page.downloadWillBegin 事件中获得所谓的 suggestedFileName 事件,在我迄今为止的测试中(至少在示例页面上)在问题中)确实与持久化到磁盘的文件名匹配。

const puppeteer = require('puppeteer');
const path = require('path');
const downloadPath = path.resolve('./download');


(async ()=> {
  let fileName;
  const browser = await puppeteer.launch({
      headless: false
  });
  
  const page = await browser.newPage();
  await page.goto(
      'https://master.d3tei1upkyr9mb.amplifyapp.com/report', 
      { waitUntil: 'networkidle2' }
  );
  
  await page._client.send('Page.setDownloadBehavior', {
      behavior: 'allow',
      downloadPath: downloadPath 
  });

  await page._client.on('Page.downloadWillBegin', ({ url, suggestedFilename }) => {
    console.log('download beginning,', url, suggestedFilename);
    fileName = suggestedFilename;
  });

  await page._client.on('Page.downloadProgress', ({ state }) => {
    if (state === 'completed') {
      console.log('download completed. File location: ', downloadPath + '/' + fileName);
    }
  });

  await page.click('button#expoPDF');
})();

【讨论】:

  • 如何获取保存的文件名?我如何知道文件何时下载?
  • 您可以再执行一次 waitForNavigation 以确保文件已下载,然后再对其进行操作。在这种特定情况下,我们事先知道文件名:CSV_EXPORT.csv、ExcelExport.xlsx 和 repo.pdf。我已更新代码以包含导航等待。
  • 我们以前并不总是知道文件名,就好像它已经存在一样,它会将其重命名为(1) 或其他。这似乎是一个笨拙的解决方案,不是waitForNavigation 的忠实粉丝,尤其是因为页面上可能存在不会导致空闲时间的脚本。
  • 全面披露,弄清楚这一点所花费的时间比我预期的要长!好处是我学到了很多关于 Chrome DevTools 协议的知识。无论如何,我更新了我的代码。我们可以通过监听Page.downloadProgress 事件并检查“完成”状态来知道下载何时完成。文件名不是 100% 万无一失的,但我们可以通过监听 Page.downloadWillBegin 事件得到所谓的 suggestedFileName(在我的测试中一直是实际文件名)。希望这会有所帮助!
  • 完全披露,几天后,这是我最终的解决方案(使用 puppeteersharp)。 i.imgur.com/rIrRVLH.png
猜你喜欢
  • 2022-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多