The Chrome messaging APIs can only transfer JSON-serializable values. If the files are small, then you could just read the file content using FileReader
in the extension, send the message over the external messaging channel to the Chrome App, then save the data using the FileWriter
API.
When the files are big, read the file in chunks using file.slice(start, end)
then follow the same method as for small files.
Extension:
var app_id = '.... ID of app (32 lowercase a-p characters) ....';
var file = ...; // File or Blob object, e.g. from an <input type=file>
var fr = new FileReader();
fr.onload = function() {
var message = {
blob: fr.result,
filename: file.name,
filetype: file.type
};
chrome.runtime.sendMessage(app_id, message, function(result) {
if (chrome.runtime.lastError) {
// Handle error, e.g. app not installed
console.warn('Error: ' + chrome.runtime.lastError.message);
} else {
// Handle success
console.log('Reply from app: ', result);
}
});
};
fr.onerror = function() { /* handle error */ };
// file or sliced file.
fr.readAsText(file);
App:
chrome.runtime.onMessageExternal.addListener(
function(message, sender, sendResponse) {
// TODO: Validate that sender.id is allowed to invoke the app!
// Do something, e.g. convert back to Blob and do whatever you want.
var blob = new Blob([message.blob], {type: message.filetype});
console.log('TODO: Do something with ' + message.filename + ':', blob);
// Do something, e.g. reply to message
sendResponse('Processed file');
// if you want to send a reply asynchronously, uncomment the next line.
// return true;
});
EDIT: Although the following method using sounded nice in theory, it does not work in practice because a separate SharedWorker process is created for the app / extension.
If you want to send huge files (e.g. File
s), then you could implement the following:
- Extension: Create
proxy.html
(content = <script src=proxy.js></script>
). (feel free to pick any other name).
- Extension: Put
proxy.html
in web_accessible_resources
.
- App: Bind a
window.onmessage
event listener. This event listener will receive messages from the frame you're going to load in the next step.
- App: Load
chrome-extension://[EXTENSIONID]/proxy.html
in a frame within your app. This extension ID can either be hard-coded (see Obtaining Chrome Extension ID for development) or exchanged via the external extension message passing API (make sure that you validate the source - hardcoding the ID would be the best way).
- Extension: When
proxy.html
is loaded, check whether location.ancestorOrigins[0] == 'chrome-extension://[APPID]'
to avoid a security leak. Terminate all steps if this condition fails.
- Extension: When you want to pass a
File
or Blob
to the app, use parent.postMessage(blob, 'chrome-extension://[APPID]');
- App: When it receives the blob from the extension frame, save it to the FileSystem that you obtained through the
chrome.fileSystem
API.
The last task to solve is getting a file from the extension to the extension frame (proxy.html
) that is embedded in the app. This can be done via a SharedWorker
, see this answer for an example (you can skip the part that creates a new frame because the extension frame is already created in one of the previous steps).
Note that at the moment (Chrome 35), File
s can only be sent with a work-around due to a bug.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…