Crash in Firefox or Chrome when using FileReader - google-chrome

We try to upload a zip file using the “FileReader” javascript object on the client. For small files, everything seems to be fine. If we use a zip input file of 132Mo, in Firefox v21.0 on Windows 7, the first upload from the local disk thru the javascript object works well. But, if we try to select another file and start the transfert, Firefox crashes…. In Chrome v27.0 on Windows 7, it crashes(see the ‘oups’ page) on the first try. We tried on a Mac OS with same browsers without problem. Mainly strange but when the Windows task manager window is open, no more crash in Firefox or Chrome while loading big files. Maybe the presence of this TOP window forces the garbage collector to do its work faster?
Has anyone already faced this issue? As reference, I joined a simple HTML page that may reproduce the behavior.
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function ()
{
$("#btnFile").change(startUpload);
});
function startUpload(e)
{
this.fileHandle = e.target.files[0];
var reader = new FileReader();
reader.onprogress = function (data)
{
if (data.lengthComputable)
{
var progress = parseInt(((data.loaded / data.total) * 100), 10);
$("#progress").html("Reading file: " + progress + " %");
}
};
reader.onloadend = function ()
{
$("#progress").html("Reading completed!");
};
reader.onerror = function (errorEvent)
{
console.error("FileReader error", errorEvent);
};
reader.readAsDataURL(this.fileHandle);
};
</script>
</head>
<body>
<input type="file" id="btnFile" />
<div id="progress"></div>
</body>
</html>

Related

AngularJS Node app downloads instead of loading in Internet Explorer

