通过chrome扩展将file upload为表单数据
我通过Chrome扩展上传文件作为表单数据,我的代码如下所示。 这里的问题是,文件浏览窗口打开了一秒钟,然后消失。
该问题仅在Mac OS中出现。
manifest.json的:
"background": { "scripts": ["jszip.js", "background.js"] },
background.js:
chrome.runtime.onMessage.addListener(function (msg) { if (msg.action === 'browse') { var myForm=document.createElement("FORM"); var myFile=document.createElement("INPUT"); myFile.type="file"; myFile.id="selectFile"; //myFile.onclick="openDialog()"; myForm.appendChild(myFile); var myButton=document.createElement("INPUT"); myButton.name="submit"; myButton.type="submit"; myButton.value="Submit"; myForm.appendChild(myButton); document.body.appendChild(myForm); } });
popup.js:
window.onload = function () { chrome.runtime.sendMessage({ action: 'browse' }); }
一点“背景故事”:
您希望让用户从popup窗口中select并上传文件。 但是在OSX中,一旦文件select器对话框打开,popup窗口就会失去焦点并closures,导致其JS上下文也被破坏。 因此,对话框立即打开和closures。
这在MAC上已经有一段时间了。
解决scheme:
您可以将对话框打开逻辑移动到不受焦点丢失影响的背景页面。 从popup框中,您可以发送消息到后台页面,请求启动浏览和上传过程(请参阅下面的示例代码)。
的manifest.json
{ ... "background": { "persistent": false, "scripts": ["background.js"] }, "browser_action": { "default_title": "Test Extension", // "default_icon": { // "19": "img/icon19.png", // "38": "img/icon38.png" // }, "default_popup": "popup.html" }, "permissions": [ "https://www.example.com/uploads" // The above permission is needed for cross-domain XHR ] }
popup.html
... <script src="popup.js"></script> </head> <body> <input type="button" id="button" value="Browse and Upload" /> ...
popup.js
document.addEventListener('DOMContentLoaded', function () { document.getElementById('button').addEventListener('click', function () { chrome.runtime.sendMessage({ action: 'browseAndUpload' }); window.close(); }); });
background.js
var uploadUrl = 'https://www.example.com/uploads'; /* Creates an `input[type="file]` */ var fileChooser = document.createElement('input'); fileChooser.type = 'file'; fileChooser.addEventListener('change', function () { var file = fileChooser.files[0]; var formData = new FormData(); formData.append(file.name, file); var xhr = new XMLHttpRequest(); xhr.open('POST', uploadUrl, true); xhr.addEventListener('readystatechange', function (evt) { console.log('ReadyState: ' + xhr.readyState, 'Status: ' + xhr.status); }); xhr.send(formData); form.reset(); // <-- Resets the input so we do get a `change` event, // even if the user chooses the same file }); /* Wrap it in a form for resetting */ var form = document.createElement('form'); form.appendChild(fileChooser); /* Listen for messages from popup */ chrome.runtime.onMessage.addListener(function (msg) { if (msg.action === 'browseAndUpload') { fileChooser.click(); } });
小心:
作为安全防范措施, 只有在用户交互的情况下,Chrome才会执行fileChooser.click()
。
在上面的例子中,用户点击popup窗口中的button,该窗口将消息发送到调用fileChooser.click();
的后台页面fileChooser.click();
。 如果您尝试以编程方式调用它,则不起作用。 (例如,在文档加载时调用它将不起作用。)
ExpertSystem的解决scheme并不适用于我,因为它不会让我在后台脚本中调用点击元素,但我想出了一个解决方法,使用他的大部分代码。 如果您没有问题,请稍微修改当前选项卡,将其background.js代码放在内容脚本中,并使用正确的消息传递包装。 大部分的荣誉都归功于ExpertSystem,我只是把这些东西搅乱了。
背景:
我需要解决的问题是,我想要允许JSONfile upload和parsing到我的扩展通过popup。 我想出来的解决方法要求所有这三个部分都是错综复杂的舞蹈; popup窗口,背景和内容脚本。
popup.js
// handler for import button // sends a message to the content script to create the file input element and click it $('#import-button').click(function() { chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { chrome.tabs.sendMessage(tabs[0].id, {message: "chooseFile"}, function(response) { console.log(response.response); }); }); });
content.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { if (request.message == "chooseFile") { /* Creates an `input[type="file]` */ var fileChooser = document.createElement('input'); fileChooser.type = 'file'; fileChooser.addEventListener('change', function () { console.log("file change"); var file = fileChooser.files[0]; var reader = new FileReader(); reader.onload = function(){ var data = reader.result; fields = $.parseJSON(data); // now send the message to the background chrome.runtime.sendMessage({message: "import", fields: fields}, function(response) { console.log(response.response); }); }; reader.readAsText(file); form.reset(); // <-- Resets the input so we do get a `change` event, // even if the user chooses the same file }); /* Wrap it in a form for resetting */ var form = document.createElement('form'); form.appendChild(fileChooser); fileChooser.click(); sendResponse({response: "fileChooser clicked"}); } });
background.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { if (request.message == "import") { fields = request.fields; // use the data sendResponse({response: "imported"}); } });
这个原因,而另一个可能会或可能不会,因为文件input元素是在当前选项卡的范围内创build的,它贯穿整个过程。