I try to write an extension caching some large media files used on my website so you can locally cache those files when the extension is installed:
I pass the URLs via chrome.runtime.sendMessage to the extension (works)
fetch the media file via XMLHttpRequest in the background page (works)
store the file using FileSystem API (works)
get a File object and convert it to a URL using URL.createObjectURL (works)
return the URL to the webpage (error)
Unfortunately the URL can not be used on the webpage. I get the following error:
Not allowed to load local resource: blob:chrome-extension%3A//hlcoamoijhlmhjjxxxbl/e66a4ebc-1787-47e9-aaaa-f4236b710bda
What is the best way to pass a large file object from an extension to the webpage?
You're almost there.
After creating the blob:-URL on the background page and passing it to the content script, don't forward it to the web page. Instead, retrieve the blob using XMLHttpRequest, create a new blob:-URL, then send it to the web page.
// assuming that you've got a valid blob:chrome-extension-URL...
var blobchromeextensionurlhere = 'blob:chrome-extension....';
var x = new XMLHttpRequest();
x.open('GET', blobchromeextensionurlhere);
x.responseType = 'blob';
x.onload = function() {
var url = URL.createObjectURL(x.response);
// Example: blob:http%3A//example.com/17e9d36c-f5cd-48e6-b6b9-589890de1d23
// Now pass url to the page, e.g. using postMessage
};
x.send();
If your current setup does not use content scripts, but e.g. the webRequest API to redirect request to the cached result, then another option is to use data-URIs (a File or Blob can be converted to a data-URI using <FileReader>.readAsDataURL. Data-URIs cannot be read using XMLHttpRequest, but this will be possible in future versions of Chrome (http://crbug.com/308768).
Two possibilities I can think of.
1) Employ externally_connectable.
This method is described in the docs here.
The essence of it: you can declare that such and such webpage can pass messages to your extension, and then chrome.runtime.connect and chrome.runtime.sendMessage will be exposed to the webpage.
You can then probably make the webpage open a port to your extension and use it for data. Note that only the webpage can initiate the connection.
2) Use window.PostMessage.
The method is mentioned in the docs (note the obsolete mention of window.webkitPostMessage) and described in more detail here.
You can, as far as I can tell from documentation of the method (from various places), pass any object with it, including blobs.
Related
In my javascript application, which utilizes Angular.js and is built with Cordova, I have created a string like so:
Ultimately, I would like to point to a page with this code just as I could point to a file like "player.html". What is the easiest way to achieve this - will I have to use some File API to export this onto a filesystem as an html file?
I ask because I am trying to open this HTML in a new window using InAppBrowser (Cordova plugin) which only accepts URLs to files
I'm not sure if blobs are available in your environment, but if they are, you could use them to generate an URL in memory, like so:
var blob = new Blob([playerBlueprint], {type : 'text/html'});
var pageURL = URL.createObjectURL(blob);
They are not persistent across windows, but you can pass a script that builds them in your new window.
I hope that helps!
As requested - using base64 instead of objectUrl by a blob
var playerBlueprint = '<script>alert("hi")</script>'
open("data:text/html;base64," + btoa(playerBlueprint))
PS A blob can inherit the origin those be able to use the client side storage.
Uncaught DOMException: Failed to read the 'localStorage' property from 'Window': Storage is disabled inside 'data:' URLs.(…)(anonymous function) # VM1085:1`
However you can still communicate with the client side storage if you talk to the opener with postMessages, but why bother - just use objectURL instead
But A base64 url can be safer in a way that it will protect your CSP
You can open the new window and write to it like this:
var myNewWindow = window.open("about:blank");
myNewWindow.document.write("HTML code to be displayed in the new window");
I'm writing an application in phonegap/cordova. In the application, users have a profile and they can select facebook images to include with their profile.
When they select the images for their profile, I want to upload those specific images to my own server.
I did some research and was able to write some code that download the images but I didn't test it on mobile until after I wrote the code.
I used an XML HTTP Request to download the image as a blob. However, upon testing in mobile, I got errors that the blob.size parameter was not set.
I checked in the browser and indeed the blob.size parameter exists. In mobile, the blob.size parameter is undefined. This led me to believe that the webkit that is on my phone does not support blobs and therefore cannot download the images as blobs.
I have 2 questions really:
1) Is my assessment correct that the blob is not being downloaded because the webkit does not support blob?
2) If 1 is true what is the proper way to download an image in cordova and then upload it to my own server? Alternatively, is there any way I can just tweak my code so that it works on mobile? Also I should note that since the files are already downloaded to my phone, is there a way to simply access them in the local storage instead of downloading them again and then upload that file to my server?
My current code to download as a blob is below:
function xhrPromise(url){
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
if (this.status == 200) {
var myBlob = this.response;
return myBlob;
}
};
xhr.send();
}
Based on further research I'm wondering if the solution is to use the cordova file-transfer plugin?
Any suggestions would be greatly appreciated.
1) Webkit supports blob through HTML File API. Read: Exploring the FileSystem APIs
2) I will try to point you into the right direction. What I would do, if I wanted to download an image then re-upload it to another server.
Use Cordova's FileTransfer Plugin to download the picture and write it to a temp location in the SD Card (Android) or the App's Document folder (iOS).
If the image is under 2MB, use a simple HTML tag to link to the downloaded picture, using CDVFILE protocol, <img src="cdvfile://localhost/persistent/temp/image.jpg" /> (for Android) then use AJAX to post it to your server, by encoding the image into base64. Read: How to convert image into base64 string using javascript.
If the image is over 2MB, use Cordova's FileTransfer Plugin to upload.
When using Cordova's FileTransfer Plugin, you will come across HTML File API, I strongly suggest you keep this link as a reference. Read: Exploring the FileSystem APIs
The reason why I would prefer using base64 encoding is because 1) I don't like to rely on Cordova plugins, they used to be super buggy. 2) You can save base64 strings into MySQL (but anything over 2MB will impact your server and the device encoding it).
I am accessing an existing WCF web service (which returns a PDF as a byte stream) using jquery's ajax methods.
When the call to the service completes, I end up with a javascript variable containing a PDF (the variable has the binary data in, starting "%PDF-1.4...").
I'd like to display this PDF in a new browser window, but I'm having difficulty achieving this.
My research so far shows that I might be able to achieve what I want using a data: uri, so my code that's called when the ajax call completes is as follows:
function GotPDF(data)
{
// Here, data contains "%PDF-1.4 ..." etc.
var datauri = 'data:application/pdf;base64,' + Base64.encode(data);
var win = window.open("", "Your PDF", "width=1024,height=768,resizable=yes,scrollbars=yes,toolbar=no,location=no,directories=no,status=no,menubar=no,copyhistory=no");
win.document.location.href = datauri;
}
This causes a new browser window to open, but the contents are blank.
Interestingly, if I point my browser (IE9) at an existing file on my local disk by using a file: uri, such as file://c:/tmp/example.pdf, then I get the same result, i.e. a blank window.
Is there any way I can display this PDF data?
Code you wrote does not display anything, simply open a blank window (location.href is an hash for browsing history, not the content of the page).
To display a PDF you have, at least, following options:
× Embed the PDF viewer inside an object tag. It may not be as straightforward as you may imagine, take a look to this post for sample code. In short it should be something like this:
<object data="your_url_to_pdf" type="application/pdf">
<div>No PDF viewer available</div>
</object>
That's basic code but I suggest to follow what I say in the linked post if you need higher cross-browser compatibility (it also contains a few examples about how you might try to detect support for a PDF viewer).
× Download the file to local computer (simply add the full URL of your web service method that produces the file, do not forget to add the proper Content-Disposition in the header).
× Open the file into a new browser window. Create a normal a tag as you point to a PDF file on-line that you want to display in a new window. Change the href to javascript:functionName and in that function produce the URI you'll use to call the web service method.
Whatever you'll do, do not forget to set the proper MIME type in your response moreover you method shouldn't return a byte stream (even if encoded) but a valid response for your web browser.
If you are using <embed> or <object> tag to display a streamed PDF file (or other file types) from a server as in:
<object data="SomeServlet?do=get_doc&id=6" type="application/pdf" width="800" height="400">
make sure the server sends the proper http content-disposition value, which in this case would be inline.
i have a blob url like blob:blahblah that points to a file. I want to write the file behind this blob to local filesystem. The writer.write() documentation says it accepts a file object (from input-type-file) and a blob. But it throws a type mismatch error when try this
fileEntry.createWriter(function(writer) {
writer.write(blob); //blob is a var with the value set to the blob url
i know the problem is that the blob does not get accepted but i would like to know how can i store a blob to the filesystem. i created the said blob earlier in the script from input-type-file and stored it's value in a var.
EDIT
Ok so i think i should have given more code in the first place.
first i create a blob url and store it in a var like this
files[i]['blob'] = window.webkitURL.createObjectURL(files[i]);
files is from an input-type-file html tag and i is looped for number of files. you know the gig.
then the variable goes through a number of mediums, first through chrome's message passing api to another page and then from that page to a worker via postMessage and then finally back to the parent page via postMessage again.
on the final page i intend to use it to store the blob's file to local file system via file system api like this..
//loop code
fileSystem.root.getFile(files[i]['name'], {create: true}, function(fileEntry) {
fileEntry.createWriter(function(writer) {
writer.write(files[i]['blob']);
});
});
//loop code
but the writer.write throws Uncaught Error: TYPE_MISMATCH_ERR: DOM File Exception 11
i believe this error is because the variable supplied to writer.write is a text and not a blob object from something like createObjectUrl (directly and not after passing through multiple pages/scopes) or not a window.WebKitBlobBuilder. So how can a blob's url be used to store a file?
From your edited code snippet and description, it sounds like you're writing the blobURL to the filesystem rather than the File itself (e.g. files[i]['name'] is a URL). Instead, pass around the File object between main page -> other page -> worker -> main page. As of recent (in Chrome at least), your round trip is now possible. File objects can be passed to window.postMessage(), whereas before, the browser serialized the argument into a string.
You 'fashion' a handler/reference to a Blob with createObjectURL(). There's not really a way to go from blobURL back to a Blob. So in short, no need to create createObjectURL(). Just pass around files[i] directly.
The script adds a download link for videos (on a specific site). How do I change the filename to something else while downloading?
Example URL:
"http://website.com/video.mp4"
Example of what I want the filename to be saved as during download:
"The_title_renamed_with_javascript.mp4"
This actually is possible with JavaScript, though browser support would be spotty. You can use XHR2 to download the file from the server to the browser as a Blob, create a URL to the Blob, create an anchor with its href property set to that URL, set the download property to whatever you want the filename to be, and then click the link. This works in Google Chrome, but I haven't verified support in other browsers.
window.URL = window.URL || window.webkitURL;
var xhr = new XMLHttpRequest(),
a = document.createElement('a'), file;
xhr.open('GET', 'someFile', true);
xhr.responseType = 'blob';
xhr.onload = function () {
file = new Blob([xhr.response], { type : 'application/octet-stream' });
a.href = window.URL.createObjectURL(file);
a.download = 'someName.gif'; // Set to whatever file name you want
// Now just click the link you created
// Note that you may have to append the a element to the body somewhere
// for this to work in Firefox
a.click();
};
xhr.send();
You can't do this with client-side JavaScript, you need to set the response header...
.NET
Response.AddHeader("Content-Disposition", "inline;filename=myname.txt")
Or PHP
header('Content-Disposition: inline;filename=myname.txt')
Also available in other server-side languages of your choice.
The filename for downloading is set in the header (take a look at "Content-Disposition"), wich is created on server-side.
There's no way you could change that with pure javascript on a file you're linking to unless you have access to the server-side (that way you could pass an additional parameter giving the filename and change the server-side behaviour to set the header to match that... but that would also be possible with pure html, no need for javascript). Conclusion: Javascript is absolute useless to achive what you want.
You can probably do this with a Chrome userscript, but it cannot be done (yet) with Greasemonkey (Firefox) javascript.
Workaround methods (easiest to hardest):
Add the links with Greasemonkey but use the excellent DownThemAll! add-on to download and rename the videos.
Download the videos as-is and use a batch file, shell-script, Python program, etc. to rename them.
Use Greasemonkey's GM_xmlhttpRequest()Doc function to send the files to your own web application on a server you control.
This server could be your own PC running XAMPP (or similar).
Write your own Firefox add-on, instead of a Greasemonkey script. Add-ons have the required privileges, Greasemonkey does not.
AFAIK, you will not be able to do this right from the client itself. You could first upload the file onto the server with the desired name, and then serve it back up to the end user (in which case your file name would be used).
Just in case you are looking for such a solution for your nasty downloading chrome extension, you should look into chrome.downloads API, it needs additional permission ('downloads') and allows you to specify filename. https://developer.chrome.com/extensions/downloads
However there is a problem I'm facing right now. The chrome extension I'm refactoring has 600k+ user base and adding a new permission would disable the extension for all of them. So it is no-go solution for me, but if you are developing a new extension you definitely should use it.