如何使用Puppeteer通过直链下载文件?Buffer序列问题如何解决
Puppeteer 下载图片时分块Buffer聚合的解决方案
问题本质说明
你遇到的写入文件仅保留最后一段Buffer的问题,不需要找特殊的Promise方法处理分块序列,核心原因是实现逻辑的几个疏漏:
page.goto默认的等待策略(load)不匹配纯二进制图片资源的加载时机,浏览器触发load事件时,图片的响应流可能还未完全接收,导致response.buffer()拿到的是截断的不完整数据- 对
response.buffer()的能力有误解:这个方法本身就是Promise封装的自动分块拼接实现,会把所有接收到的响应分块Buffer聚合成一个完整Buffer返回,不存在“只返回最后一段”的设计问题 - 如果是自己监听
data事件时,直接用当前分块覆盖之前的Buffer变量而不是存入数组,就会出现最终只拿到最后一段分块的问题,这属于代码逻辑疏漏,不是Puppeteer的流设计问题
修正后的基础实现
针对直接在浏览器打开图片二进制资源的场景,只需要调整page.goto的等待参数,确保所有响应数据加载完成后再读取Buffer,即可拿到完整的图片内容:
const puppeteer = require('puppeteer-core'); const fs = require('fs').promises; (async () => { const options = { product: 'chrome', headless: true, pipe: true, executablePath: 'chrome.exe' }; const browser = await puppeteer.launch(options); const page = await browser.newPage(); // 调整goto等待策略为networkidle0,确保网络请求完全空闲、响应流全部接收完成 const response = await page.goto( 'https://static.wikia.nocookie.net/naruto/images/d/dd/Naruto_Uzumaki%21%21.png/revision/latest?cb=20161013233552', { waitUntil: 'networkidle0' } ); // buffer()自动拼接全部分块,直接拿到完整的图片Buffer const fullImageBuffer = await response.buffer(); // 注意匹配实际资源类型调整文件后缀,示例中的资源为png格式 await fs.writeFile('file.png', fullImageBuffer); await browser.close(); })();
分块序列手动处理的场景
如果有自定义需求(比如实时展示下载进度、适配特殊截断场景),可以直接手动聚合分块Buffer序列,不需要依赖额外的Promise封装:
- 监听页面的
response事件匹配目标资源 - 把每个接收到的分块推入数组
- 流传输结束后,用
Buffer.concat()方法将所有分块拼接为完整Buffer,再执行写文件
对应实现代码:
// 手动处理分块的逻辑 page.on('response', (res) => { // 匹配目标图片的响应请求 if (res.url().includes('Naruto_Uzumaki')) { const chunkList = []; // 监听流的每一段分块 res.on('data', (chunk) => chunkList.push(chunk)); // 流传输完成后触发聚合写入 res.on('end', async () => { const fullBuffer = Buffer.concat(chunkList); await fs.writeFile('custom-download.png', fullBuffer); }); } }); // 保持网络空闲等待,确保所有分块传输完成 await page.goto('图片地址', { waitUntil: 'networkidle0' });
内容的提问来源于stack exchange,提问作者david stephen
相关产品推荐
相关产品推荐

