Upload Files using Awesome Uploader


Problem Statement
ExtJS already comes up with the component for uploading a file. A simple code as shown below allows you to make use of filefield to be able to achieve file upload specific need:

Ext.create('Ext.form.Panel', {
title: 'Upload a Photo',
width: 400,
bodyPadding: 10,
frame: true,
renderTo: Ext.getBody(),
items: [{
xtype: 'filefield',
name: 'photo',
fieldLabel: 'Photo',
labelWidth: 50,
msgTarget: 'side',
allowBlank: false,
anchor: '100%',
buttonText: 'Select Photo...'
}],

buttons: [{
text: ‘Upload’,
handler: function() {
// Form upload related validations and code
}
}
}]
});

After clicking on the Browse (select photo) button you get an opportunity to select a file (photo):

While it works alright for the single file selection and upload, the moment you have a need for multiple file upload, you have to explore for the custom components / plugins.

Andrew has come up with Awesome Uploader to solve the above mentioned limitation in a great way. Using awesome uploader you can of course do what you could do using the standard file uploader and in addition you can:
1. Drag-and-drop facility to drop files from the file system into the upload area
2. Multiple File Selection
3. Upload Progress
4. Custom Limits for File Size and Number of Uploads
5. Cancelling upload process for specific file
6. Cancelling upload for all the files
.
.
And many more …

Following is a sample screen of Awesome Uploader from Andrew’s demo example

While the awesome uploader was definitely awesome, it was not compatible with the latest version of ExtJS. Also, it didn’t have capability to indicate that the files shall be uploaded as a zip file on the server. We have bridged that gap and this is what we intend to address in this blog.

Pre-requisite
• Download the demo version of awesome uploader
• Download extjs 4.1 SDK
• It is assumed that you have complete understanding of Sencha’s VMC architecture. In any case, visiting the following link will help you in understanding this blog better:
http://docs.sencha.com/ext-js/4-1/#!/guide/application_architecture
• Understanding of existing Awesome Uploader code (list of files shown in following figure):

How to do this?
Create a web project by following the ExtJS MVC guidelines and ensure that your directory structure looks similar to as shown in the below image:

Now that you have the basic directory structure created for your models, views and components, you need to place the source files into the corresponding directory. However, before you jump, copy the ExtJs 4.1 SDK inside your application directory.

Put the following content in your index.html:

Here you need to ensure that you are pointing to current version of extjs.

Put the following in your app.js:

Ext.require([
'Ext.direct.*' , 'Ext.util.Cookies', 'Ext.TaskManager', 'Ext.window.MessageBox'
]);
Ext.application({
name: 'AWP',
autoCreateViewport: true,
// All the paths for custom classes
paths: {
'Ext': 'ext4.1/src/',
'Ext.ux': 'ext4.1/examples/ux/'
},
launch: function() {
},
// Define all the controllers that should initialize at boot up of your application
controllers: [
'Main'
]
});

Put the following code in Viewport:

Ext.define('AWP.view.Viewport', {
extend: 'Ext.container.Viewport',
requires:['AWP.view.AwesomeUploader'],
initComponent:function(){
this.items = [{
xtype:'awesomeuploader',
awesomeUploaderRoot:'app/view/',
autoUpload:false,
uploadPath:'/tmp/testupload',
optionalZip : true
}]
this.callParent( arguments );
}
});

Note that awesomeUploaderRoot is pointing to the same path with as the view port class. However, you can decide to keep them into separate folder, you can do that as well.

Put the following piece of code in your controller (Main.js):

Ext.define('AWP.controller.Main', {
extend: 'Ext.app.Controller',
views:['AwesomeUploader'],
models:['AwesomeModel'],
stores:['AwesomeStore']
});

Put the following piece of code in your Model (AwesomeModel.js)
Ext.define('AWP.model.AwesomeModel',{
extend:'Ext.data.Model',
fields:['id', 'name', 'size', 'status', 'progress']
});

Put the following piece of code in your Store (AwesomeStore.js)
Ext.define('AWP.store.AwesomeStore',{
extend :'Ext.data.Store',
requires:['AWP.model.AwesomeModel'],
model:'AWP.model.AwesomeModel',
autoLoad:false
});

