javascript 通过 chrome 扩展上传文件作为表单数据

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21132971/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 20:09:41  来源:igfitidea点击:

Upload File as a Form Data through chrome extension

javascriptgoogle-chromegoogle-chrome-extension

提问by Arvind Anandala

I am uploading a file through chrome extension as a form data and my code follows below. The problem here is that the file browsing window opens for just a second and then disappears.
The issue appears in Mac OS only.

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

manifest.json:

清单.json:

"background": {
  "scripts": ["jszip.js", "background.js"]
},

background.js:

背景.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:

popup.js:

window.onload = function () {
  chrome.runtime.sendMessage({
    action: 'browse'
  });
}

采纳答案by gkalpak

A little "background story":

一个小小的“背景故事”:

You want to let the user choose and upload a file from your popup. But in OSX, as soon as the file-chooser dialog opens, the popup loses focus and closes, causing its JS context to get destroyed as well. Thus, the dialog opens and closes immediately.

您想让用户从弹出窗口中选择并上传文件。但是在 OSX 中,只要文件选择器对话框打开,弹出窗口就会失去焦点并关闭,从而导致其 JS 上下文也被破坏。因此,对话框会立即打开和关闭。

This is a known bugon MAC for quite some time.

这是MAC 上的一个已知错误已经有一段时间了。



The solution:

解决方案:

You can move the dialog opening logic to the background-page, which is not affected by loss of focus. From the popup, you can send a message to the background-page, requesting to initiate the browse-and-upload process (see sample code below).

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

manifest.json

清单文件.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

弹出窗口.html

    ...
    <script src="popup.js"></script>
</head>
<body>
    <input type="button" id="button" value="Browse and Upload" />
    ...

popup.js

弹出窗口.js

document.addEventListener('DOMContentLoaded', function () {
    document.getElementById('button').addEventListener('click', function () {
        chrome.runtime.sendMessage({ action: 'browseAndUpload' });
        window.close();
    });
});

background.js

背景.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();
    }
});


Heads up:
As a security precaution, Chrome will execute fileChooser.click()onlyif it is a result of user interaction.
In the above example, the user clicks the button in the popup, which sends a message to the background-page, which calls fileChooser.click();. If you try to call it programmatically it won't work. (E.g. calling it on document load won't have any effect.)


注意作为安全预防措施,ChromefileChooser.click()在用户交互的结果下才会执行。
在上面的例子中,用户点击弹出窗口中的按钮,它向后台页面发送一条消息,调用fileChooser.click();. 如果您尝试以编程方式调用它,它将不起作用。(例如,在文档加载时调用它不会产生任何影响。)

回答by RayfenWindspear

ExpertSystem's solution didn't work for me as it would not let me call click on the element in the background script, but I came up with a workaround using much of his code. If you don't have an issue slightlytainting the current tab, put his background.js code in a content script with the proper message passing wrappers. Most of the credit goes to ExpertSystem, I merely shuffled things around.

ExpertSystem 的解决方案对我不起作用,因为它不允许我调用后台脚本中的元素单击,但我想出了一个使用他的大部分代码的解决方法。如果您没有稍微污染当前选项卡的问题,请将他的 background.js 代码放在具有适当消息传递包装器的内容脚本中。大部分功劳归功于 ExpertSystem,我只是把事情洗了一遍。

Background:

背景:

The problem I needed to solve was that I wanted to allow a JSON file to be uploaded and parsed into my extension via the popup. The workaround I came up with for doing so required an intricate dance of all three pieces; the popup, background, and content scripts.

我需要解决的问题是我希望允许通过弹出窗口上传 JSON 文件并解析到我的扩展程序中。我为此提出的解决方法需要所有三部曲的复杂舞蹈;弹出窗口、背景和内容脚本。

popup.js

弹出窗口.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

内容.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

背景.js

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

The reason this works while the other may or may not, is because the file input element is created within the scope of the current tab, which persists throughout the entire process.

这样做的原因可能会也可能不会,因为文件输入元素是在当前选项卡的范围内创建的,该选项卡在整个过程中持续存在。