javascript/angular readAsArrayBuffer security error - javascript

Hey guys I'm having an issue with file reader directive where on IE 11 I'm getting intermittent security error:
the directive code:
(function () {
var module = angular.module("new");
module.directive("freader", function () {
return {
scope: {
freader: "="
},
link: function (scope, element) {
element.bind("change", function (e) {
var reader = new FileReader();
var file = e.target.files[0];
var data = null;
file.size = file.date;
reader.onload = function (e) {
var array = new Uint8Array(e.target.result);
var result = Array.prototype.slice.call(array);
data = { data: result };
data.name = file.name;
if (data) {
scope.$apply(function() {
scope.freader = data;
});
}
}
if(file)
reader.readAsArrayBuffer(file);
});
}
}
});})();
As I researched so far I created clean function:
<input type="file" freader="vm.file" onclick="clean(this)" />
function clean(val) {
try {
val.value = null;
} catch (ex) { }
if (val.value) {
val.parentNode.replaceChild(val.cloneNode(true), val);
}
}
I have also tried to set the input type to text and then back again to file but nothing really works, error always shows up after some time.
Not really sure how to handle this problem?

Related

Return true/false from asynchronous function to use in synchronous if()

I'm creating simple upload form (for .zip files) and I want to validate if zip contains all files which will be necessary later.
so I have a function isZipCorrect():
isZipCorrect = function() {
'use strict';
if (this.name.slice(-3) === 'zip') {
var fileReader = new FileReader();
var zip = new JSZip();
var shpCorrect = false;
fileReader.onload = function() {
var zip = new JSZip(this.result);
shpCorrect = zip.file(/.*?/).every(function(file) {
return (file.name.slice(-3) === 'shp' ||
file.name.slice(-3) === 'dbf' ||
file.name.slice(-3) === 'shx');
});
console.log(shpCorrect);
};
fileReader.readAsArrayBuffer(this.file);
return shpCorrect;
} else {
return true;
}
and I use it in if(isZipCorrect()) before XMLHttpRequest.
I assume that the problem is asynchronous function (fileReader.onload) which end operation when whole code is already ended. But I don't want to call send function from fileReader.onload because for me checking is Zip correct must be optional (you should be able to upload other files which will go through without 'parsing')
You could use jQuery.Deferred() to do something like this:
validateZipFile = function(file) {
var
deferred = $.Deferred(),
fileReader = new FileReader();
fileReader.onload = function() {
var zip = new JSZip(this.result);
var isCorrect = zip.file(/.*?/).every(function(file) {
return /\.(shp|dbf|shx)$/i.test(file.name)
});
isCorrect ? deferred.resolve() : deferred.reject();
};
if( !/\.(zip)$/i.test(file.name) ) {
deferred.reject()
} else {
fileReader.readAsArrayBuffer(file)
}
return deferred.promise()
};
and call it like so:
validateZipFile(file).done(submitForm).fail(handleBadZip);
where 'submitForm' and 'handleBadZip' are functions you've previously defined
you can pass a callback function as a parameter to isZipCorrect and in callback function you can use send function:
isZipCorrect = function(callback) {
'use strict';
if (this.name.slice(-3) === 'zip') {
// all the existing code
fileReader.onload = function() {
var zip = new JSZip(this.result);
shpCorrect = zip.file(/.*?/).every(function(file) {
callback((file.name.slice(-3) === 'shp' ||
file.name.slice(-3) === 'dbf' ||
file.name.slice(-3) === 'shx'));
});
};
fileReader.readAsArrayBuffer(this.file);
//return shpCorrect;
} else {
callback(true);
}

read multiple file inputs

This is the code I have for reading the first item in a file input, how can I iterate over all items inside this input?
function readFile (uploadControlId) {
if (!window.FileReader)
throw "The browser does not support HTML 5";
var element = document.getElementById(uploadControlId);
var def = new $.Deferred();
var file = element.files[0];
var parts = element.value.split("\\");
var fileName = parts[parts.length - 1];
var reader = new FileReader();
reader.onload = function (e) {
if (uploadControlId == 'uploadControlId'){
def.resolve(e.target.result, fileName);
} else {
def.resolve(e.target.result, fileName);
}
};
reader.onerror = function (e) {
def.reject(e.target.error);
};
reader.readAsArrayBuffer(file);
return def.promise();
}
I have tried something like:
angular.forEach(element.files, function (file){
})
But this doesn't work since the variables 'parts' and 'fileName' is from the variable 'element', so if I iterate over each file in element, they get 'undefined' fileName, this means they won't have like .txt or .pdf, so they are unreadable.
Update: This give no error, but only the last file gets uploaded:
function readFile (uploadControlId) {
if (!window.FileReader)
throw "The browser does not support HTML 5";
var def = new $.Deferred();
var element = document.getElementById(uploadControlId);
angular.forEach(element.files, function(file){
var fileName = file.name;
var reader = new FileReader();
reader.onload = function (e) {
def.resolve(e.target.result, fileName);
};
reader.onerror = function (e) {
def.reject(e.target.error);
};
reader.readAsArrayBuffer(file);
});
return def.promise();
}
My upload function:
$scope.UploadAttachment = function(){
readFile(uploadControlId).done(function (buffer, fileName) {
// logic to upload to server
}).fail(function (err) {
alert("error in reading file content");
});
};
Have you added the "multiple" attribute on the input tag?
By the way if you add this directive to your tag, an event will be fired with all files and you will handle that in you controller.
// Directive
(function(){
var Directive = function(){
return {
restrict: 'A',
scope : {},
link : function(scope, element, attrs){
element.bind('change', function(changeEvent){
scope.$emit('fileReader', changeEvent.target.files);
});
}
}
};
Directive.$inject = [];
app.directive('fileReader', Directive);
})();
// Controller
(function(){
var Controller = function($scope){
// Methods
function fileReader(files){
for(var iFile = 0, fileLen = files.length; iFile < fileLen; iFile = iFile + 1){
var file = files[iFile];
// Do something
}
}
// Events
$scope.$on('fileReader', function(event, files){
fileReader(files);
});
};
Controller.$inject = [
'$scope'
];
app.controller('MainCtrl', Controller);
})();

IndexedDB how to read element?

I am trying to save a key if it doesn't exists and if it does- just read it.
But it always alerts undefined.
var idb = window.indexedDB.open('MyDB', 1);
idb.onupgradeneeded = function(e)
{
var db = e.target.result;
if (!db.objectStoreNames.contains('all'))
{
db.createObjectStore('all');
}
}
idb.onsuccess = function(e)
{
db = e.target.result;
setData();
}
function setData()
{
var store = db.transaction(['all'], 'readwrite').objectStore('all');
var item1 = {theTitle: 'myKey', theValue: 'myValue'};
var op = store.get('myKey');
op.onsuccess = function(event)
{
alert(op.result);
}
op.onerror = function()
{
var req = store.add(item1, 1);
req.onsuccess = function()
{
alert('Saved');
}
}
}
IDBObjectStore will return undefined if it can't find anything, so your op.onsuccess function is actually working correctly.
See here: http://www.w3.org/TR/IndexedDB/#widl-IDBObjectStore-get-IDBRequest-any-key
You can place your "store.add" code in your onsuccess function:
var transaction = db.transaction(['all'], 'readwrite');
var store = transaction.objectStore('all');
var item1 = {
theTitle: 'myKey',
theValue: 'myValue'
};
var op = store.get('myKey');
op.onsuccess = function(event) {
if (op.result) {
alert(op.result);
} else {
var req = store.add(item1, 1);
req.onsuccess = function() {
alert('Saved');
}
}
}
transaction.oncomplete = function(event) {
console.log("Transaction complete. Everything is saved.")
}
Also look at the transaction.complete, onerror and onabort functions - they provide you with a better place to do dump all your error handling.
I would advise you to read the IDB spec: it's seems longwinded at first, but you get used to it and it's the best document there is on IDB.
http://www.w3.org/TR/IndexedDB/
Have fun!

Save image file in sdcard in firefox OS

I am trying to save an image in sdcard. I am following this documentation.
$('.btnSave').on('click', function () {
var imageRawData = canvas.toDataURL("image/png") ;
var sdcard = navigator.getDeviceStorage("sdcard");
var file = new Blob([imageRawData], { type: "image/png" });
var request = sdcard.addNamed(file, "FilertedImage.png");
request.onsuccess = function () {
var name = this.result;
console.log('File "' + name + '" successfully wrote on the sdcard storage area');
}
request.onerror = function (e) {
console.log('Unable to write the file: ' + e.target.error.name);
}
});
In the documentation I found that "pictures only accepts Blob with a valid image mime type". So how can I convert imageRawData to valid image mime type using javascript.
I have done it like the following - Saves and then shares:
function saveAndSend(blob) {
var sdcard = navigator.getDeviceStorage("sdcard");
var request = sdcard.addNamed(blob, "test/mycanvas.png");
//could just share the blob instead of saving
request.onsuccess = function () {
var sharingImage = new MozActivity({
name: "share",
data: {
type: "image/*",
number: 1,
blobs: [blob],
filenames: ["mycanvas.png"],
filepaths: ["test/mycanvas.png"]
}
});
}
// An error could occur if a file with the same name already exist
request.onerror = function () {
alert('Unable to write the file: ' + this.error.name);
}
}
var cnv = document.getElementById('myCanvas');
cnv.toBlob(function (blob) {
//var sdcard = navigator.getDeviceStorage("pictures");
var sdcard = navigator.getDeviceStorage("sdcard");
var request = sdcard.delete("test/mycanvas.png");
//try to delete in case it exists
request.onsuccess = function () {
saveAndSend(blob);
}
request.onerror = function () {
saveAndSend(blob);
}
});
Your app also need to make sure that it has the appropriate device storage permissions.
See: https://github.com/mozilla-b2g/gaia/blob/master/dev_apps/ds-test/manifest.webapp#L13 for an example. ds-test is a test app I wrote for testing things in device storage.

How to remove duplicate JavaScript code of DOM event handler?

I'm trying to remove duplicate JavaScript code. I have a page with many <input type="file">. Each loads an image and performs some distinct processing. The problem is that I have many duplicates of the following code:
inputFile1.onchange = function (e) {
var file = e.target.files[0];
if (typeof file == 'undefined' || file == null) {
return;
}
var imageType = /image.*/;
if (!file.type.match(imageType)) {
window.alert('Bad file type!');
return;
}
var reader = new FileReader();
reader.onloadend = function (e) {
var imageLoader = new Image();
imageLoader.onload = function () {
// process image
};
imageLoader.src = e.target.result;
};
reader.readAsDataURL(file);
};
inputFile2.onchange = ... (repeats all but process image)
inputFile3.onchange = ... (repeats all but process image)
Only the code at process image comment varies. How can I remove the surrounding duplicate code?
I know that JavaScript functions are objects. How can I define a function object and create one distinct instance for each event handler, passing a different function for process image to each object?
You can make a generator for such functions with a closure taking the individual callback as an argument:
function getChangeHandler(loadCallback) {
return function (e) {
var file = e.target.files[0];
if (typeof file == 'undefined' || file == null) {
return;
}
var imageType = /image.*/;
if (!file.type.match(imageType)) {
window.alert('Bad file type!');
return;
}
var reader = new FileReader();
reader.onloadend = function (e) {
var imageLoader = new Image();
imageLoader.onload = loadCallback; // <= uses the closure argument
imageLoader.src = e.target.result;
};
reader.readAsDataURL(file);
};
}
inputFile1.onchange = getChangeHandler(function() { /* custom process image */ });
inputFile2.onchange = getChangeHandler(function() { /* custom process image */ });
inputFile3.onchange = getChangeHandler(function() { /* custom process image */ });
An other, eventually superior approach would be to use only one change-event handler for all inputs, that dynamically chooses the custom image processor by the name or id of the input:
var imageProcessors = {
"box1": function() { … },
"anotherbox": function() { … },
…
};
function changeHandler(e) {
var input = this; // === e.target
…
reader.onloadend = function (e) {
…
imageLoader.onload = imageProcessors[input.id];
};
}
// and bind this one function on all inputs (jQuery-style):
$("#box1, #anotherbox, …").click(changeHandler);
You can write a function that returns a function:
function processFile(callback) { //callback is the unique file processing routine
return function(e) {
var file = e.target.files[0];
if (typeof file == 'undefined' || file == null) {
return;
}
var imageType = /image.*/;
if (!file.type.match(imageType)) {
window.alert('Bad file type!');
return;
}
var reader = new FileReader();
reader.onloadend = function (e) {
var imageLoader = new Image();
imageLoader.onload = callback; //Put it here!
imageLoader.src = e.target.result;
};
reader.readAsDataURL(file);
};
}
Then call like this:
inputFile1.onchange = processFile(function() {
//file processing for number 1
});
inputFile2.onchange = processFile(function() {
//file processing for number 2
});
inputFile3.onchange = processFile(function() {
//file processing for number 3
});
Here's an EMCA5 solution, just to throw it into the mix. It binds a dynamic event callback depending on the element.
I've assumed each field has an ID (input1 etc) but with some modification of the code (i.e. identifying the trigger element by some other means) this wouldn't be necessary.
Array.prototype.slice.call(document.querySelectorAll('input[type=file]')).forEach(function(element) {
/* prepare code specific to the element */
var input_specific_code = (function() {
switch (element.id) {
case 'input1': return function() { /* #input1 code here */ };
case 'input2': return function() { /* #input2 code here */ };
case 'input3': return function() { /* #input3 code here */ };
}
})();
element.addEventListener('change', (function(input_specific_code) { return function(evt) {
var id_of_trigger_input = element.id;
/* common code here */
/* element-specific code */
input_specific_code();
/* continuation of common code */
}; })(input_specific_code), false);
});

Categories

Resources