Whenever I try loading my AngularJS webpage (on a node js + express server) through http://localhost:3000, Internet Explorer just tries to download it with a random name and no file extension, instead of showing the actual page.
I can't even check the console for errors, because the download attempt seems to start before even trying to load it as a webpage.
The website works perfectly on both Firefox and Chrome. I am using Internet Explorer 11, so it's not even an old version.
I tried running my index through the W3 Validator, and the only errors it points out in the DOM are the Angular directives and tags (which are not really errors).
I tried changing the DOCTYPE from just a simple <!DOCTYPE html> to this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-loose.dtd">
And this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
... and this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
To no effect.
As suggested in other questions, I also tried serving my page as "text/plain" rather than "text/html" in my node server, but that didn't help either.
Here's the html for my index:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="it">
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<!-- Angular Material CSS now available via Google CDN; version 1.0.7 used here -->
<link rel="stylesheet" href="./modules/angular-material/angular-material.min.css"/>
<link rel="stylesheet" href="./css/style.css"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"/>
<link rel="icon" href="icons/favicon.ico" type="image/x-icon"/>
<base href="/"/>
<title>Area Clienti</title>
<script src="./modules/angular/angular.js" type="text/javascript"></script>
<script src="./modules/ngstorage/ngStorage.js" type="text/javascript"></script>
<script src="./modules/angular-aria/angular-aria.min.js" type="text/javascript"></script>
<script src="./modules/angular-animate/angular-animate.min.js" type="text/javascript"></script>
<script src="./modules/angular-material/angular-material.min.js" type="text/javascript"></script>
<script src="./modules/angular-ui-router/release/angular-ui-router.min.js" type="text/javascript"></script>
<script src="./modules/ui-router-extras/release/modular/ct-ui-router-extras.core.min.js" type="text/javascript"></script>
<script src="./modules/ui-router-extras/release/modular/ct-ui-router-extras.dsr.min.js" type="text/javascript"></script>
<script src="./modules/ui-router-extras/release/modular/ct-ui-router-extras.sticky.min.js" type="text/javascript"></script>
<script src="./modules/pdfmake/build/pdfmake.min.js" type="text/javascript"></script>
<script src="./modules/pdfmake/build/vfs_fonts.js" type="text/javascript"></script>
<script src="./modules/angular-messages/angular-messages.min.js" type="text/javascript"></script>
<script src="http://ngmaterial.assets.s3.amazonaws.com/svg-assets-cache.js" type="text/javascript"></script>
<script src="./js/client.js" type="text/javascript"></script>
</head>
<body ng-app="App" ng-controller="AppCtrl" ng-view ui-view ng-cloak layout="row">
</body>
</html>
The script tags were in the body before, I tried putting them in the head to see if that would have helped, but it didn't.
What might be the issue here? And why is it so painful to make stuff work on IE?
EDIT: here's the server code.
var express = require("express");
var fs = require('fs');
var xml2js = require("./node_modules/xml2js");
var auth = require("./authenticator.js");
var bodyParser = require('body-parser');
var app=express();
//app.use(express.static('pages'));
var server = app.listen(3000, function(){
var host = server.address().address;
var port = server.address().port;
});
var jsonParser = bodyParser.json(); // to support JSON-encoded bodies
var urlencodedParser = bodyParser.urlencoded({ // to support URL-encoded bodies
extended: true
});
app.get('/', function(req, res){
sendFile(res, './index.html', getHeader('text/plain'));
});
app.use('/font/', express.static('./font/'));
app.use('/modules/', express.static('./node_modules/'));
app.use('/css/', express.static('./css/'));
app.use('/views/', express.static('./views/'));
app.use('/img/', express.static('./img/'));
app.use('/js/', express.static('./js/'));
function sendFile(res, path, header){
fs.readFile(path, function(err, content){
if(err) {
console.log("\nErrore caricando " + path + " - err: " + err);
//pageNotFound(res, "error");
}
else sendToClient(content, res, 200, header);
});
}
function getHeader(type){
return {"Content-Type": type};
}
function sendToClient(data, res, code, type){
res.writeHead(code, getHeader(type));
(type === "text/html" || type === "text") ? res.end(data, "utf-8") : res.end(data);
}
Plus a bunch of other POST and GET services which I think are not relevant
UPDATE: I managed to get it to say something in the developer console. This is the error:
(translation: impossible to open http://localhost:3000/)
It still tries to download the page.
UPDATE 2:
I managed to find out what the issue was: for some reason, the Content-Type header content was being interpreted as an object rather than the string it actually was, so IE couldn't render the page.
It now renders the index, but Angular-UI-Router doesn't seem to work.
It appears as though ui-router can't load sub states (it can switch between normal states though, since I was able to log in and be transferred to my dashboard page, which is a different state)
(Everything is still working perfectly fine on Chrome and Firefox)
Could it be sending you a file because you're using .sendFile():
app.get('/', function(req, res){
sendFile(res, './index.html', getHeader('text/plain'));
});
instead of using .render() to render the page?
app.get('/', function (req, res) {
res.render('index.html');
})
Try using Express's use() to always send via sendFile() the index.html after any HTTP action using the * wildcard. Make sure this goes after any other API REST routes or similar. This helps ensure that the Angular app within index.html is not constantly reloaded by other pages being rendered. This way any other API routes send/receive the data you need and only if nothing else is matched, will the index.html be loaded. Below is a structure that worked for me for an Express + Angular 2 application.
File Structure:
api
foo
foo.js
bar
bar.js
public
fonts
css
img
index.html
server.js
Server JS:
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
// fonts, css, images are stored in respective folders within folder "public"
app.use(express.static(path.join(__dirname, 'public')));
// some RESTful endpoints
app.use('/api/foo', fooRoutes);
app.use('/api/bar', barRoutes);
app.use('*', (req, res) => res.sendFile(path.join(__dirname, 'public', 'index.html')));
// error handling middleware
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});

Google Docs Viewer occasionally failing to load content in iframe

I'm having an issue with the Google Docs viewer that is causing a nightmare to solve because it only happens intermittently. I'm looking for guidance on how to make the content in the iframe load everytime without issue as it should.
Steps to reproduce
1) This page is a basic HTML page with a h1 tag and an iframe containing a link to a PDF on the same server
http://bit.ly/1mqbuf7
2) When you load the page, the pdf document will load in the iframe 60% of the time.
3) If you hit refresh 10 or so times, at least once it will fail to appear. Google returns a 307 first (Which it also does when it works) and then returns a 204 - no content. When it works, it returns a 200, with the content you see in the viewer.
I'm struggling to understand why it only does this some of the time with no visible errors. This has been tested and failed on Google Chrome v 48.0.2564.103 (PC) and Internet Explorer Edge v25.10586 (PC) with the same results and frequency of failure.
Any guidance would be greatly appreciated.
This is not fixing your problem per se, but since I had the same problem and I eventually managed to find an acceptable solution, I thought I'd share it.
var $docViewer = $(`<iframe src="${newValue}" height="100%" width="100%"></iframe>`);
//If using modern browser, use and embed object
if (window.chrome || typeof (window.mozInnerScreenX) != "undefined")
$docViewer = $(`<object width="100%" height="100%" data="${newValue}" type="application/pdf">
<embed src="${newValue}" type="application/pdf">
<p>This browser does not support PDFs.Please download the PDF to view it: Download PDF.</p>
</embed>
</object>`);
//Add the new viewer
$docViewer.appendTo($("#invoicePreview"));
Basically, use an embed if modern browser, and the gviewer if not. The embed object behaves identically to the google doc viewer, it works in 100% of cases (no failed loads), but since it's not supported for IE and/or low-end mobile devices, use the google doc viewer for that... Progressive Enhancements I guess.
Here's a "hack" that will ensure a proper loading every time (albeit with some delay, due to potential failed attempts - it's Google's fault, don't shoot the messenger!). The 2s interval duration can be modified to best fit the time expected for a successful effort to start loading the iFrame.
HTML:
<div id="test-id-1" style="text-align: center; width: 100%; height: 1150px" class="embed-pdf" data-url="{insert_pdf_link_here}"><span class="loader">Please wait...</span></div>
JS:
$(document).ready(function() {
let embed_pdfs = {};
$('.embed-pdf').each(function() {
var $pdfViewer = $('<iframe src="https://docs.google.com/viewer?url=' + $(this).data('url') + '&embedded=true" style="width: 100%; height: 100%" frameborder="0" scrolling="no"></iframe>');
$pdfViewer.appendTo($(this));
console.log($(this).attr('id') + " created");
embed_pdfs[$(this).attr('id')] = 'created';
});
$(document).find('.embed-pdf iframe').load(function(){
embed_pdfs[$(this).parents('.embed-pdf').attr('id')] = 'loaded';
$(this).siblings('.loader').remove();
console.log($(this).parents('.embed-pdf').attr('id') + " loaded");
});
let embed_pdf_check = setInterval(function() {
let remaining_embeds = 0;
$.each(embed_pdfs, function(key, value) {
try {
if ($($('#' + key)).find('iframe').contents().find("body").contents().length == 0) {
remaining_embeds++;
console.log(key + " resetting");
$($('#' + key)).find('iframe').attr('src', src='https://docs.google.com/viewer?url=' + $('#' + key).data('url') + '&embedded=true');
}
}
catch(err) {
console.log(key + " reloading");
}
});
if (!remaining_embeds) {
clearInterval(embed_pdf_check);
}
}, 2000);
});
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<div id="iframeContainer"></div>
</body>
<script>
var URL = "https://docs.google.com/viewer?url=http://www.africau.edu/images/default/sample.pdf&embedded=true";
var count = 0;
var iframe = ` <iframe id = "myIframe" src = "${URL}" style = "width:100%; height:500px;" frameborder = "0"></iframe>`;
$(`#iframeContainer`).html(iframe);
$('#myIframe').on('load', function(){
count++;
if(count>0){
clearInterval(ref)
}
});
var ref = setInterval(()=>{
$(`#iframeContainer`).html(iframe);
$('#myIframe').on('load', function() {
count++;
if (count > 0) {
clearInterval(ref)
}
});
}, 4000)
</script>
</html>
Change var URL = your_googel_docs_pdf_url
The code will keep loading the url into iframe until the doc loads successfully.
It's not the best solution. But I waited a few seconds after the page loaded and checked if the iframe was loaded (see how below). If it wasn't, then I set the iframe's src attribute to null and then back to the correct source, affectively reloading it. Then waited a few seconds to check again and repeated. Once it has loaded you can stop checking.
conditionalPdfIFrameReloadTimeout() {
setTimeout(() => {
let iFrame = document.GetElementById("pdfIframe")
if (iFrame.contentDocument/*pdf iframe failed to load*/) {
iFrame.src = null
iFrame.src = "the url to your pdf"
conditionalPdfIFrameReloadTimeout()
}
}, 6000)
}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en" style="width:100%; height:100%;">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
</head>
<body id="iframeContainer" style="height: 100%; width: 100%; overflow: hidden; margin:0px;">
<script>
var URL = "https://docs.google.com/gview?url=enteryoururl&embedded=true";
var count = 0;
var iframe = `<iframe id = "myIframe" src = "${URL}" style="width:100%; height:100%;" frameborder="0" allowfullscreen=""></iframe>`;
$(`#iframeContainer`).html(iframe);
$('#myIframe').on('load', function(){
count++;
if(count>0){
clearInterval(ref)
}
});
var ref = setInterval(()=>{
$(`#iframeContainer`).html(iframe);
$('#myIframe').on('load', function() {
count++;
if (count > 0) {
clearInterval(ref)
}
});
}, 2000)
</script>
</body>
</html>
The above code worked for me.

Overcoming connection limitation with mobile safari

I recently discovered that mobile safari (on iOS 9.1 - though unsure about older safari versions) has an unfortunate problem with multiple connections and images. If you have six images when you load a page, it will delay XHR requests that loads after those six images by a huge lag time (order of 30s).
For example, I loaded the following using a standard NodeJS/Express server and saw giant delays before the alert showed up - despite the fact that all there images are just a couple of KB and I can visibly see that they are all loaded. The dev console also shows the images loaded but not the XHR request. The duration it took to download the file was very small but the latency was huge.
This is not a problem on any other browsers (mobile chrome, regular safari, etc).
Sample problem html:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<img src="/static/images/home/payment.png">
<img src="/static/images/home/couple-present-mobile.jpg">
<img src="/static/images/general/welcome.jpg">
<img src="/static/images/general/arrow-down.png">
<img src="/static/images/general/arrow-right.png">
<img src="/static/images/general/check.png">
<script>
var url = '/static/test.html'
var xhr = new XMLHttpRequest();
xhr.open('GET', encodeURI(url));
xhr.onload = function() {
alert(url) //This takes forever
};
xhr.send();
</script>
</body>
</html>
An odd thing is if you ran the XHR request BEFORE there were 6 images, it would work perfectly. In fact, if you even did something like this one below, it was fine. I think this works because the background CSS image must retrieve the URL after the XHR is initiated.
Replace one of the img tags with a background css image and it works:
<!DOCTYPE html>
<html>
<head>
<style>
.test {
background-image: url("/static/images/home/check.png");
height: 400px;
}
</style>
</head>
<body>
<img src="/static/images/home/payment.png">
<img src="/static/images/home/couple-present-mobile.jpg">
<img src="/static/images/general/welcome.jpg">
<img src="/static/images/general/arrow-down.png">
<img src="/static/images/general/arrow-right.png">
<!--<img src="/static/images/general/check.png"> REMOVE THIS LINE and add the background image instead-->
<div class="test"></div>
<script>
var url = '/static/test.html;
var xhr = new XMLHttpRequest();
xhr.open('GET', encodeURI(url));
xhr.onload = function() {
console.log(url) //NO DELAYS!
};
xhr.send();
</script>
</body>
</html>
Also I found out that just running 7 simultaneous XHR requests also does not cause this problem (such as the below):
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script>
var urls = ['/static/images/home/payment.png',
'/static/images/home/couple-present-mobile.jpg',
'/static/images/general/arrow-right.png',
'/static/images/general/arrow-down.png',
'/static/images/general/welcome.jpg',
'/static/images/general/check.png',
'/static/test.html'];
for(var i = 0, ii = urls.length; i < ii; i++){
(function(){
var url = urls[i];
var xhr = new XMLHttpRequest();
xhr.open('GET', encodeURI(url));
xhr.onload = function() {
console.log(url)
};
xhr.send();
})()
}
</script>
</body>
</html>
Has anyone come across this problem and figured out a way to deal with it without reducing the number of images or putting them in a sprite or something?
The hack I ultimately used to get around this was to load the images from a different host. For simplicity, if the browser detected that the host was www.domain.com, I would load images it from domain.com and vice versa. You can also just have all your images come from a certain host like images.domain.com and keep your api's and other stuff on another host.
Not the most ideal or elegant solution, but its super simple to implement and solves the problem.

