原Chrome扩展上传脚本在Angular页面失效,如何触发文件上传?
问题描述
我之前一直靠自己开发的Chrome扩展帮着往Blackboard上传文件:把文件传到扩展弹窗里,读好内容和文件名后,注入下面这段代码就行:
const file = new File([contents],filename,{"type":'text/csv'}), dt = new DataTransfer(); dt.items.add(file); document.querySelector('input[type="file"]').files = dt.files; setTimeout(function() { document.querySelector('input[type="submit"]').click(); },1000);
结果现在网站换成Angular框架了,这段脚本直接失效。新页面的相关HTML代码是这样的:
<div class="file-upload-container"> <div ngf-drop="" ng-model="files" class="file-upload ng-pristine ng-valid ng-empty ng-touched" ngf-multiple="fileUpload.allowMultiple" ngf-drop-disabled="disableDrag && disableDrag()" ngf-change="fileUpload.onFileSelect($files, $file, $newFiles, $duplicateFiles, $invalidFiles, $event)" ngf-stop-propagation="!(disableDrag && disableDrag())" ngf-drag-over-class="dragClass" tabindex="-1"> <div class="fileUploadTransclude" ng-transclude="" ng-show="fileUpload.transcludeVisible"> <div class="columns drag-area ng-scope" ng-class="{'dropped': gradesUploadSettings.hasDroppedClass}"> <div class="file-icon"></div> <div class="drag-info"> <p bb-translate="" class="ng-scope">Drop your spreadsheet to upload</p> <p bb-translate="" class="ng-scope">You can upload a CSV or XLS file</p> </div> </div> </div> <div class="js-file-upload-complete" ng-attr-ready="{{!fileUpload.uploadInProgress}}" ready="true"></div> <div ng-show="fileUpload.fileReviewVisible" class="file-upload-review ng-hide"></div> </div> <input ngf-select="" ng-if="!fileUpload.allowMultiple" type="file" ngf-change="fileUpload.onFileSelect($files)" class="hide fileUploadInput ng-scope" analytics-id="components.directives.fileUpload.input.file"><!-- end ngIf: !fileUpload.allowMultiple --> </div>
我没法改网站本身的HTML或JS代码,只能通过Chrome扩展注入任意JS。之前找的相关问题都不适用:
- 一个是5年前的,前提是能改网站JS代码
- 另一个是6年前针对AngularJS的,要求能访问
angular.element(elem).scope()
请问怎么在这个新页面上触发文件上传?
解决方案
针对这种用ngf-select/ngf-drop(ng-file-upload指令)的Angular文件上传组件,可以按以下步骤模拟上传:
1. 构造文件对象并包装
和之前逻辑类似,先把内容转换成目标File对象,再用DataTransfer包装:
const file = new File([contents], filename, { type: 'text/csv' }); const dt = new DataTransfer(); dt.items.add(file);
2. 定位文件输入框并设置文件
找到页面里隐藏的那个文件输入元素:
const fileInput = document.querySelector('input.fileUploadInput[type="file"]'); if (!fileInput) { console.error('找不到文件上传输入框'); return; } // 给输入框设置文件 fileInput.files = dt.files;
3. 触发事件并通知Angular更新
Angular不会自动检测到files属性的变化,所以要手动触发绑定的ngf-change事件,还要强制触发变更检测:
// 模拟change事件并触发 const changeEvent = new Event('change', { bubbles: true }); fileInput.dispatchEvent(changeEvent); // 触发Angular变更检测的函数 function triggerAngularChangeDetection(element) { if (!window.ng) return; // 优先用NgZone触发,这是Angular推荐的方式 const ngZone = window.ng.getZone(); if (ngZone) { ngZone.run(() => {}); return; } // 兼容老版本Angular,尝试获取变更检测器 try { const injector = window.ng.getInjector(element); const cdr = injector.get(window.ng.ChangeDetectorRef); cdr.detectChanges(); } catch (e) { console.warn('变更检测触发失败,尝试兜底方案'); // 极端情况:触发input事件强制刷新 const inputEvent = new Event('input', { bubbles: true }); fileInput.dispatchEvent(inputEvent); } } triggerAngularChangeDetection(fileInput);
4. 自动触发提交(可选)
等Angular处理完文件选择逻辑后,找到提交按钮触发点击:
setTimeout(() => { const submitBtn = document.querySelector('input[type="submit"]'); if (submitBtn) submitBtn.click(); }, 1200); // 延迟时间可根据页面实际加载速度调整
完整注入代码示例
function uploadFileToBlackboard(contents, filename) { // 构造文件 const file = new File([contents], filename, { type: 'text/csv' }); const dt = new DataTransfer(); dt.items.add(file); // 获取输入框 const fileInput = document.querySelector('input.fileUploadInput[type="file"]'); if (!fileInput) { console.error('找不到文件上传输入元素'); return; } // 设置文件并触发事件 fileInput.files = dt.files; const changeEvent = new Event('change', { bubbles: true }); fileInput.dispatchEvent(changeEvent); // 触发Angular变更检测 function triggerAngularCD(element) { if (!window.ng) return; const ngZone = window.ng.getZone(); if (ngZone) { ngZone.run(() => {}); return; } try { const injector = window.ng.getInjector(element); const cdr = injector.get(window.ng.ChangeDetectorRef); cdr.detectChanges(); } catch (e) { console.warn('变更检测触发失败,尝试兜底'); const inputEvent = new Event('input', { bubbles: true }); fileInput.dispatchEvent(inputEvent); } } triggerAngularCD(fileInput); // 自动提交 setTimeout(() => { const submitBtn = document.querySelector('input[type="submit"]'); if (submitBtn) submitBtn.click(); }, 1200); } // 调用示例(替换成你的内容和文件名) // uploadFileToBlackboard('你的CSV内容', 'grades.csv');
注意事项
- 必须把脚本注入到页面上下文中,Chrome扩展可以用
chrome.scripting.executeScript时设置world: 'MAIN'实现。 - 先在页面控制台确认
window.ng是否存在,部分Angular项目可能会隐藏这个全局对象,需要调整获取方式。 - 延迟时间可能需要根据页面加载情况微调,确保Angular有足够时间处理文件选择逻辑。
内容的提问来源于stack exchange,提问作者Teepeemm
相关产品推荐
相关产品推荐

