I tested the loading of very large files with XMLHttpRequest and noticed an interesting behavior of Chrome (90.0.4430.212): It loads the full file, but finishes with a 200-OK code and and empty (!) result. First I thought it was a silent timeout or whatever, but no, it seems to be a reached memory limit.
Trying it in Firefox (13.0esr (64-bit)) took for ages, but in the end I knew more: The Transfer itself did not trigger an "error" or a "timeout" event, resulted with 200 and empty result as well. But it threw an error:
Exception { name: "NS_ERROR_OUT_OF_MEMORY", message: "", result: 2147942414, filename: "http://localhost/testcenter-backend/vo_data/teterei.html", lineNumber: 16, columnNumber: 0, data: null, stack: "transferComplete#http://localhost/testcenter-backend/vo_data/teterei.html:16:9\n" }
So it has nothing to do with the transport itself, but apparently a memory limit reached when trying to do anything with the result. When I don't use the oReq.response, the error does not appear.
Okay, how to handle this? In Firefox I can catch the error with a simple try catch block. But this does not work for Chrome. A naive approach would be just to check if the resulting content is empty, but I wonder if there is another possibility to detect this stuff happening.
--
How to reproduce:
Create a huge file.
I generated a 10,4GB file this way:
dd if=/dev/zero of=test.img bs=1024 count=0 seek=$[1024*10000]
Run this code in a browser to load it.
const oReq = new XMLHttpRequest();
function updateProgress (oEvent) {
console.log("progress", oEvent.loaded);
}
function transferComplete(evt) {
console.log("The transfer is complete.", oReq.getAllResponseHeaders(), oReq.status);
console.log(oReq.response); // here too much memory is allocated
}
function transferFailed(evt) {
console.log("error", oReq.getAllResponseHeaders(), oReq.status);
}
function transferCanceled(evt) {
console.log("cancel", oReq.getAllResponseHeaders(), oReq.status);
}
function timeOut(evt) {
console.log("timeout", oReq.getAllResponseHeaders(), oReq.status);
}
oReq.addEventListener("progress", updateProgress);
oReq.addEventListener("load", transferComplete);
oReq.addEventListener("error", transferFailed);
oReq.addEventListener("abort", transferCanceled);
oReq.addEventListener("timeout", timeOut);
// your huge file here instead
oReq.open("GET", "https://cdn.jsdelivr.net/npm/jquery#3.2.1/dist/jquery.min.js"); // URL to huge file here
oReq.send();
The actual memory limit may vary to your system.
Related
I'm trying to run "inherited" code and since I'm fairly new to HTML & javascript I keep running into annoying little issues (that's how you learn :D ).
I have a GET request that is meant to load an mp4 file from a server, but for the sake of debugging and practice, I'm trying to load it from a local directory. I enabled the "--allow-file-access-from-files" flag on my chrome, so I know it's not a SOP issue. I can't figure out why, the request is sent 3 times, ALWAYS the first and last fail and the 2nd doesn't, but the file is not loaded.
here's a simplified version of the code:
var req = new XMLHttpRequest();
req.open('GET', currentFileName, true);
// currentFileName is an mp4 in the same directory
req.responseType = 'blob';
req.onload = function () {
// Onload is triggered even on 404
// so we need to check the status code
if (this.status === 200) {
console.log("success!");
doSomeFunc();
}
}
};
req.send();
here's the network log:
I looked into many possible solutions (example 1, 2, 3, 4, 5)
Couldn't find a question where this happens with local files.
What might be wrong?
I am using AJAX2 as described here
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest?redirectlocale=en-US&redirectslug=DOM/XMLHttpRequest/Using_XMLHttpRequest#Monitoring_progress
to monitor progress of ajax request, but i am receiving following error "TypeError: Not enough arguments to XMLHttpRequest.open."
Can anyone help why this error occuring?
My Code is:
var oReq = new XMLHttpRequest();
oReq.addEventListener("progress", updateProgress);
oReq.addEventListener("load", transferComplete);
oReq.addEventListener("error", transferFailed);
oReq.addEventListener("abort", transferCanceled);
oReq.open();
// ...
// progress on transfers from the server to the client (downloads)
function updateProgress (oEvent) {
if (oEvent.lengthComputable) {
var percentComplete = oEvent.loaded / oEvent.total;
console.log(percentComplete+ " percent completed.");
// ...
} else {
console.log(" Unable to compute progress information since the total size is unknown.");
// Unable to compute progress information since the total size is unknown
}
}
function transferComplete(evt) {
console.log("The transfer is complete.");
}
function transferFailed(evt) {
console.log("An error occurred while transferring the file.");
}
function transferCanceled(evt) {
console.log("The transfer has been canceled by the user.");
}
Actuall error is occuring on this line
oReq.open();
open() method has three parameters
open('get' , url , true or false or null)
The code example in that document is not complete, working code. It is designed to show you how to add progress tracking to existing, working XMLHttpRequest code.
You need to fill in the gaps yourself (or better yet: Build a basic XHR based script and then add the progress tracking to it). That includes replacing:
oReq.open();
with a real call to open. See the documentation for open. At a minimum you need to provide an HTTP method and a URL (both string arguments).
I have a very simple function that downloads chunks of a file using an xhr request that looks like so:
var blobXHR = new XMLHttpRequest();
//api.media.chunkURL() returns the correct URL for each chunk
blobXHR.open("GET", api.media.chunkURL({
fileId: fileID,
chunkId: chunkNumber
}));
blobXHR.responseType = "arraybuffer";
blobXHR.onerror = function (e) {
console.log("Error: ", e);
};
var arrayBuffer;
blobXHR.onload = function (e) {
arrayBuffer = blobXHR.response;
};
blobXHR.send();
Now this download function works without any hitches at all using Chrome, Firefox, and just about every Android browser. Unfortunately, when using anything Safari or iOS based I get a very vague error in blobXHR.onerror(). When I output this error to the console I get this response under "e.currentTarget.responseText":
Error: InvalidStateError: DOM Exception 11
I've looked up many questions similar to this and nothing has seemed to work. Any reason why I would be experiencing this with only Safari/iOS browsers?
Edit: This is what I get when I console.log(blobXHR) within onerror():
This is likely a CORS issue. Make sure your server is properly configured to allow this:
http://enable-cors.org/server.html
Also be mindful that Safari won't allow localhost for CORS.
I come here hoping that you lovely folks here on SO can help me out with a bit of a problem that I'm having.
Specifically, every time I attempt to use the decodeAudioData method of a webkitAudioContext, it always triggers the error handler with a null error. This is the code that I'm currently using:
var soundArray;
var context = new webkitAudioContext();
function loadSound(soundName) {
var request = new XMLHttpRequest();
request.open('GET',soundName);
request.responseType = 'arraybuffer';
request.onload = function() {
context.decodeAudioData(this.response, function(buf) {
sounds[soundName] = buf;
},function(err) { console.log("err(decodeAudioData): "+err); });
}
request.send();
}
At this point, it constantly logs error messages to the console saying err(decodeAudioData) = null, mostly because that was just how I decided to log it.
In any case, any idea why this might be going on?
I'm using Chrome Canary, v20.0.1121.0, to try and get something working. But, obviously, it's not working! So, any idea what I might be able to do? If any new information is needed, let me know, and I'll update as necessary.
The real reason is that both createBuffer and decodeAudioData right now have a Bug and throw weird vague DOM exception 12 for files they should normally play.
But we should be aware that this is new and evolving technology and be thankful even for web audio api as it is now since its small miracle that happened to us.
They are missing stream syncing on header boundary that any reasonable decoder of streaming audio format should start with.
And mp3 or many aac/adts files are streaming fileformats. streaming means that you can cut them anywhere or insert append anything (various tags even image artwork) decoder shouldnt care about unknown data. decoder should just seek until he finds header he knows and can decode.
I thrown together this temporary solution that seeks to nearest frame header start and passes data from this offset only.
mp3 or mp2 all start header for every audio frame (every around 200bytes) with 0XFFE and aac(adts) on oxFFF syncword that is there just for this reason. therefore both will sync on 0xFFE.
Here is the code I currently use to play previously not played files.
What I hate is that arrayBuffer doesnt have subarray() like its typed childs to return just different view from different offset instead of whole new array copy that slice() returns. if only webaudio api accepted typedarrays as input but unfortunately the only way to create arraybuffer back seems huge slice() copy.
thankfully usually only one or two seeks are needed.
Forcing Web Audio Api to not being Picky about files
node={};
node.url='usual_mp3_with_tags_or_album_artwork.mp3';
function syncStream(node){ // should be done by api itself. and hopefully will.
var buf8 = new Uint8Array(node.buf);
buf8.indexOf = Array.prototype.indexOf;
var i=node.sync, b=buf8;
while(1) {
node.retry++;
i=b.indexOf(0xFF,i); if(i==-1 || (b[i+1] & 0xE0 == 0xE0 )) break;
i++;
}
if(i!=-1) {
var tmp=node.buf.slice(i); //carefull there it returns copy
delete(node.buf); node.buf=null;
node.buf=tmp;
node.sync=i;
return true;
}
return false;
}
function decode(node) {
try{
context.decodeAudioData(node.buf,
function(decoded){
node.source = context.createBufferSource();
node.source.connect(context.destination);
node.source.buffer=decoded;
node.source.noteOn(0);
},
function(){ // only on error attempt to sync on frame boundary
if(syncStream(node)) decode(node);
});
} catch(e) {
log('decode exception',e.message);
}
}
function playSound(node) {
node.xhr = new XMLHttpRequest();
node.xhr.onload=function(){
node.buf=node.xhr.response;
node.sync=0;
node.retry=0;
decode(node);
}
node.xhr.open("GET", node.url, true);
node.xhr.responseType = "arraybuffer";
node.xhr.send();
}
I was using webkitAudioContext with Chrome 19. Today I've upgraded to Chrome 20 and I have the same problem as you.
I have taken another MP3 file and it works again. The only difference between the two files is the cover embedded in the wrong MP3 file.
I have removed the cover and it works again.
My code changes are not being reflected in IE or FF.
In IE:
I commented out the alert('start'), but I still see the alert when I stop-restart my visual studio debugger.
In FF:
Same code change, but none of the alerts show at all. I uncommented alert('start') and I still dont see it in FF.
I tried setting my port to stay fixed at 5204. I also stopped the ASP.NET development server prior to starting the debugger again. Still doesnt help.
Environment: VS 2010 IE7 and FF3.6.
Code
simplexhr = {
doxhr: function (container, url) {
//alert("START");
if (!document.getElementById || !document.createTextNode) {
alert("NO JS SUPPORT");
return;
}
simplexhr.outputContainer = document.getElementById(container);
if (!simplexhr.outputContainer) {
alert("NO OUTPUT CONTAINER");
return;
}
var request;
try {
//alert("Mozilla AJAX");
request = new XMLHttpRequest();
alert(request);
}
catch (error) {
try {
//alert("IE AJAX");
request = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (error) {
return true;
}
}
request.onreadystatechange = function () {
if (request.readyState == 1)
simplexhr.outputContainer.innerHTML = 'loading...';
}
alert("made it to try AJAX");
request.open('get', url);
//alert("ready state: " + request.readyState);
}
}
UPDATE
On further testing, this problem is due to caching. But, it only happens when I use "unobtrusive javascript" or javascript inside its own *.js file linked in the header:
<script src="Scripts/simpleXHR.js" type="text/javascript"></script>
Moving the code into the page - viewing source - the javascript updates everytime with no caching issues. So is there a better way to perform "unobtrusive javascript"? Or should it be renamed "unproductive javascript" :P
Sounds like it might be a browser cache issue. Try clearing your cache in each browser. Hopefully that will pick up the changes.
You could verify that by doing a view-source in the browser and see if you're actually getting the latest code.