Javascript 通过chrome扩展插件将文件作为表单数据上传

Javascript 通过chrome扩展插件将文件作为表单数据上传,javascript,google-chrome,google-chrome-extension,Javascript,Google Chrome,Google Chrome Extension,我正在通过chrome扩展名上传一个文件作为表单数据,我的代码如下。这里的问题是,文件浏览窗口仅打开一秒钟,然后消失。 该问题仅出现在Mac OS中。 manifest.json: "background": { "scripts": ["jszip.js", "background.js"] }, chrome.runtime.onMessage.addListener(function (msg) { if (msg.action === 'browse') { var

我正在通过chrome扩展名上传一个文件作为表单数据,我的代码如下。这里的问题是,文件浏览窗口仅打开一秒钟,然后消失。
该问题仅出现在Mac OS中。

manifest.json:

"background": {
  "scripts": ["jszip.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);
  }
});
window.onload = function () {
  chrome.runtime.sendMessage({
    action: 'browse'
  });
}
background.js:

"background": {
  "scripts": ["jszip.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);
  }
});
window.onload = function () {
  chrome.runtime.sendMessage({
    action: 'browse'
  });
}
popup.js:

"background": {
  "scripts": ["jszip.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);
  }
});
window.onload = function () {
  chrome.runtime.sendMessage({
    action: 'browse'
  });
}
一个小“背景故事”: 您想让用户从弹出窗口中选择并上载文件。但在OSX中,只要文件选择器对话框打开,弹出窗口就会失去焦点并关闭,导致其JS上下文也被破坏。因此,对话框会立即打开和关闭

这是一个在MAC上运行了相当长一段时间的


解决方案: 您可以将对话框打开逻辑移动到后台页面,该页面不受焦点丢失的影响。从弹出窗口,您可以向后台页面发送一条消息,请求启动浏览和上载过程(请参阅下面的示例代码)

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" />
    ...
background.js

document.addEventListener('DOMContentLoaded', function () {
    document.getElementById('button').addEventListener('click', function () {
        chrome.runtime.sendMessage({ action: 'browseAndUpload' });
        window.close();
    });
});
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();
    }
});
// 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);
        });
    });
});
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"});
    }

});
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    if (request.message == "import") {
        fields = request.fields; // use the data
        sendResponse({response: "imported"});
    }
});
var uploadUrl='1〕https://www.example.com/uploads';
/*创建一个'input[type=“file]”*/
var fileChooser=document.createElement('input');
fileChooser.type='file';
fileChooser.addEventListener('change',函数(){
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',函数(evt){
console.log('ReadyState:'+xhr.ReadyState,
“状态:”+xhr.Status);
});
xhr.send(formData);

表单.重置()/ExpertSystem的解决方案不适合我,因为它不允许我在后台脚本中调用click元素,但我用他的大部分代码想出了一个解决方法。如果您对当前选项卡没有轻微污染的问题,请将他的background.js代码放入带有适当消息传递包装器的内容脚本中。大多数cr编辑转到ExpertSystem,我只是把东西混在一起

背景:

我需要解决的问题是,我想允许一个JSON文件通过弹出窗口上传并解析到我的扩展名中。为此,我提出的解决方案需要所有三个部分的复杂舞蹈:弹出窗口、背景和内容脚本

popup.js

document.addEventListener('DOMContentLoaded', function () {
    document.getElementById('button').addEventListener('click', function () {
        chrome.runtime.sendMessage({ action: 'browseAndUpload' });
        window.close();
    });
});
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();
    }
});
// 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);
        });
    });
});
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"});
    }

});
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    if (request.message == "import") {
        fields = request.fields; // use the data
        sendResponse({response: "imported"});
    }
});
content.js

document.addEventListener('DOMContentLoaded', function () {
    document.getElementById('button').addEventListener('click', function () {
        chrome.runtime.sendMessage({ action: 'browseAndUpload' });
        window.close();
    });
});
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();
    }
});
// 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);
        });
    });
});
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"});
    }

});
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    if (request.message == "import") {
        fields = request.fields; // use the data
        sendResponse({response: "imported"});
    }
});

之所以这样做有效,而另一个可能有效或无效,是因为文件输入元素是在当前选项卡的范围内创建的,在整个过程中始终有效。

感谢您的回复,我正在努力集成您的代码。我会让您知道更新我在我的项目中集成了您的代码。单击“上”“浏览并上传”按钮提示两个窗口(一个窗口固定有扩展图标(插件),另一个窗口是普通窗口)选择文件。普通窗口在窗口上方,该窗口固定有扩展图标(插件)。但是当我点击“关闭”或“打开”时“正常窗口中的按钮。两个窗口都关闭,这肯定不是由我的代码引起的:)如果我看不到代码,我就说不出有什么问题。好的。如果你想在某个地方发布代码,如果你愿意,我可以看一看。嗨!我在Windows 7(chrome版本37)下尝试了这个解决方案,但没有触发.click事件。”。(与@ExpertSystem注释相同)。我还尝试将文件加载逻辑直接放在弹出窗口(window.html/js)中,但只要我单击按钮,弹出窗口就会关闭。如果我保持“开发人员工具”窗口打开(右键单击插件图标->检查弹出窗口),弹出窗口才会保持不动(并正确执行文件加载)。有什么想法/解决方案吗?您好,这是一个很棒的建议,但是有没有办法防止弹出窗口自动关闭?用户选择文件或关闭文件选择器窗口后,弹出窗口会失去焦点并关闭。@tinyCoder弹出窗口无法保持打开状态。这只是扩展名的限制。我的经理将我设置为t要求将其保持打开状态一周。我甚至寻求支持。这是它的设计方式。这是不可能的。唯一的例外是,如果打开devtools(右键单击inspect),它将保持打开状态,但这不是一个实际的解决方法。