小背景
我已经在Chrome扩展程序上工作了几天,每天多次截取给定网页的屏幕截图。我使用this作为指导,事情按预期工作。
但是,有一个小的要求扩展无法满足。用户必须能够访问保存图像(屏幕截图)的文件夹,但Chrome Extensions don't have access to the file system。另一方面,Chrome应用程序可以。因此,经过多次调查,我得出结论,我必须创建Chrome扩展程序和Chrome应用程序。我们的想法是扩展会创建一个屏幕截图,然后将该blob发送到应用程序,然后将其作为图像保存到用户指定的位置。而这正是我正在做的事情 - 我在扩展端创建一个screentshot的blob然后将其发送到应用程序,在该应用程序中要求用户选择保存图像的位置
问题
直到保存部分,一切都按预期工作。 blob在扩展程序上创建,发送到应用程序,由应用程序接收,用户被问到保存位置,图像被保存....这就是事情崩溃的地方。 生成的图片无法使用。当我尝试打开它时,收到一条消息,上面写着"无法确定类型"。以下是我使用的代码:
首先在EXTENSION方面,我创建一个blob并将其发送出去,如下所示:
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));
}
);
然后,在APP方面,我收到blob并试图像这样保存它:
// 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"});
}
}
);
这是执行实际保存的APP上的功能:
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'}));
});
});
});
}
没有错误。没有打嗝。代码可以工作,但图像没用。我到底错过了什么?我哪里错了?我们只能在扩展/应用之间传递字符串吗?斑点在路上被破坏了吗?我的应用程序是否无法访问blob,因为它是在扩展程序上创建的?谁能请一些亮点?
更新(2014年9月23日) 对于最新的更新很抱歉,但我被分配到了另一个项目,直到2天前才回复。
因此,经过多次调查,我决定采用@Danniel Herr的建议,建议使用SharedWorker和嵌入在应用程序框架中的页面。我们的想法是扩展程序将blob提供给SharedWorker,后者将blob转发到嵌入应用程序框架中的扩展中的页面。该页面然后使用parent.postMessage(...)将blob转发到应用程序。它有点麻烦,但它似乎是我唯一的选择。
让我发布一些代码,以便更有意义:
扩展程序:
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>
共享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,但出于测试目的,我只发送一个简单的字符串。
接下来,共享工作人员收到该消息并将其转发给已连接的所有人。 app中框架内的proxy.html / js确实已经连接,并且应该接收共享工作者转发的任何内容。
接下来,proxy.js [应该]从共享工作者接收消息并使用parent.postMessage(...)将其发送到应用程序。该应用程序正在通过window.addEventListener(&#34; message&#34;,...)进行侦听。
要测试此流程,我首先打开应用程序,然后单击扩展按钮。我在应用程序上没有收到任何消息。我也没有错。
扩展程序可以与共享工作者来回通信。该应用程序可以很好地与共享工作人员进行通信。但是,我从扩展程序&gt; proxy-&gt;应用程序发送的消息未到达该应用程序。我错过了什么?
对于那些长篇大论的人来说很抱歉,但是我希望有人能说清楚,因为这让我感到疯狂。
由于
答案 0 :(得分:8)
感谢您的帮助。我发现解决方案是将blob转换为扩展名上的二进制字符串,然后使用chrome&#39; s消息传递API将字符串发送到应用程序。在应用程序上,我接着做了Francois建议将二进制字符串转换回blob的内容。之前我曾尝试过这个解决方案但是我没有工作,因为我在应用程序上使用了以下代码:
blob = new Blob([blobAsBinString], {type: mimeType});
该代码可能适用于文本文件或简单字符串,但它不适用于图像(可能是由于字符编码问题)。这就是我疯狂的地方。解决方案是使用Francois从一开始就提供的内容:
var bytes = new Uint8Array(blobAsBinString.length);
for (var i=0; i<bytes.length; i++) {
bytes[i] = blobAsBinString.charCodeAt(i);
}
blob = new Blob([bytes], {type: mimeString});
该代码重新训练二进制字符串的完整性,并在应用程序上正确地重新创建blob。
现在我还在这里收集了一些我发现你和其他人建议的东西,这是将blob拆分成块并将其发送过来,以防blob太大。整个解决方案如下:
关于扩展:
function sendBlobToApp() {
// read the blob in chunks/chunks and send it to the app
// Note: I crashed the app using 1 KB chunks. 1 MB chunks work just fine.
// I decided to use 256 KB as that seems neither too big nor too small
var CHUNK_SIZE = 256 * 1024;
var start = 0;
var stop = CHUNK_SIZE;
var remainder = blob.size % CHUNK_SIZE;
var chunks = Math.floor(blob.size / CHUNK_SIZE);
var chunkIndex = 0;
if (remainder != 0) chunks = chunks + 1;
var fr = new FileReader();
fr.onload = function() {
var message = {
blobAsText: fr.result,
mimeString: mimeString,
chunks: chunks
};
// APP_ID was obtained elsewhere
chrome.runtime.sendMessage(APP_ID, message, function(result) {
if (chrome.runtime.lastError) {
// Handle error, e.g. app not installed
// appendLog is defined elsewhere
appendLog("could not send message to app");
}
});
// read the next chunk of bytes
processChunk();
};
fr.onerror = function() { appendLog("An error ocurred while reading file"); };
processChunk();
function processChunk() {
chunkIndex++;
// exit if there are no more chunks
if (chunkIndex > chunks) {
return;
}
if (chunkIndex == chunks && remainder != 0) {
stop = start + remainder;
}
var blobChunk = blob.slice(start, stop);
// prepare for next chunk
start = stop;
stop = stop + CHUNK_SIZE;
// convert chunk as binary string
fr.readAsBinaryString(blobChunk);
}
}
关于APP
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (sender.id in blacklistedIds) {
return; // don't allow this extension access
} else if (request.blobAsText) {
//new chunk received
_chunkIndex++;
var bytes = new Uint8Array(request.blobAsText.length);
for (var i=0; i<bytes.length; i++) {
bytes[i] = request.blobAsText.charCodeAt(i);
}
// store blob
_blobs[_chunkIndex-1] = new Blob([bytes], {type: request.mimeString});
if (_chunkIndex == request.chunks) {
// merge all blob chunks
for (j=0; j<_blobs.length; j++) {
var mergedBlob;
if (j>0) {
// append blob
mergedBlob = new Blob([mergedBlob, _blobs[j]], {type: request.mimeString});
}
else {
mergedBlob = new Blob([_blobs[j]], {type: request.mimeString});
}
}
saveBlobToFile(mergedBlob, "myImage.png", request.mimeString);
}
}
}
);
答案 1 :(得分:4)
我的应用无法访问blob,因为它是在 延期?谁能请一些亮点?
完全!您可能希望传递dataUrl
而不是blob
。下面这样的东西可以起作用:
/* Chrome Extension */
var blobToDataURL = function(blob, cb) {
var reader = new FileReader();
reader.onload = function() {
var dataUrl = reader.result;
var base64 = dataUrl.split(',')[1];
cb(base64);
};
reader.readAsDataURL(blob);
};
blobToDataUrl(blob, function(dataUrl) {
chrome.runtime.sendMessage(APP_ID, {databUrl: dataUrl}, function() {});
});
/* Chrome App */
function dataURLtoBlob(dataURL) {
var byteString = atob(dataURL.split(',')[1]),
mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0];
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
var blob = new Blob([ia], {type: mimeString});
return blob;
}
chrome.runtime.onMessageExternal.addListener(
function(request) {
var blob = dataURLtoBlob(request.dataUrl);
saveBlobToFile(blob, "screenshot.png");
});
答案 2 :(得分:3)
我对这个问题非常感兴趣,因为我正在努力完成类似的事情。
这些是我发现的相关问题:
根据Rob W的说法,在第一个链接中:
&#34; Chrome的fileSystem(app)API可以直接写入用户指定的用户文件系统(例如〜/ Documents或%USERPROFILE%\ Documents)。&#34;
如果您可以写信给用户的文件系统,您应该能够正确地阅读它吗?
我没有机会尝试这一点,但是您可以使用chrome扩展程序下载API将项目保存到您的下载中,而不是直接将文件blob传递给应用程序。
然后您可以使用chrome app filesystem api检索它以获取对它的访问权限。
编辑:
我一直在读,api可以访问的文件系统是沙箱。所以我不知道这个解决方案是否可行。它是沙箱和Rob W的描述&#34;直接写入用户的文件系统&#34;听起来像对我一样。
编辑:
Rob W在这里修改了答案:Implement cross extension message passing in chrome extension and app。
它不再使用共享工作器,并将文件数据作为字符串传递给后端,后端可以将字符串转换回blob。
我不确定消息的最大长度是多少,但是Rob W还提到了一种解决方法,即将blob切片以将其分段发送。
编辑:
我发送了43 mbs的数据而没有崩溃我的应用。
答案 3 :(得分:2)
这真是一个有趣的问题。从我的观点来看,可以使用这些技术来完成: