Drag and Drop Files in Browser

Drop files here

It is so amazing!

Since my first acquaintance with internet I couldn't understand why threre is no possibility to drag and drop files to browser. So when I saw such ability in Gmail I immediately decided to implement such a thing in my projects.

File share service

All of us in our studio are constantly sharing files with each other. Up to this moment we used mail attachments or Skype to transfer files. It's not always handy. So I decided to create a simple internal project for file sharing.

Browsers support

Only last versions of Firefox and Chrome supports this functionality.

Implementation

There were several implementation stages: processing of dropped files, sending them and processing on the severe side, displaying the files in the list, deleting the files and rendering of already uploaded files. Let's look closer.

Processing of dropped files. Listen required events (all are required).

this.el.bind("dragover", this._over.bind(this))//higlight drop area
  .bind("dragenter", function(){return false;})//just listen event
  .bind('dragleave', this._leave.bind(this))//turn off highlighting
  .bind("drop", this._drop.bind(this))//process drop event
  
  //limit drop area
  this.blockDocumentDrop();

Event handlers above. In each we should return false.

//hide higlighting
_leave:function(e)
{
return this.hideHighlight();
},
//show highlighting
_over: function(e)
{
var dt = e.originalEvent.dataTransfer;
if(!dt) return;
 
//check that dropped object is file
 //FF
if(dt.types.contains&&!dt.types.contains("Files")) return;
//Chrome
if(dt.types.indexOf&&dt.types.indexOf("Files")==-1) return;
 
 //without this not works in hrome
if($.browser.webkit) dt.dropEffect = 'copy';
 
this.el.addClass(this.mousein_class);

return false;
},
//process drop
_drop:function(e)
{
var dt = e.originalEvent.dataTransfer;
if(!dt&&!dt.files) return;

this.hideHighlight();
var files = dt.files;
for (var i = 0; i < files.length; i++) {
        var file = files[i];
//in Firefox also present file.name, but in Chrome is only file.fileName
this.onDropFile(e, file.fileName);
this.upload(file);
}
return false;
}

The code that limits drop area.

$(document)
   .bind('dragenter', function(e) {return false;})
   .bind('dragleave', function(e) {return false;})
   .bind('dragover', function(e) {
       var dt = e.originalEvent.dataTransfer;
       if (!dt) { return; }
       dt.dropEffect = 'none';
       return false;
   }.bind(this));

Sending and processing files on the server side. There is only one working method for sending files both in Chrome and Firefox:

upload: function(file)
{
    var xhr = new XMLHttpRequest();
    
    xhr.upload.addEventListener("progress", function(e){this.onProgress(e, file.fileName)}.bind(this), false);
    
    //attempts to get working upload progress event in Chrome. Without luck. 
    //xhr.upload.onprogress = function(e){debugger;this.onProgress(e, file.fileName)}.bind(this);
    //xhr.onprogress = function(e){debugger;this.onProgress(e, file.fileName)}.bind(this);
    
    xhr.onload = function(e){this.onComplete(e, file.fileName)}.bind(this);
    
    //as parameter send file name, there is no other ways to pass it to the serverside
    xhr.open('POST', this.url+"?file="+file.fileName, true);
    xhr.send(file);
}

On server side (I use my favorite web framework Django) I process the bytes of the file.

filename = request.GET["file"]
//bytes
data = request.raw_post_data

//I created class to encapsulate file saving
name = fm.save_file(filename, data)

Code that saves the file:

def save_file(self, filename, data):
        filename = self.rename_file(filename)
        file = open(os.path.join(self.dir, filename), 'w')
        file.write(data)
        file.close()
        return filename

Final structure

After initial code refactoring it became easy to understand the logical structure.

Client side code

DragUpload - class that manages highlighting and processes files (handles drop and upload)

FileLine - gui class that incapsulates file line in string (render, event handling).

FileManager - application class.

Serverside code. File handling on the server side does FileManager class. Perhaps a recursive method of file renaming is interesting:

def get_indexed_name(self, filename):
    if not os.path.exists(os.path.join(self.dir, filename)):
        return filename
    index = 1
    name, ext = os.path.splitext(filename)
    filename_regexp = "^(.*?)(_\d+)%s$" % ext.replace(".", "\.")
        
    match = re.match(filename_regexp, filename)
    if match:
        _index = int(match.group(2).replace('_', ''))
        name = match.group(1)
        index = _index+1
        
        
    new_name =  name + '_' + unicode(index)+ ext
    return self.get_indexed_name(new_name)

At the end

Drag and drop feature the thing that changes the web we used to. I'm going to use it inn all upload scenarios for my web applications. Also all the code is available in zip archive (including server side Django code). Please use it and make your users happy.

Igor Kononuchenko (i@vedjo.com.ua), Vedjo studio.

Comments

Quick note: this is working fine in Safari 5.0.

Hi
Nice yar. it is very helpful for us

just wanted to say this works on mine fine as well thanks

Advertise on this site

Recent Comments