Tuesday, May 15, 2012

File upload widget for ExtJS 4.x

In a project I'm working on, I needed a flexible file upload panel with multiple file upload support. Unfortunately, the ExtJS built-in mechanisms (Ext.form.field.File) does not support multiple upload and there are some other limitations as well. I searched for a user contributed plugin, but I couldn't find a suitable one. Most of the contributions were written for older versions of ExtJS - 3.x or even 2.x. So I decided to write one myself, trying to use some advanced features of HTML5.

You may first check the live demo.

I had to focus on two main aspects:
  • user interface - the way it looks like and interacts with the user (dialogs, grids, buttons, status bars, ...)
  • data transport - the upload itself, how to transport data to the server
In my case, the first aspect was very much influenced by the fact, that I needed the widget to fit the whole application. So perhaps the layout won't suit everyone. I'm planing to improve the flexibility of the user interface in the future.

Regarding the second aspect - the transport method - I wanted to separate the implementation in a way, that will allow easy deployment of alternative methods in the future. For the time being I implemented just one method - raw PUT/POST with the following features:
  • uses the standard ExtJS mechanisms - Ext.data.Connection and its XMLHttpRequest
  • doesn't require hidden iframes, flash objects or other workarounds - it's just plain AJAX
  • sends data in the body of the request and metadata (filename, size, type) in the headers
  • provides upload progress data
  • allows uploading of really large files (tested with a 2GB file - works best in Chrome, Mozilla Firefox had problems, because it apparently tries to read the whole file into the memory first)
You may try the initial version available on github. For now, it's not as flexible as I would like it to be, but perhaps it will be useful for someone. To be able to use it in your application you just need to clone the repository somewhere on your system and add the path with the prefix to Ext.Loader:
Ext.Loader.setPath({
    'Ext.ux.upload' : '/my/path/to/extjs-upload-widget/lib/upload'
});
And then, you can use the dialog:
var dialog = Ext.create('Ext.ux.upload.Dialog', {
    dialogTitle: 'My Upload Widget',
    uploadUrl: 'upload.php'
});

dialog.show();
For more options you can check the API docs.

Links:

