Is there a way to use Model.set and Model.save in a way that will force Backbone to send the data to the server as a file (as if you were submitting a form with an <input type="file"> tag?
The short answer is no. The long answer is, sort of.
This doesn't really have anything to do with Backbone and instead is about being able to AJAX a file in the browser. The solution is to use the File API from HTML 5. Below is an example of how you would do that with a Backbone model.
Let's assume we have a User model and we want to save an avatar file on that model.
// Backbone Model JS
User = Backbone.Model.extend({
readAvatar : function (file, callback) {
var reader = new FileReader(); // File API object for reading a file locally
reader.onload = (function (theFile, self) {
return function (e) {
// Set the file data correctly on the Backbone model
self.set({avatar_file_name : theFile.name, avatar_data : fileEvent.target.result});
// Handle anything else you want to do after parsing the file and setting up the model.
callback();
};
})(file, this);
reader.readAsDataURL(file); // Reads file into memory Base64 encoded
}
});
// Somewhere in your view JS
this.$("input[type='file']").change(function (event) {
var file = e.originalEvent.target.files[0];
userModel.readAvatar(file, userModel.save);
});
// HTML
<form><input type="file" name="avatar"/></form>
Now on your back end you need to handle the file coming through as Base64 encoded data.
A couple of warnings:
If you need extensive browser support then this solution probably won't work for you.
Base64 encoding a file is going to increase the amount of data sent over the wire by about 30%.
Related
I am working on a form set for a client. In a nutshell:
The forms are filled out by my client’s customers by selecting different options on each form.
Each form can have multiple instances, depending on the customer.
At the end of the process, the customer can opt to either sign one or all the forms digitally or decline to sign them digitally and at the end of the process the forms are printed out and signed manually.
To accomplish this, I’ve created a signature plugin written in jQuery. Once the customer fills out the forms, they are presented each form separately. To sign the form digitally they simply tap (click) the signature block, a dialog with a canvas element appears, they sign the form and save it, the signature appears in the form, and they move on to the next form.
Here is the portion of the code that stores the completed signature and adds the image to the form:
$.sig = {
signatures: {},
}
function signatureSave() {
var canvas = document.getElementById("sigcanvas"),
dataURL = canvas.toDataURL("image/png");
document.getElementById($.sig.target).src = dataURL;
$.sig.signatures[$.sig.target].url = dataURL;
$.sig.signatures[$.sig.target].hasSignature = true;
};
The function is only called if the signature is saved, if there is no signature, the $.sig.signatures[$.sig.target].hasSignature remains false and the system skips the object.
This all works as intended, almost.
My problem lies in the process used to save the form information. If the customer does not sign any forms digitally the form information is simply saved and the forms are printed out, no need to save any signatures.
If the customer signs at least one form, though, the signatures must be sent to the server using the FormData() object.
I’ve used the FormData object in other projects for the client successfully, but only when the customer uploads one or more images to the browser using the input file element. It’s a pretty simple process because the resulting images have a img.file property that I send to the server.
Not so with a canvas object. All I get is the .src property, an any attempt to use anything from the resulting .png image that is created in the function above results in either a “cannot use a blob” or some other error.
Now I know if I have a single image, I can send it using AJAX with the following:
$.ajax({
type: "POST",
url: "script.php",
data: {
imgBase64: dataURL
}
})
Problem is that I am sending from one to x number of signatures.
Edit: I forgot to add this in. This is the function that is supposed to create the FormData object used to send the signatures to the server (and where my problem lies):
function getUploadData() {
var upl = new FormData();
$.each($.sig.signatures, function (e, u) {
if (u.hasSignature == true && u.url != null) {
var im = new Image();
im.src = u.url;
upl.append(u.target, im, u.target + '.png');
}
})
return upl;
}
I've tried all the tricks and nothing is working. The var im = new Image(); as well as the following line are just my latest ill fated attempt.
Picture perfect would be the ability to save the image information in the $.sig.signatures object so I can simply loop through any signatures that are signed, add them as elements of the FormData object, and then send the FormData object as the data for the AJAX call. As stated before, I use this method in other projects and works fine.
Does anyone know a way to do this?
Please note:
The server-side AJAX processor functions correctly.
The signature process works correctly (customer signs canvas, signature is displayed, signature information is stored).
All I need is how to send multiple images created using the canvas element in a FormData object to the server.
I know the answer is staring me right in the face, but I am just not getting it. Any hints or suggestions would be greatly appreciated!
Edit: Just a note. I've searched the entire afternoon for this and have found entries that either deal with sending multiple files using FormData and AJAX - but the files are uploaded to the browser (not created using Canvas), or single files sent to the server that are created using Canvas, but nothing about sending multiple files sent using FormData and AJAX that are created using Canvas. Oje!
As stated, the answer was staring me in the face, but I didn't see it because was looking behind the wrong door. FormData has nothing to do with it (Homer Dope Slap!).
Since I already have the data stored in $.sig.signature for each signature, I just need to send the information to the server as the data in the AJAX function. I updated my function above as shown:
function getUploadData() {
var upl = {};
$.each($.sig.signatures, function (e, u) {
if (u.hasSignature == true && u.url != null) {
upl[e] = u.url;
}
})
return upl;
}
Since the form information is sent as JSON I just add the signature info to the object that contains the form information, JSON.stringify it and send it on its way. This should work because the information retrieved above are strings.
Server side will look something like this:
$info = json_decode( $_POST['info'] );
// Various validation routines and checks
foreach( $info->signatures as $sig=>$data ):
$data = str_replace('data:image/png;base64,', '', $data);
$data = str_replace(' ', '+', $data);
$img = base64_decode($data);
// Do some processing, file naming, database saving and other general dodads
$success = file_put_contents( $file, $img );
endforeach;
The above function is still concept, I am reworking some of the code but this should work.
Credit is given to this post for opening my eyes:
post sending base64 image with ajaxpost sending base64 image with ajax
So question answered and yeah, I deserve a dope slap, but all comes out right in the end.
CAVEAT: Works like a charm.
I'm new to angularJS. I want to upload a JSON file using
<input type="file"onchange="angular.element(this).scope().uploadFile(this.files)"/>
and I am trying to access its data in the controller like this:
$scope.uploadFile = function(files) {
$scope.jsonData = files[0].data;
};
But I get an undefined in my variable. I have to use this variable to populate various other fields on the website. Kindly help me how to access the data of the input file uploaded.
You need use FileReader API.
The FileReader object lets web applications asynchronously read the contents of files (or raw data buffers) stored on the user's computer.
Its method FileReader.readAsText()
The readAsText method is used to read the contents of the specified Blob or File.
Note: Here is an example, It works in Modern browsers
$(document).ready(function() {
$('#file').change(function(e) {
var reader = new FileReader();
reader.onload = function(e) {
console.log(e.target.result);
//if you want in JSON use
//var json = JSON.parse(e.target.result)
}
reader.readAsText(this.files[0]);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="file" id="file" name="file" />
Just Another way to go around... if you don't want to use the FileReader and manually parse the text using JSON.parse and also wrapping it around a try/catch in case of wrong format
// simulate a file `file = files[0]`
var file = new Blob(['{"hello": "world"}'])
new Response(file).json().then(
json => console.log(json),
err => console.error("invalid json data")
)
Or with the help of Screw-FileReader
// simulate a file `file = files[0]`
var file = new Blob(['{"hello": "world"}'])
file.json().then(json => console.log(json))
<script src="https://cdn.rawgit.com/jimmywarting/Screw-FileReader/master/index.js"></script>
(Posted solution on behalf of the OP).
Thank you #Satpal I solved it using fileReader.readAsText() and JSON.parse().
I am trying to Upload a CSV File containing IP addresses using Ext-js and have it processed and converted into a JSON Object on the client-side before sending it to the server to be added to the repository.
I need to parse the CSV, convert it into a JSON Object and then add the information to a Data Store- but I'm terribly stuck.
Are there any existing libraries that can help achieve this?
I am looking at avoiding jQuery/HTML5. Is there a way we can do it exclusively using Javascript / Extjs?
I tried going through a lot of examples- but most of them use jQuery/HTML5 API/ Papa-Parse or prefer doing it server side.
My 'Upload' Button handler function looks like this:
handler: function () {
var file = Ext.getCmp('form-file-win').getEl().down('input[type=file]').dom.files[0];
var reader = new FileReader();
var importedAddresses=[];
reader.onload = function (oFREvent) {
importedAddresses=oFREvent.target.result;
};
reader.readAsText(file);
this.dataStore.add({
id: '',
name: 'Imported from CSV',
});
win.close();
}
Unfortunately there is no way to access the content of the file on the client side.
You have two options:
You either post the file to the server and process it serves-side and give a json response for your javascript
Or you need to use HTML5 to access a local file. Here is a tutorial on how to do that: local file processing
I have two JS files.
First file contains a function obtains some data (particularly, my function gets state of AbstractJavaScriptComponent which is just a String, but I think it's not important).
And I want to send this data to another JS file (or make it accessible in that file in any way).
com_company_htmlcontent_web_screens_JsData = function() {
// this value is what I want to send
document.getElementById('myButton').value = this.getState().xhtml();
this.onStateChange = function() {
e.innerHTML = this.getState().xhtml();
alert("on State changed is called"); // but this inner function is not called
}
}
Are there any ways to make it possible?
Can you just send it with GET? Just pass the thata in URL that will switch you to the other file with js in it, then grab the data from it.
I don't really see the point of that action tbh. Can you state, what you want to archive?
If you just want to run some script that doing something with data given, and you dont want to change files you can send this data with Ajax.
I want to unzip the zipped file while downloading the file using AngularJS.
I am trying with the following code,
For eg:
<a download="My_File.PNG" target="_self" ng-href="Path of the zip file/My_File.PNG.zip">My_File.PNG</a>
How can I download the actual file My_File.PNG from My_File.PNG.zip in the browser?
You can't do it from angular directly, but you can call use the JSUnzip library which you can call from your angular controller https://github.com/augustl/js-unzip
var myZip = ... // Get it with an XHR request, HTML5 files, etc.
var unzipper = new JSUnzip(myZip);
unzipper.isZipFile(); // true or false
unzipper.readEntries(); // Creates "entries"
unzipper.entries; // Array of JSUnzip.ZipEntry objects.
It's a little bit late, but the problem is in the angular request. You have to pass a special parameter, as in
$http.get('...', { responseType: "arraybuffer" }).success(successfunc);
and then proceed with the unzip. Search for this...