一些背景信息
我已经花了几天时间开发一个Chrome扩展程序,可以每天多次对给定的网页进行截屏。我使用这个作为指南,一切都按照预期运行。
然而,扩展程序有一个小要求无法满足。用户必须能够访问保存图像(截屏)的文件夹,但Chrome扩展程序无法访问文件系统。相反,Chrome应用程序可以。因此,经过多方查找,我得出结论必须创建Chrome扩展程序和Chrome应用程序两者。我的想法是:扩展程序将创建截屏的blob,然后将该blob发送到应用程序,应用程序将其保存为用户指定位置的图像。这正是我正在做的——在扩展程序上创建截屏的blob,然后将其发送到应用程序,在那里用户被要求选择保存图像的位置。
问题
在保存部分之前,一切都按照预期工作。blob在扩展程序上创建,发送到应用程序,应用程序接收到,用户被要求选择保存位置,图像被保存……这就是问题所在。 生成的图像无法使用。 当我尝试打开它时,会出现一个消息,上面写着“无法确定类型”。下面是我正在使用的代码:
First ON THE EXTENSION side, I create a blob and send it over, like this:
chrome.runtime.sendMessage( APP_ID, /* I got this from the app */ {myMessage: blob}, /* Blob created previously; it's correct */ function(response) { appendLog("response: "+JSON.stringify(response)); } );
Then, ON THE APP side, I receive the blob and attempt to save it like this:
// listen for external messages chrome.runtime.onMessageExternal.addListener( function(request, sender, sendResponse) { if (sender.id in blacklistedIds) { sendResponse({"result":"sorry, could not process your message"}); return; // don't allow this extension access } else if (request.incomingBlob) { appendLog("from "+sender.id+": " + request.incomingBlob); // attempt to save blob to choosen location if (_folderEntry == null) { // get a directory to save in if not yet chosen openDirectory(); } saveBlobToFile(request.incomingBlob, "screenshot.png"); /* // inspect object to try to see what's wrong var keys = Object.keys(request.incomingBlob); var keyString = ""; for (var key in keys) { keyString += " " + key; } appendLog("Blob object keys:" + keyString); */ sendResponse({"result":"Ok, got your message"}); } else { sendResponse({"result":"Ops, I don't understand this message"}); } } );
Here's the function ON THE APP that performs the actual save:
function saveBlobToFile(blob, fileName) { appendLog('entering saveBlobToFile function...'); chrome.fileSystem.getWritableEntry(_folderEntry, function(entry) { entry.getFile(fileName, {create: true}, function(entry) { entry.createWriter(function(writer) { //writer.onwrite = function() { // writer.onwrite = null; // writer.truncate(writer.position); //}; appendLog('calling writer.write...'); writer.write(blob); // Also tried writer.write(new Blob([blob], {type: 'image/png'})); }); }); }); }
更新(9/23/14): 抱歉更新晚了,因为我被分配到了一个不同的项目上,所以直到两天前才回来。
经过长时间的搜索,我决定采用@Danniel Herr的建议,他建议使用SharedWorker和嵌入到app中的框架中的页面。这个想法是扩展会将blob提供给SharedWorker,然后SharedWorker将blob转发到嵌入在app中的框架中的扩展中的页面。然后,该页面使用parent.postMessage(...)将blob转发到app。虽然有点繁琐,但似乎这是我唯一的选择。
让我放一些代码,这样就更容易理解了:
扩展:
var worker = new SharedWorker(chrome.runtime.getURL('shared-worker.js'));
worker.port.start();
worker.postMessage('hello from extension'); // Can send blob here too
worker.port.addEventListener("message", function(event) {
$('h1Title').innerHTML = event.data;
});
proxy.js
var worker = new SharedWorker(chrome.runtime.getURL('shared-worker.js'));
worker.port.start();
worker.port.addEventListener("message",
function(event) {
parent.postMessage(event.data, 'chrome-extension://[extension id]');
}
);
proxy.html
<script src='proxy.js'></script>
shared-worker.js
var ports = [];
var count = 0;
onconnect = function(event) {
count++;
var port = event.ports[0];
ports.push(port);
port.start();
/*
On both the extension and the app, I get count = 1 and ports.length = 1
I'm running them side by side. This is so maddening!!!
What am I missing?
*/
var msg = 'Hi, you are connection #' + count + ". ";
msg += " There are " + ports.length + " ports open so far."
port.postMessage(msg);
port.addEventListener("message",
function(event) {
for (var i = 0; i < ports.length; ++i) {
//if (ports[i] != port) {
ports[i].postMessage(event.data);
//}
}
});
};
在应用程序中
context.addEventListener("message",
function(event) {
appendLog("message from proxy: " + event.data);
}
);
所以这是执行流程...在扩展中,我创建了一个共享工作者并向其发送了一条消息。共享工作者应该能够接收blob,但出于测试目的,我只发送了一个简单的字符串。
接下来,共享工作者接收到消息并将其转发给所有已连接的人。此时,在应用程序中的框架中的proxy.html/js确实连接,并且应该接收由共享工作者转发的任何内容。
接下来,proxy.js [应]从共享工作者接收到消息,并使用parent.postMessage(...)将其发送到应用程序。应用程序通过window.addEventListener("message",...)监听。
为了测试这个流程,我首先打开了应用程序,然后点击了扩展按钮。我没有在应用程序上收到任何消息。我也没有收到任何错误。
扩展可以与共享工作者往返通信。应用程序可以与共享工作者往返通信。然而,我从扩展->proxy->app发送的消息未到达应用程序。我错过了什么?
对不起,帖子很长,但我希望有人能给我一些提示,因为这让我发疯了。
谢谢