使用CDP拦截请求时,如何直接返回二进制数据实现用户下载?
解决CDP拦截请求并触发直接下载的问题
虽然Fetch.fulfillRequest确实只接受base64编码的body,但这并不影响实现直接下载的需求——只需将二进制数据转成base64,再配合正确的响应头配置,就能让浏览器自动触发下载行为。
核心思路
- 二进制转base64:这是CDP的强制要求,所有返回的body必须是base64格式,可通过语言自带的工具函数完成转换(比如Node.js的
Buffer.toString('base64'))。 - 配置响应头触发下载:关键设置
Content-Disposition为attachment; filename="文件名.后缀",同时匹配文件对应的Content-Type,让浏览器识别这是需要下载的文件而非可渲染内容。
代码示例(Puppeteer场景)
const puppeteer = require('puppeteer'); const fs = require('fs'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); // 读取本地二进制文件作为待返回的下载内容 const fileBuffer = fs.readFileSync('./sample.zip'); const base64Body = fileBuffer.toString('base64'); // 开启请求拦截 await page.setRequestInterception(true); page.on('request', (req) => { // 拦截目标下载请求(这里以URL包含/download为例) if (req.url().includes('/download')) { req.respond({ status: 200, headers: { 'Content-Type': 'application/zip', // 匹配文件MIME类型 'Content-Disposition': 'attachment; filename="my-download.zip"' // 触发下载并指定文件名 }, body: base64Body }); } else { req.continue(); } }); await page.goto('https://your-test-site.com'); // 触发页面上的下载操作(比如点击下载按钮) await page.click('#download-btn'); // 等待下载完成(根据实际情况调整时长) await new Promise(resolve => setTimeout(resolve, 3000)); await browser.close(); })();
代码示例(纯CDP场景,使用chrome-remote-interface)
const CDP = require('chrome-remote-interface'); const fs = require('fs'); (async () => { const client = await CDP(); const { Fetch, Page } = client; // 开启Fetch拦截,匹配目标请求URL await Fetch.enable({ patterns: [{ urlPattern: '*download*' }] }); await Page.enable(); const fileBuffer = fs.readFileSync('./sample.pdf'); const base64Body = fileBuffer.toString('base64'); // 处理拦截到的请求 Fetch.requestPaused(async (event) => { await Fetch.fulfillRequest({ requestId: event.requestId, responseCode: 200, // CDP的响应头需用数组形式的键值对 responseHeaders: [ { name: 'Content-Type', value: 'application/pdf' }, { name: 'Content-Disposition', value: 'attachment; filename="document.pdf"' } ], body: base64Body }); }); await Page.navigate({ url: 'https://your-test-site.com' }); await new Promise(resolve => setTimeout(resolve, 3000)); await client.close(); })();
注意事项
- 大文件转base64会增加约33%的体积,若处理超大文件,需评估性能影响;
Content-Type必须与文件格式匹配,否则可能出现下载后无法打开的问题;- 不同CDP封装库的响应头格式可能不同(比如Puppeteer用对象,原生CDP用数组),需注意对应格式。
内容的提问来源于stack exchange,提问作者emo
相关产品推荐
相关产品推荐

