Playwright Chromium/Webkit无法下载文件仅Firefox正常问题
根因分析
核心触发前提:目标资源地址的响应头未设置Content-Disposition: attachment,没有明确告知浏览器需要将响应作为附件下载,不同浏览器内核对这类资源的默认处理逻辑存在差异,不属于Playwright 1.22版本的bug。
各内核异常的具体原因:
- Chromium 报错原因:1.22版本绑定的Chromium内核,在直接导航到无附件标记的二进制压缩资源(.gz格式)时,不会触发原生下载流程,会尝试将资源作为网页内容加载解析,二进制格式无法被页面渲染引擎正常处理,直接导致导航流程中断抛出错误,全程不会触发Playwright监听的下载事件,因此
waitForDownload逻辑直接失败。 - Webkit 下载失败原因:Webkit内核对无附件标记的xml、gz类资源的默认处理策略是直接在当前页面渲染内容(文本类资源直接展示文本,二进制类资源展示乱码),完全不会触发保存/下载流程,因此
waitForDownload会持续等待直到触发超时。 - Firefox 运行正常原因:Firefox对
.xml.gz格式的sitemap压缩包默认配置为保存到本地,即使服务端没有返回附件标记,也会触发原生下载事件,因此现有逻辑可以正常跑通。
1.22版本兼容全内核的实现方案
不要使用直接导航+等待下载事件的逻辑,这类逻辑天生依赖浏览器自身的资源处理策略,兼容性差。推荐用路由拦截的方式实现,绕开内核的行为差异,三个内核下都能稳定运行:
import com.microsoft.playwright.*; import java.nio.file.Files; import java.nio.file.Paths; public class PwDownload { public static void main(String[] args) { try (Playwright playwright = Playwright.create()) { // 切换为webkit、firefox内核均可正常运行 final Browser browser = playwright.chromium().launch( new BrowserType.LaunchOptions().setHeadless(false) ); Page page = browser.newPage(); // 拦截目标资源请求 page.route("**/sitemap-top-post.xml.gz", route -> { // 透传请求拿到原始响应 APIResponse response = route.fetch(); // 直接将响应字节写入本地文件 byte[] fileContent = response.body(); Files.write(Paths.get("./sitemap-top-post.xml.gz"), fileContent); // 正常返回响应给浏览器,避免页面报错 route.fulfill(new Route.FulfillOptions() .setStatus(response.status()) .setHeaders(response.headers()) .setBody(fileContent) ); }); // 直接导航即可,不需要额外等待下载事件 page.navigate("https://www.fnac.es/sitemap-top-post.xml.gz"); browser.close(); } catch (Exception e) { e.printStackTrace(); } } }
该方案不需要依赖浏览器的下载触发逻辑,不管内核选择渲染资源还是中断导航,都能直接拿到完整的原始响应字节保存,不存在超时、兼容问题。
如果一定要使用Playwright原生的Download对象操作,可以通过在页面注入带download属性的隐藏a标签,主动触发强制下载,但该方案在1.22版本的Webkit下存在偶发兼容问题,稳定性不如路由拦截。
相关说明
- 该问题是浏览器内核默认行为差异导致,高版本Playwright也没有对这部分逻辑做特殊兼容,因为内核本身的资源处理策略不属于自动化工具需要强行统一的范畴。
- 目前社区内大量开发者都是通过路由拦截的方式实现Playwright文件下载,尤其是针对pdf、图片、压缩包、文本文件这类浏览器默认会预览/直接渲染的资源,路由拦截是兼容性、稳定性最高的实现方案。
内容的提问来源于stack exchange,提问作者MeT
相关产品推荐
相关产品推荐