How do I make a simple snapshot webapp in HTML5?

I need to make a HTML5 page that can display live video coming from the device's camera/webcam and that has a button that can take a snapshot; nothing fancy (similar to html5camera.com). I tried following some tutorials (http://www.html5rocks.com/en/tutorials/getusermedia/intro/ AND http://davidwalsh.name/browser-camera), but I'm still new at HTML, so I'm not sure what to do with the code snippets I find.
I copied some code into a HTML-file, but when I open it with Chrome, it says at the right in the address bar that it blocked access to the camera and microphone (without bothering to ask for permission). When I click the option to ask for permission next time and reload the page, nothing happens. There is no way to set the preferences to allow by default. I also tried Chrome Canary (same as Chrome) and Firefox (didn't ask for permission).
Did I make a mistake in the HTML? Is Chrome the problem?
The code I have so far:
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=yes">
</head>
<body>
<video id="video" width="640" height="480" autoplay></video>
<button id="snap">Snap Photo</button>
<canvas id="canvas" width="640" height="480"></canvas>
<script>
// Put event listeners into place
window.addEventListener("DOMContentLoaded", function() {
// Grab elements, create settings, etc.
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
video = document.getElementById("video"),
videoObj = { "video": true },
errBack = function(error) {
console.log("Video capture error: ", error.code);
};
// Put video listeners into place
if(navigator.getUserMedia) { // Standard
navigator.getUserMedia(videoObj, function(stream) {
video.src = stream;
video.play();
}, errBack);
} else if(navigator.webkitGetUserMedia) { // WebKit-prefixed
navigator.webkitGetUserMedia(videoObj, function(stream){
video.src = window.webkitURL.createObjectURL(stream);
video.play();
}, errBack);
}
// Trigger photo take
document.getElementById("snap").addEventListener("click", function() {
context.drawImage(video, 0, 0, 640, 480);
});
}, false);
</script>
</body>
</html>
You might also consider just going native. You can add native camera and camcorder support to any web app on Android, iOS or Windows Phone 8 very easily with BridgeIt: http://bridgeit.mobi. It's a simple javascript api that allows you to access native mobile features.

HTML5 file upload fails in IE9

Following code works fine in Chrome, but fails in IE9 - in processFiles() when we retrieve selected files e.target.files is null
<!DOCTYPE html>
<html>
<header>
<meta http-equiv="X-UA-Compatible" content="IE=8" />
</header>
<body>
<input type="file" id="uploader"/>
<script>
var uploader = document.getElementById ("uploader");
if (uploader.addEventListener) { // all browsers except IE before version 9
uploader.addEventListener ("change", processFiles, false);
}
else {
if (uploader.attachEvent) { // IE before version 9
uploader.attachEvent ("change", processFiles);
}
}
function processFiles(e)
{
var files = e.target.files || e.dataTransfer.files;
for (var i = 0 ; i < files.length ; i ++)
{
window.console && console.log && console.log(files[i].name);
}
}
</script>
<body>
Any ideas?
Your code assumes support for the File API. The first version of IE to support the File API is IE10. Your code will never work cross-browser as it stands now.
Consider using Fine Uploader which already handles uploads cross-browser and includes a number of useful features.