58 comments:

  1. Hey, Nice Widget....
    do you have same with Extjs 3.4

    aMaN

    ReplyDelete
    Replies
    1. Maybe I have some old code, which I wrote for my project, but I'm not sure if it will work as a standalone extension.

      Delete
  2. How do I use this widget. I tried it for my file upload can not upload with PUT method. please help me

    ReplyDelete
    Replies
    1. Maybe your server does not support PUT. Try the upload.php example to find out.

      Delete
    2. I've used the example that you created sample code as below. but it will not run secerip liking.

      $mimeType = $_SERVER['HTTP_X_FILE_TYPE'];
      $size = $_SERVER['HTTP_X_FILE_SIZE'];
      $fileName = $_SERVER['HTTP_X_FILE_NAME'];

      $fp = fopen('php://input', 'r');
      $realSize = 0;
      $data = '';

      if ($fp) {
      while (! feof($fp)) {
      $data = fread($fp, 1024);
      $realSize += strlen($data);
      }
      } else {
      _response(false, 'Error saving file');
      }

      _response();

      //---
      function _log ($value)
      {
      error_log(print_r($value, true));
      }


      function _response ($success = true, $message = 'OK')
      {
      $response = array(
      'success' => $success,
      'message' => $message
      );

      echo json_encode($response);
      exit();
      }

      and how should I use your widget so that I can retrieve data that is sent from EXTJS4 use php sekerip below :


      move_uploaded_file($_FILES['file']['tmp_name'].$_FILES['file']['name'], 'mydirectory/'.$_FILES['file']['name']);


      thanks for helping me

      Delete
    3. To actually save uploaded data, you have to save data obtained through the fread() function into some file, for example:

      $targetFile = fopen('/tmp/' . $fileName, 'w');
      while (! feof($fp)) {
      $data = fread($fp, 1024);
      $realSize += strlen($data);

      fwrite($targetFile, $data);
      }

      You cannot use the $_FILES array, because this is not actually a file upload handled. Data are sent through an AJAX call.

      Delete
  3. It is my sampel output in my localhost

    Request URL:http://localhost/web/ta-yii/index.php/user/manager/upload
    Request Method:PUT
    Status Code:200 OK
    Request Headersview source
    Accept:*/*
    Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
    Accept-Encoding:gzip,deflate,sdch
    Accept-Language:en-US,en;q=0.8
    Connection:keep-alive
    Content-Length:561276
    Content-Type:application/x-www-form-urlencoded
    Host:localhost
    Origin:http://localhost
    Referer:http://localhost/web/ta-yii/index.php/user
    User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5
    X-File-Name:Lighthouse.jpg
    X-File-Size:561276
    X-File-Type:image/jpeg
    X-Requested-With:XMLHttpRequest
    Response Headersview source
    Connection:Keep-Alive
    Content-Length:16
    Content-Type:text/html
    Date:Sun, 17 Jun 2012 16:20:40 GMT
    Keep-Alive:timeout=5, max=100
    Server:Apache/2.2.21 (Win32) PHP/5.3.10 mod_python/3.3.1 Python/2.4.3
    X-Powered-By:PHP/5.3.10

    ReplyDelete
  4. This comment has been removed by the author.

    ReplyDelete
  5. Great Work! I have a question... I would to change PUT in POST (I do it jet changing method attribute in AbstractUploader.js) but I would also send file's content in POST, so my question is: How I can file's content? Here my code in ExtJsUploader :

    uploadItem : function(item) {
    var file = item.getFileApiObject();
    if (!file) {
    return;
    }

    item.setUploading();

    this.conn = this.initConnection();

    this.conn.request({
    scope : this,
    headers : this.initHeaders(item),
    // xmlData : file,
    params: {
    filecontent: file // THIS NOT WORKING
    },

    ReplyDelete
  6. How to set the file upload location in the server ?

    ReplyDelete
    Replies
    1. You just need to save the raw POST data into a file. The name of the uploaded file is contained in the HTTP headers.

      Delete
  7. Hi,
    I do have problem with your extjs-upload-widget (great library BTW) under windows 7 and possibly windows vista using both chrome and firefox.
    Problem occurred in my application, but the same problem seems to exists in demo on: http://debug.cz/demo/upload/
    Under linux everything works fine.

    Under windows uploading anything ends in connection broken with HTTP code 400 on server side.

    Below are headers send by browser.
    Response is empty, php script working as a backend is never used. It seems that connection is broken on browser side.
    Progress bar stops at 15 or 29% percent.


    Request URL:http://debug.cz/demo/upload/upload.php

    Content-Type:application/x-www-form-urlencoded
    Origin:http://debug.cz
    Referer:http://debug.cz/demo/upload/
    User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.75 Safari/537.1
    X-File-Name:Koala.jpg
    X-File-Size:780831
    X-File-Type:image/jpeg
    X-Requested-With:XMLHttpRequest

    Thats all. No further headers are sent, i.e. no "Content-length" is set.

    Under linux (different file):

    Accept:*/*
    Accept-Charset:UTF-8,*;q=0.5
    Accept-Encoding:gzip,deflate,sdch
    Accept-Language:en,en-US;q=0.8,pl;q=0.6
    Connection:keep-alive
    Content-Length:219173
    Content-Type:application/x-www-form-urlencoded
    Host:debug.cz
    Origin:http://debug.cz
    Referer:http://debug.cz/demo/upload/
    User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.19 (KHTML, like Gecko) Ubuntu/12.04 Chromium/18.0.1025.168 Chrome/18.0.1025.168 Safari/535.19
    X-File-Name:1_i2e.jpg
    X-File-Size:219173
    X-File-Type:image/jpeg
    X-Requested-With:XMLHttpRequest


    Do you have an idea what can cause the problem? Missing headers? I've tried to set them by hand, but get blocked by security restrictions:

    Refused to set unsafe header "Accept-Charset"
    Refused to set unsafe header "Accept-Encoding"
    Refused to set unsafe header "Connection"
    Refused to set unsafe header "Content-Length"

    ReplyDelete
    Replies
    1. Hi Adam,
      I tested the demo briefly with Chrome 21 and Firefox 12 on Windows 7. When uploading small files, everything was fine. But when uploading larger files, I got the error you described.

      I found two problems:

      1) The default connection timeout is too short - 6 seconds, it should be much more - so there is not enough time to upload the whole file.

      2) The connection timeout error is not displayed, so it is not obvious what has happend.

      I filed issues for both problems on GitHub and I'll try to resolve them ASAP:
      https://github.com/ivan-novakov/extjs-upload-widget/issues

      In the time being, you could try to increase the connection timeout, by setting the "uploadTimout" property:
      http://debug.cz/demo/upload/docs/#!/api/Ext.ux.upload.Dialog-cfg-uploadTimeout

      Delete
  8. Hi Ivan,
    Thank you for the suggestion. Rising timeout worked and solved this problem.
    I'm afraid that there is some deeper problem connected with timeout setting. I do have very fast connection to server, and 6s timeout should be more than enough to upload multi megabyte file. On linux or windows XP upload with the same file was never an issue.

    BTW... as a side note for developers working on linux. Under linux HTML5 file api object is using "fileName" and "name" variable to holds name of the uploaded file. On windows only "name" is used. The same goes for "fileSize/size" pair.

    ReplyDelete

  9. Edit Anonymous said...

    HI Ivan Nice Job !!
    please help me to SAVE files upload in my web/folder

    only read in this file upload.php

    thks a lot !!!

    ReplyDelete
    Replies
    1. I'm not sure I understand what you are asking for... What is the problem with upload.php?

      Delete
    2. Dear Ivan,

      Thanks for you tool.
      I have the same question with Anonymous.

      How could I move the uploaded files to my assigned folder?
      Or where could I get the uploaded files?

      I tried fwrite() to copy the file content, but the file header couldn't be defined.

      p.s. My files are like *.pdf/*.jpg/*.doc/*.xls

      Delete
    3. I added a simple example, which shows how to store the uploaded data:

      upload_into_dir.php.

      If there are problems or bugs, you may file an issue.

      Delete
  10. Great job!
    It is excellent uploader!

    Can it work in IE? It doesn't add files to the queue after the Browse widow.

    ReplyDelete
  11. Nice widget.
    i use it in ExtJS 4 and make some change to fit in my app.. it's working fine.
    thank you Ivan.

    ReplyDelete
  12. nice job man.. tks a lot.. how can i put the dialog to render into my Panel? tks

    ReplyDelete
  13. If you need to implement your own window, it's not supported currently, but I'm planning to rewrite the widget as a panel instead of a window.

    ReplyDelete
    Replies
    1. Hi Ivan,

      thank you so much for this sharing, I was starting to write my own one, but I hope to save some days of works thanks to your implementation. Unfortunately I am quite new to ExtJS4 and I am trying to integrate the widget as a Panel. I have changed the Dialog.js to extend from Ext.panel.Panel, but it is not so straightforward. I guess you didn't have the chance to updates to widget to work in a panel, but can you give me some hints to what I need to modify to let it work?
      thank you
      Antonio

      Delete
    2. Hi Antonio,
      actually I would go the same way. The window is nothing more than a panel with some additional features. In that particular case those "features" are not used, so it won't be hard to switch from window to panel.

      Delete
  14. Thank you very much - great widget - especially the raw post put part !

    ReplyDelete
  15. Hey, I have to say that you have a very good programming style. I don't see very many people code the way you do.

    ReplyDelete
  16. Does not work in IE. Unfortunatelly

    ReplyDelete
    Replies
    1. Yeah, the widget is built around the File API and according to caniuse.com IE supports it from version 10.

      Delete
  17. Hi,How to use this multi file upload in IE9 and below?

    ReplyDelete
    Replies
    1. Hi aravinda,
      for what I know, you can't use it in browsers, that don't support the File API.

      Delete
    2. I am not able to upload multiple files if i open in Internet explorer.Is there any solution?

      Delete
    3. Hi,The file widget multi file uploader is working fine in chrome,fire fox.But if i open in internet explorer 9 and below, at a time only single file is uploading.Is there any solution for this so that this multi file uploader can work in all browsers? Thank you :)

      Delete
  18. This comment has been removed by a blog administrator.

    ReplyDelete
  19. Hello Ivan,

    Seen you demo and its much impressive. Thanks for it. When i want to try that in my website, by downloading all your codes from GITHUB and by running the public/index.html page, Its not working. That will be very useful for me and everyone, if you make a demo folder with the appropriate files and links of the demo. Please make a demo folder and help the newbies of extjs to play in and around on it and understand clearly and contribute to polish more.

    Advance thanks.

    Mani

    ReplyDelete
  20. For asynchronous uploading, can we set the max concurrent transmission number ?

    ReplyDelete
    Replies
    1. Currently it's not possible, but probably it's not a bad idea :). I'll try to add this feature to the next release.

      Delete
  21. Hi, Nice Widget
    I have a question...
    how can i check max file size ? tks

    ReplyDelete
    Replies
    1. What do you mean by checking the max file size?

      Delete
    2. thanks for your reply.
      I have problems uploading files over a certain size limit.
      Is it possible to set a limit?

      Delete
    3. The size limit has to be set on the server side. Check your server configuration. If you use PHP, search for settings like - upload_max_filesize, post_max_size ...

      Delete
  22. Thanks, works like a charm, easy en looks nice!

    ReplyDelete
  23. Superb... How can I have the same in MVC in ExtJS 4.2?

    ReplyDelete
    Replies
    1. It depends on how you are going to use the widget. You can put the Dialog/Panel in a view and then register listeners for the widget events in your controller.

      Delete
  24. hi ivan used this stuff but failed, 403 403 (Forbidden) coming. i am checking from localhost to call an API.plz help...

    ReplyDelete
    Replies
    1. Check if you are using the right upload URL. If you are running the server as well, check if it is configred properly. If you are trying to use a third-party API, make sure you fulfill its requirements - it seems that some kind of authentication is required.

      Delete
  25. ultimately solved. problems were some extra headers were added, which caused problem. but now i need some json value with multipart/form-data while sending POST method.currently there is only image details (as file) are there in the request-header by calling an API. anyhow i have send json values with that image.plz help..thanks in advance...

    ReplyDelete
    Replies
    1. If you want to pass additional fields with the multipart/form uploader, there is no such functionality yet. You may file an enhancement request in the issues section on GitHub: https://github.com/ivan-novakov/extjs-upload-widget/issues?state=open and when I got some time I can implement it. Or you can extend the FormDataUploader yourself, it shouldn't be too complicated.

      Delete
    2. hi ivan, i have passed the additional fields with FormData() defined in FormDataUploader.js. bt now i have a query that i want to show Dialog box in a panel(currently in window).how to do that. thanks partha...

      Delete
    3. You can use the Ext.ux.upload.Panel directly instead of the Dialog.

      Delete
  26. hi ivan, i am facing with a problem regarding ERROR HANDLING.actually on upload-failure i have to show the errror message coming from SERVER.On cosoling i have found the error and errortext(responseText) bt would be able to get it.plz help...

    ReplyDelete
    Replies
    1. Currently the error shows when you hover on the red exclamation mark. But there is a problem, if you want to handle the error yourself, because the panel doesn't relay events like "itemuploadfailure". I'll add this feature in the upcoming few days, see https://github.com/ivan-novakov/extjs-upload-widget/issues/21.

      Fot he time being, you can use a dirty hack - listen to the upload manager object directly. Or you can implement it in the panel and send me a pull request :).

      Delete
  27. hi ivan, currently i am using Ext.ux.upload.Panel.all are working fine.but actually throughout the site for that panel all its necessary files(js files) are loading when the page is being loaded.i have to make it conditional.basically i using a tabpanel where in one of the tab that "upload panel" being shown.how to do that??waiting for your answer...

    ReplyDelete
    Replies
    1. I'm not sure what are you asking about. It seems that it's ExtJS related, so I'm sure you'll find your answer in the documentation - http://docs.sencha.com/extjs/4.2.0/#!/guide

      Delete
  28. Hola ecesito saber como puedo al momento de subir los archivos que una grilla que esta aparte se carge cuando lla se a subido el archivo

    ReplyDelete
  29. Hi Ivan, the API Docs page -> 403 Forbidden.

    ReplyDelete
  30. Hi lvan, I am using ExtJS 5.0 and your demo is not supported by it. So, is there any solution to integrate this demo in 5.0 version?

    ReplyDelete