I'm developing a XUL based Firefox extension. I'm trying to create an inline Web Worker using BLOB. The code used to work in Firefox 33 but after update to Firefox 35 I get an error. Here is a code sample:
try {
var blob = new Blob(["function f(){}"], {type: "application/javascript"});
var url = window.URL.createObjectURL(blob); //blob:null/371e34bd-1fbf-4f66-89cc-24d0c1c7bad5
return new Worker(url);
} catch(e) {
console.error(e);
}
And I get a following error:
Failed to load script (nsresult = 0x805303f4)
I'm aware that this error appears when Web Worker tries to load a script from a different domain but I cannot figure out why this is happening in my case. The url I get from createObjectURL() function appears to be invalid. It contains "null/" prefix.
Does anybody have an explanation what is going on? What is the possible fix here?
This example work for me, tested from Firefox 37 to 39.0a2.
// URL.createObjectURL
window.URL = window.URL || window.webkitURL;
// "Server response", used in all examples
var response = "self.onmessage=function(e){postMessage('Worker: '+e.data);}";
var blob;
try {
blob = new Blob([response], {type: 'application/javascript'});
} catch (e) { // Backwards-compatibility
window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
blob = new BlobBuilder();
blob.append(response);
blob = blob.getBlob();
}
var worker = new Worker(URL.createObjectURL(blob));
// Test, used in all examples:
worker.onmessage = function(e) {
alert('Response: ' + e.data);
};
worker.postMessage('Test');
Related
This is my script:
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.7.13.min.js"></script>
<script>
AWS.config.region = 'eu-west-1';
AWS.config.accessKeyId = 'FOO';
AWS.config.secretAccessKey = 'BAR';
var polly = new AWS.Polly({apiVersion: '2016-06-10'});
var params = {
OutputFormat: 'mp3', /* required */
Text: 'Hello world', /* required */
VoiceId: 'Joanna', /* required */
SampleRate: '22050',
TextType: 'text'
};
polly.synthesizeSpeech(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
</script>
The request succeeds, and I get this kind of response:
How do I use this kind of response? I understand that the response is deserialized audio, but how do I actually play it, say, inside a HTML5 audio element?
Furthermore, this answer on SO explains why is this type of array suitable for audio data: https://stackoverflow.com/a/26320913/1325575
var uInt8Array = new Uint8Array(audioStream);
var arrayBuffer = uInt8Array.buffer;
var blob = new Blob([arrayBuffer]);
var url = URL.createObjectURL(blob);
audioElement.src = url;
audioElement.play();
I created a Javascript library called ChattyKathy that will handle the entire process for you if you want to take the easy way out.
Just pass it an AWS Credentials object and then tell her what to say. She'll call AWS, transform the response, and play the audio.
var settings = {
awsCredentials: awsCredentials,
awsRegion: "us-west-2",
pollyVoiceId: "Justin",
cacheSpeech: true
}
var kathy = ChattyKathy(settings);
kathy.Speak("Hello world, my name is Kathy!");
kathy.Speak("I can be used for an amazing user experience!");
Elliott's Chatty Kathy code worked beautifully for me, but there are two separate issues with Safari and mobile.
Safari: When creating the blob, the content type MUST be specified:
var blob = new Blob([arrayBuffer], {type: 'audio/mpeg'});
url = webkitURL.createObjectURL(blob);
Mobile: The above must be true, plus playback needs to be initiated by a user touch event. Note: Older iOS versions seem to require that playback be initiated in the same thread as the touch event, so a touch event that initiates a promise chain that eventually calls audio.play() will fail. Later iOS versions seem to be smarter about this.
Using the Web Audio API:
const result = await polly.synthesizeSpeech(params).promise();
const aContext = new AudioContext();
const source = aContext.createBufferSource();
source.buffer = await aContext.decodeAudioData(result.AudioStream.buffer);
source.connect(aContext.destination);
source.start();
Docs:
AudioContext
Decode ArrayBuffer
I am writing an app using PhoneGap. I want to use javascript Web Worker. It works fine on Android. It also works on iOS with UIWebView.
I would like to use WKWebView in iOS 9. I tried to read the .txt files in a local folder successfully using cordova-plugin-file.
I have used the following tutorial:
https://www.scirra.com/blog/ashley/25/hacking-something-useful-out-of-wkwebview
But this did not solve it...
new Worker ('js/tested.js');
Dom Exception 18 returns an error.
new Worker ('www/js/tested.js');
Dom Exception 18 returns an error.
I tried to specify the full path to worker javascript file, but I get the same error:
new Worker (file_path+'js/tested.js');
Dom Exception 18 returns an error.
So how to create the worker?
It worked!
Web workers without a separate Javascript file?
var worker_js = null;
function fetchLocalFileViaCordova(filename, successCallback, errorCallback)
{
var path = cordova.file.applicationDirectory + "www/" + filename;
window.resolveLocalFileSystemURL(path, function (entry)
{
entry.file(successCallback, errorCallback);
}, errorCallback);
};
function fetchLocalFileViaCordovaAsText(filename, successCallback, errorCallback)
{
fetchLocalFileViaCordova(filename, function (file)
{
var reader = new FileReader();
reader.onload = function (e)
{
successCallback(e.target.result);
};
reader.onerror = errorCallback;
reader.readAsText(file);
}, errorCallback);
};
fetchLocalFileViaCordovaAsText("js/worker.js", function (js_script) { // Worker WKWebView
worker_js = window.URL.createObjectURL(
new Blob([js_script], { type: "text/javascript" })
);
});
if (worker_js != null) {
backgroundEngine = new Worker(worker_js);
} else {
backgroundEngine = new Worker("js/worker.js");
}
My application receives some video chunks from a ServerSentEvent (SSE) and, using MediaStream API, it should append them in a buffer and visualize them into an HTML5 video tag.
The problem is MediaSource API, that stops working when the program tries to append a chunk to the mediaStream buffer.
The error appears when the program tries to append the first chunk.
This is the client-side code:
window.MediaSource = window.MediaSource || window.WebKitMediaSource;
window.URL = window.URL || window.webkitURL;
if (!!!window.MediaSource) {alert('MediaSource API is not available');}
var video = document.getElementById("video");
var mediaSource = new MediaSource();
video.src = window.URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', function(e) {
var source = new EventSource('http://localhost:5000/video');
// this is the line that catch the error
var sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vorbis,vp8"');
source.addEventListener('chunkStreamed', function(e){
var chunk = new Blob(JSON.parse(e.data));
console.log("chunk: ", chunk);
var reader = new FileReader();
reader.onload = function(e) {
// error is caused by this line
sourceBuffer.appendBuffer(new Uint8Array(e.target.result));
};
reader.readAsArrayBuffer(chunk);
});
source.addEventListener('endStreaming', function(e){
console.log(e.data);
// mediaSource.endOfStream();
// endOfStream() not here, sourceBuffer.appendBuffer will done after this command and will cause InvalidStateError
});
source.onopen = function(e) {
console.log("EventSource open");
};
source.onerror = function(e) {
console.log("error", e);
source.close();
};
}, false);
and this is the complete error log:
Uncaught QuotaExceededError: An attempt was made to add something to storage that exceeded the quota.
The problem comes out when the app tries to do sourceBuffer.appendBuffer(new Uint8Array(e.target.result));.
I really can't understand way this error appear. Code is really
like the code of this example
Hi i am using the JS HTML5 File API to handle file uploads to my server.
I am getting the following error in Aurora(Fire Fox Bleeding edge builds)
NS_ERROR_INVALID_POINTER: Component returned failure code: 0x80004003 (NS_ERROR_INVALID_POINTER) [nsIDOMFileReader.readAsBinaryString]
function readBlob(opt_startByte, opt_stopByte,file,partNo) {
var start = parseInt(opt_startByte);
var stop = parseInt(opt_stopByte);
var reader = new FileReader();
var totalParts = parseInt(file.size/MAX_READ);
if((file.size % MAX_READ) !== 0){
totalParts++;
}
// If we use onloadend, we need to check the readyState.
reader.onloadend = function(evt) {
if (evt.target.readyState == FileReader.DONE) {
//var contents = reader.result;
postFilePart(partNo,contents,totalParts,escape(file.name))// DONE == 2
}
};
if (file.webkitSlice) {
var blob = file.webkitSlice(start, stop);
} else if (file.mozSlice) {
var blob = file.mozSlice(start, stop);
}
reader.readAsBinaryString(blob);
}
the error is occurring at this line
reader.readAsBinaryString(blob);
i have tried mozSlice and Slice
if (file.mozSlice) {
var blob = file.mozSlice(start, stop);
}
and it gave me the same results. it might not be the best idea to use HTML 5 API yet as this may cause issues with other browsers as well.
does anyone have a work around to get the same functionality or how i can resolve this particular error
Solved the issue it it was rerunning the reader code with incorrect parameters due to a mistake on the calling method
https://bugzilla.mozilla.org/show_bug.cgi?id=725289
rather use slice vs mozSlice
I'm attempting to provide a script-only solution for reading the contents of a file on a client machine through a browser.
I have a solution that works with Firefox and Internet Explorer. It's not pretty, but I'm only trying things at the moment:
function getFileContents() {
var fileForUpload = document.forms[0].fileForUpload;
var fileName = fileForUpload.value;
if (fileForUpload.files) {
var fileContents = fileForUpload.files.item(0).getAsBinary();
document.forms[0].fileContents.innerHTML = fileContents;
} else {
// try the IE method
var fileContents = ieReadFile(fileName);
document.forms[0].fileContents.innerHTML = fileContents;
}
}
function ieReadFile(filename)
{
try
{
var fso = new ActiveXObject("Scripting.FileSystemObject");
var fh = fso.OpenTextFile(filename, 1);
var contents = fh.ReadAll();
fh.Close();
return contents;
}
catch (Exception)
{
return "Cannot open file :(";
}
}
I can call getFileContents() and it will write the contents into the fileContents text area.
Is there a way to do this in other browsers?
I'm most concerned with Safari and Chrome at the moment, but I'm open to suggestions for any other browser.
Edit: In response to the question, "Why do you want to do this?":
Basically, I want to hash the file contents together with a one-time-password on the client side so I can send this information back as a verification.
Edited to add information about the File API
Since I originally wrote this answer, the File API has been proposed as a standard and implemented in most browsers (as of IE 10, which added support for FileReader API described here, though not yet the File API). The API is a bit more complicated than the older Mozilla API, as it is designed to support asynchronous reading of files, better support for binary files and decoding of different text encodings. There is some documentation available on the Mozilla Developer Network as well as various examples online. You would use it as follows:
var file = document.getElementById("fileForUpload").files[0];
if (file) {
var reader = new FileReader();
reader.readAsText(file, "UTF-8");
reader.onload = function (evt) {
document.getElementById("fileContents").innerHTML = evt.target.result;
}
reader.onerror = function (evt) {
document.getElementById("fileContents").innerHTML = "error reading file";
}
}
Original answer
There does not appear to be a way to do this in WebKit (thus, Safari and Chrome). The only keys that a File object has are fileName and fileSize. According to the commit message for the File and FileList support, these are inspired by Mozilla's File object, but they appear to support only a subset of the features.
If you would like to change this, you could always send a patch to the WebKit project. Another possibility would be to propose the Mozilla API for inclusion in HTML 5; the WHATWG mailing list is probably the best place to do that. If you do that, then it is much more likely that there will be a cross-browser way to do this, at least in a couple years time. Of course, submitting either a patch or a proposal for inclusion to HTML 5 does mean some work defending the idea, but the fact that Firefox already implements it gives you something to start with.
In order to read a file chosen by the user, using a file open dialog, you can use the <input type="file"> tag. You can find information on it from MSDN. When the file is chosen you can use the FileReader API to read the contents.
function onFileLoad(elementId, event) {
document.getElementById(elementId).innerText = event.target.result;
}
function onChooseFile(event, onLoadFileHandler) {
if (typeof window.FileReader !== 'function')
throw ("The file API isn't supported on this browser.");
let input = event.target;
if (!input)
throw ("The browser does not properly implement the event object");
if (!input.files)
throw ("This browser does not support the `files` property of the file input.");
if (!input.files[0])
return undefined;
let file = input.files[0];
let fr = new FileReader();
fr.onload = onLoadFileHandler;
fr.readAsText(file);
}
<input type='file' onchange='onChooseFile(event, onFileLoad.bind(this, "contents"))' />
<p id="contents"></p>
There's a modern native alternative: File implements Blob, so we can call Blob.text().
async function readText(event) {
const file = event.target.files.item(0)
const text = await file.text();
document.getElementById("output").innerText = text
}
<input type="file" onchange="readText(event)" />
<pre id="output"></pre>
Currently (September 2020) this is supported in Chrome and Firefox, for other Browser you need to load a polyfill, e.g. blob-polyfill.
Happy coding!
If you get an error on Internet Explorer, Change the security settings to allow ActiveX
var CallBackFunction = function(content) {
alert(content);
}
ReadFileAllBrowsers(document.getElementById("file_upload"), CallBackFunction);
//Tested in Mozilla Firefox browser, Chrome
function ReadFileAllBrowsers(FileElement, CallBackFunction) {
try {
var file = FileElement.files[0];
var contents_ = "";
if (file) {
var reader = new FileReader();
reader.readAsText(file, "UTF-8");
reader.onload = function(evt) {
CallBackFunction(evt.target.result);
}
reader.onerror = function(evt) {
alert("Error reading file");
}
}
} catch (Exception) {
var fall_back = ieReadFile(FileElement.value);
if (fall_back != false) {
CallBackFunction(fall_back);
}
}
}
///Reading files with Internet Explorer
function ieReadFile(filename) {
try {
var fso = new ActiveXObject("Scripting.FileSystemObject");
var fh = fso.OpenTextFile(filename, 1);
var contents = fh.ReadAll();
fh.Close();
return contents;
} catch (Exception) {
alert(Exception);
return false;
}
}
This works fine
function onClick(event) {
filecontent = "";
var myFile = event.files[0];
var reader = new FileReader();
reader.addEventListener('load', function (e) {
filecontent = e.target.result;
});
reader.readAsBinaryString(myFile);
}