能否为JavaScript的FileList写原型构造File对象?适配路由器固件更新自动化
解决路由器固件更新自动化中的FileList构造问题
嘿,这个场景我刚好折腾过,咱们直接说结论:完全可以通过构造符合标准的FileList对象(甚至扩展它的原型)来绕过这个JS处理的限制,结合Selenium的JS注入能力就能搞定。下面一步步给你讲具体怎么做:
核心思路
路由器的onsubmit JS会依赖FileList来获取上传的固件文件,而原生FileList是只读的,不能直接修改。但我们可以通过Selenium的execute_script()方法注入自定义JS,手动构造包含目标File对象的FileList,再替换到文件输入框的files属性上,这样JS处理逻辑就会认为是正常上传的文件了。
具体实现步骤
1. 准备固件文件并传入JS
先把本地的固件文件读成二进制数据,然后通过Selenium的execute_script()参数传递给JS代码:
from selenium import webdriver # 初始化PhantomJS(如果兼容性有问题,建议换成Headless Chrome) driver = webdriver.PhantomJS() driver.get("你的路由器固件更新页面URL") # 读取本地固件文件 with open("firmware.bin", "rb") as f: firmware_bytes = f.read()
2. 注入JS构造FileList并替换
这一步是关键,我们要在浏览器环境里创建Blob、File对象,再构造一个符合FileList接口的对象:
# 执行自定义JS driver.execute_script(""" // 把Python传入的二进制数据转成Blob(模拟文件内容) const firmwareBlob = new Blob([arguments[0]], { type: 'application/octet-stream' }); // 创建File对象,文件名和MIME类型要和路由器表单要求一致! const firmwareFile = new File([firmwareBlob], 'firmware.bin', { type: 'application/octet-stream' }); // 构造FileList对象:因为原生FileList不能直接修改,我们基于它的原型创建 const customFileList = Object.create(FileList.prototype); // 添加File对象到索引0的位置 Object.defineProperty(customFileList, '0', { value: firmwareFile }); // 设置FileList的长度为1 Object.defineProperty(customFileList, 'length', { value: 1 }); // 找到页面上的文件输入框,替换它的files属性 const fileInput = document.querySelector('input[type="file"]'); Object.defineProperty(fileInput, 'files', { value: customFileList }); """, firmware_bytes)
3. 触发表单提交并通过校验
现在文件输入框的files已经是我们构造的FileList了,直接触发提交操作即可:
# 点击提交按钮(或者直接调用表单的submit()方法) submit_btn = driver.find_element_by_css_selector('button[type="submit"]') submit_btn.click()
关键注意事项
- PhantomJS兼容性问题:PhantomJS已经停止维护,对ES6的支持有限,如果遇到
File构造函数报错,可以换成Blob代替(有些旧JS逻辑会兼容Blob),或者直接换成Headless Chrome,稳定性和兼容性会好很多。 - 表单校验匹配:路由器的JS校验会检查文件名、文件类型甚至文件大小,你要确保构造的File对象的文件名、MIME类型和正常手动上传的完全一致,不然会触发校验失败。
- onsubmit事件触发:尽量模拟用户点击提交按钮,而不是直接调用
form.submit(),这样能确保onsubmit事件被正常触发,JS处理逻辑能完整执行。
关于FileList原型的补充
你问的“给FileList对象类型编写原型”,其实上面的代码已经用到了:Object.create(FileList.prototype)就是让我们构造的对象继承FileList的原型方法,这样它的行为和原生FileList完全一致,路由器的JS不会察觉到异常。如果需要扩展FileList的方法(比如添加自定义的遍历方法),也可以直接修改FileList.prototype,不过在这个场景里完全没必要,构造符合接口的对象就足够了。
内容的提问来源于stack exchange,提问作者swartkatt
相关产品推荐
相关产品推荐