Make code changes in AwesomeUploder.js to make it compliant with ExtJS 4.1

  • Remove name space declaration Ext.ns(‘Ext.ux’);
  • Replace standard way of extending an existing class (Ext.ux.AwesomeUploader = Ext.extend(Ext.Container, {) to new way, which is:

Ext.define('AWP.view.AwesomeUploader', {
extend:'Ext.Panel',
requires:['Ext.XTemplate','Ext.ux.form.FileUploadField','AWP.ux.XHRUpload','AWP.store.AwesomeStore'],
alias:'widget.awesomeuploader'
,bodyCls:'upload-popup-css'
,swfUploadItems:[]
,dndUploadItems:[]
,autoUpload:false
,dndTotalUploadItems:0
,swfTotalUploadItems:0
,extraPostData:{}
,doLayout:function(){
AWP.view.AwesomeUploader.superclass.doLayout.apply(this, arguments);
}
....

}

  • Also, review the methods of this class (shown in below image)

Make the Ext.ux.XHRUpload.js code compliant with ExtJS 4.1
Replace the code of this file by following code:

Ext.define('AWP.ux.XHRUpload',{
extend:'Ext.util.Observable',
constructor:function(config){

Ext.apply(this, config, {
method: ‘POST’
,fileNameHeader: ‘X-File-Name’
,filePostName:’fileName’
,contentTypeHeader: ‘text/plain; charset=x-user-defined-binary’
,extraPostData:{}
,xhrExtraPostDataPrefix:’extraPostData_’
,sendMultiPartFormData:false
});
this.addEvents( //extend the xhr’s progress events to here
‘loadstart’,
‘progress’,
‘abort’,
‘error’,
‘load’,
‘loadend’
);
AWP.ux.XHRUpload.superclass.constructor.call(this);

},
send:function(config){
Ext.apply(this, config);

this.xhr = new XMLHttpRequest();
this.xhr.addEventListener(‘loadstart’, Ext.Function.bind(this.relayXHREvent, this), false);
this.xhr.addEventListener(‘progress’, Ext.Function.bind(this.relayXHREvent, this), false);
this.xhr.addEventListener(‘progressabort’, Ext.Function.bind(this.relayXHREvent, this), false);
this.xhr.addEventListener(‘error’, Ext.Function.bind(this.relayXHREvent, this), false);
this.xhr.addEventListener(‘load’, Ext.Function.bind(this.relayXHREvent, this), false);
this.xhr.addEventListener(‘loadend’, Ext.Function.bind(this.relayXHREvent, this), false);

this.xhr.upload.addEventListener(‘loadstart’, Ext.Function.bind(this.relayUploadEvent,this), false);
this.xhr.upload.addEventListener(‘progress’, Ext.Function.bind(this.relayUploadEvent,this), false);
this.xhr.upload.addEventListener(‘progressabort’, Ext.Function.bind(this.relayUploadEvent,this), false);
this.xhr.upload.addEventListener(‘error’, Ext.Function.bind(this.relayUploadEvent,this), false);
this.xhr.upload.addEventListener(‘load’, Ext.Function.bind(this.relayUploadEvent,this), false);
this.xhr.upload.addEventListener(‘loadend’, Ext.Function.bind(this.relayUploadEvent,this), false);

this.xhr.open(this.method, this.url, true);

if(typeof(FileReader) !== ‘undefined’ && this.sendMultiPartFormData ){
//currently this is firefox only, chrome 6 will support this in the future
this.reader = new FileReader();
this.reader.onload= Ext.Function.bind(this.sendFileUpload,this, false);
this.reader.readAsBinaryString(this.file);
return true;
}
//This will work in both Firefox 1.6 and Chrome 5
this.xhr.overrideMimeType(this.contentTypeHeader);
this.xhr.setRequestHeader(this.fileNameHeader, this.file.name);
for(attr in this.extraPostData){
this.xhr.setRequestHeader(this.xhrExtraPostDataPrefix + attr, this.extraPostData[attr]);
}
this.xhr.setRequestHeader(‘X-File-Size’, this.file.size); //this may be useful
this.xhr.send(this.file);
return true;

}
,sendFileUpload:function(){

var boundary = (1000000000000+Math.floor(Math.random()*8999999999998)).toString(),
data = ”;

data += ‘–‘+boundary + ‘\r\nContent-Disposition: form-data; name=”‘ + this.filePostName + ‘”; filename=”‘ + this.file.name + ‘”\r\nContent-Type: ‘+this.file.type+’\r\nContent-Transfer-Encoding: base64\r\n\r\n’ + window.btoa(this.reader.result) + ‘\r\n’+’–‘+boundary+’–\r\n\r\n’;

this.xhr.setRequestHeader(this.fileNameHeader, this.file.name);

for(attr in this.extraPostData){
this.xhr.setRequestHeader(this.xhrExtraPostDataPrefix + attr, this.extraPostData[attr]);
}
this.xhr.send(data);
}
,relayUploadEvent:function(event){
this.fireEvent(‘upload’+event.type, event);
}
,relayXHREvent:function(event){
this.fireEvent(event.type, event);
}
});

Other things to review
While you have been able to setup the UI side, you may also need to play with following files to save files on the web server and implement business rules around those uploaded files
• upload.php and
• xhrupload.php

For example we had to change file size limit to allow upload of larger files. Also, we had to change code to create certain directories (if they didn’t already exist) based on the business need.

Also, move the images files and relevant CSS files inside following files (and make the required changes)
• app\resources\images
• app\resources\css\AwesomeUploader.css
• app\resources\css\Ext.ux.form.FileUploadField.css

Once you are done with above set of changes, you shall have the awesome uploader up and running. Following is a sample image from the demo app built using ExtJS 4.1:

Summary
The purpose of this article was to guide ExtJS users to be able to make use of the previous work done on the Awesome Uploader. I hope you found this article helpful and it enables you to make use of tree panel to solve similar business problem. In case you need any professional support on Sencha products like ExtJS and Sencha Touch do visit our website and get in touch with us.

Reference
http://docs.sencha.com/ext-js/4-1/#!/api/Ext.form.field.File
http://jsjoy.com/blog/ext-js-extension-awesome-uploader
http://code.google.com/p/swfupload/
http://demo.swfupload.org/v220/speeddemo/index.php

Tagged with: , ,
Posted in Sencha ExtJS
15 comments on “Upload Files using Awesome Uploader
  1. Kalpana says:

    After trying all methods mention here, Browse button is not coming. I am not using php,.net. I am using java servlet for file upload

  2. Kalpana says:

    can you please send demo code? And also is this api uploader send file in chunk to the server?

  3. Hi, could you please email or publish AwersomeUploader.js code with extjs 4.1 supported?

  4. DHKim says:

    Hello, your post is very useful, could you send the source code to me?

  5. Can you just upload the source for download? I will make sure it gets onto the code.google page as many users are interested in an extjs 4 version of this control. Or email it to me on craig.whitcombe@gmail.com

  6. cartouche699 says:

    hello,

    does it work with ExtJs 2.1? Or do you know a “plugin” i can use to have multi files upload?

    Thank you

  7. happy says:

    Put the following code in Viewport: ? WHAT CODE???

    • Walking Tree says:

      Thanks for pointing out this. You shall be able to see the viewport code as well.

    • davidmaclean441 says:

      Hey thank you.

      This is a test message.

      What will it cost me to get this working asap …?

      • Walking Tree says:

        Are you facing any specific issue?

      • davidmaclean441 says:

        A number of small ones.

        I’m using the latest (extjs 421a). I’m not an extjs expert with a lot of experience.

        1. Can’t be sure I have picked up the latest/best source for AwesomeUploader, I had to change “delegate” to “bind”, there is no zip option. Hard to be sure I am working with what you are/had.

        2. Setting up the store is not shown. I don’t have the background to fix it easily.

        3. The SWF+button image are loading but the button doesn’t display. I verified as much as I could then decided to try the standard upload instead. There I ran into issues with the form field ux thing. I would have to spend more time there to come up with the right question.

        4. My IDE (phpstorm) is angry about ‘{value} %’. I’d have to go back and check the exact error, I just replaced it with something simpler for now.

        There may be more, I only spent an hour or so with it. Your post is helpful to get started but not sufficient for guys at my level of experience with extjs. I will get it eventually by tinkering, or I may start from scratch with my own grid panel and add what I understand and can get working.

        However I have some pressure to get it done.

        I have to leave the office now. Back later today and working through the weekend. I attach the script so far. Search for “dsm 30 nov” to see the problem areas for me.

        It may be you can coach me or send a working script. Let me know how you want to proceed.

        And thank you for your response.

  8. steward says:

    Put the following code in Viewport: ?? Missing code.
    Also no date on this post.

  9. TKFan says:

    Hello, your post is very useful, could you send the source code to me?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

We Have Moved Our Blog!

We have moved our blog to our company site. Check out https://walkingtree.tech/index.php/blog for all latest blogs.

Sencha Select Partner Sencha Training Partner
Xamarin Authorized Partner
Do More. With Sencha.

Recent Publication
%d bloggers like this: