backbone-model-file-upload/backbone-model-file-upload.js
Alejandro 48b68bdc69 handle FileList objects
Recurse FileList objects from file inputs with multiple files and add all the files, this will generate an array on the server when getting the fileattribute key.

I use this in Flask (python) as an example:
files = request.files.getlist('files')

Which returns when using fileAtrribute: 'files':
[<FileStorage: u'file1.png' ('image/png')>, <FileStorage: u'file2.png' ('image/png')>]
2014-09-09 15:28:02 -03:00

139 lines
4.3 KiB
JavaScript

// Backbone.Model File Upload v0.4
// by Joe Vu - joe.vu@homeslicesolutions.com
// For all details and documentation:
// https://github.com/homeslicesolutions/backbone-model-file-upload
// Contributors:
// lutherism - Alex Jansen - alex.openrobot.net
// bildja - Dima Bildin - github.com/bildja
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['underscore', 'backbone'], factory);
} else {
// Browser globals
factory(_, Backbone);
}
}(this, function(_, Backbone){
// Clone the original Backbone.Model.prototype
var backboneModelClone = _.clone( Backbone.Model.prototype );
// Extending out
_.extend(Backbone.Model.prototype, {
// ! Default file attribute - can be overwritten
fileAttribute: 'file',
// @ Save - overwritten
save: function(key, val, options) {
// Variables
var attrs, attributes = this.attributes;
// Signature parsing - taken directly from original Backbone.Model.save
// and it states: 'Handle both "key", value and {key: value} -style arguments.'
if (key == null || typeof key === 'object') {
attrs = key;
options = val;
} else {
(attrs = {})[key] = val;
}
// Validate & wait options - taken directly from original Backbone.Model.save
options = _.extend({validate: true}, options);
if (attrs && !options.wait) {
if (!this.set(attrs, options)) return false;
} else {
if (!this._validate(attrs, options)) return false;
}
// Merge data temporarily for formdata
var mergedAttrs = _.extend({}, attributes, attrs);
if (attrs && options.wait) {
this.attributes = mergedAttrs;
}
// Check for "formData" flag and check for if file exist.
if ( options.formData === true
|| options.formData !== false
&& mergedAttrs[ this.fileAttribute ]
&& mergedAttrs[ this.fileAttribute ] instanceof File
|| mergedAttrs[ this.fileAttribute ] instanceof FileList
|| mergedAttrs[ this.fileAttribute ] instanceof Blob ) {
// Flatten Attributes reapplying File Object
var formAttrs = _.clone( mergedAttrs ),
fileAttr = mergedAttrs[ this.fileAttribute ];
formAttrs = this._flatten( formAttrs );
formAttrs[ this.fileAttribute ] = fileAttr;
// Converting Attributes to Form Data
var formData = new FormData();
_.each( formAttrs, function( value, key ){
if (value instanceof FileList) {
_.each(value, function(file) {
formData.append( key, file );
});
return;
}
formData.append( key, value );
});
// Set options for AJAX call
options.data = formData;
options.processData = false;
options.contentType = false;
// Apply custom XHR for processing status & listen to "progress"
var that = this;
options.xhr = function() {
var xhr = $.ajaxSettings.xhr();
xhr.upload.addEventListener('progress', function(){
that._progressHandler.apply(that, arguments);
}, false);
return xhr;
}
}
// Resume back to original state
if (attrs && options.wait) this.attributes = attributes;
// Continue to call the existing "save" method
return backboneModelClone.save.call(this, attrs, options);
},
// _ FlattenObject gist by "penguinboy". Thank You!
// https://gist.github.com/penguinboy/762197
_flatten: function( obj ) {
var output = {};
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
if (typeof obj[i] == 'object') {
var flatObject = this._flatten(obj[i]);
for (var x in flatObject) {
if (!flatObject.hasOwnProperty(x)) continue;
output[i + '.' + x] = flatObject[x];
}
} else {
output[i] = obj[i];
}
}
return output;
},
// _ Get the Progress of the uploading file
_progressHandler: function( event ) {
if (event.lengthComputable) {
var percentComplete = event.loaded / event.total;
this.trigger( 'progress', percentComplete );
}
}
});
}));