Desktop Image Uploaders Using Adobe AIR and JavaScript

July 1, 2008

Tutorials

What makes the web fun? Is it the cool applications like Google Maps, which are neat but not particularly fun? Is it social networks like Facebook? Sure, that’s a little fun. But what really gets passed around the web? Viral video. Nothing catches our interest like video and images, which is ironic, as the HTML technology for uploading media to web sites is one of the worst parts of browsers.

To solve the upload problem, most of the big services have dedicated upload applications that run on all the different platforms. But it takes a lot of work to develop them, especially when they have to run on both Macintosh and Windows. Or is it so tough? As it turns out, Adobe AIR technology makes it possible to write applications for any platform using just HTML and JavaScript. It also offers access to cool desktop features, notably drag and drop.

In this article I’ll show you how to build an uploader in Adobe AIR using JavaScript and HTML. My first example will show the upload of a single file. Then I’ll build on that example to show how to handle uploading a batch of files. I’ll finish by extending the example to show the list of files currently available on the server after they are uploaded.

It all starts with the PHP code for the server.

The Server PHP Code

As with most things having to do with PHP, the code to handle uploading a file from a web browser is remarkably simple. The code in Listing 1 moves the uploaded file to the uploads directory.

Listing 1. basicFileUpload.php

<html>
<?php
$tempFile = $_FILES['Filedata']['tmp_name'];
$fileName = $_FILES['Filedata']['name'];
$fileSize = $_FILES['Filedata']['size'];

move_uploaded_file($tempFile, "./uploads/" . $fileName);

echo "Uploaded $fileName";
?>
</html>

I then put that file on my local Apache web server and create the uploads directory.

Now I want to test the file using a web client, so I write the HTML page that Listing 2 shows.

Listing 2. upload.html

<html>
<body>
<form enctype="multipart/form-data" action="basicFileUpload.php" method="post">
<input type="hidden" name="MAX_FILE_SIZE" value="2000000" />
<input type="file" name="Filedata" />
<input type="submit" value="Upload" />
</form>
</body>
</html>

Using my browser, I navigate to the upload.html web page and see the window displayed in Figure 1.


Figure 1. A basic HTML upload page

Ok, it’s not much to look at, but it gets the job done. I first choose the file (a picture of my daughter), and click the upload button. After the image is uploaded, the window looks like the one Figure 2 shows.


Figure 2. The result page after the upload

This file certainly works, but it’s not a great experience. I can’t drag and drop a bunch of files and watch them upload. I get no feedback about how the upload is proceeding. It’s really not a great way to get media up to service. So let’s use Adobe AIR technology, HTML, and JavaScript to create an uploader.

Building the Simple Adobe AIR Uploader

Developing for Adobe AIR requires downloading and installing the Adobe AIR runtime and the Adobe AIR software development kit (SDK). After they’re installed, the first step is to create an XML file that tells Adobe AIR where the root HTML file is located, and provides a name for the window, a size, the icons for the application, and so on. Listing 3 shows this file.

Listing 3. application.xml

<?xml version="1.0" encoding="utf-8" ?>
<application xmlns="http://ns.adobe.com/air/application/1.0" minimumPatchLevel="0">
        <id>com.jherr.draguploader</id>
        <name>Drag Uploader</name>
        <version>1.0</version>
        <filename>Drag Uploader</filename>
        <description>A simple image uploader.</description>
        <copyright>(c) 2007 Example Co., Inc.</copyright>
        <initialWindow>
                <content>appSandbox.html</content>
                <title>Drag Uploader</title>
                <systemChrome>standard</systemChrome>
                <transparent>false</transparent>
                <visible>true</visible>
                <minimizable>true</minimizable>
                <maximizable>true</maximizable>
                <resizable>true</resizable>
                <width>400</width>
                <height>350</height>
                <x>150</x>
                <y>150</y>
                <minSize>300 300</minSize>
                <maxSize>400 350</maxSize>
        </initialWindow>
</application>

This might look like a lot of code, but there really isn’t much to it. The most important tags are the <content> tag, which specifies the name of the HTML page, and the <width> and <height> tags, which tell Adobe AIR how big the application should be.

As I work this example through the article, this application.xml file really won’t change until the end, when I just adjust the size to give me a little more room.

The file that will be contained in the Adobe AIR window is called appSandbox.html and is shown in Listing 4.

Listing 4. appSandbox.html

<html>
<head>
<title>Drop Uploader</title>
<script src="js/AIRAliases.js"></script>
<script>
var Exposed = {};

Exposed.uploadToServer = function( fileList )
{
  url = "http://localhost/basicFileUpload.php";

  boundary = '--------------======-------------------AaB03x';

  request = new air.URLRequest(url);
  request.useCache = false;
  request.contentType = 'multipart/form-data, boundary='+boundary;
  request.shouldCacheResponse = false;
  request.method='POST';

  buffer = new air.ByteArray();
  
  file = new air.File(fileList.nativePath);

  fileStream = new air.FileStream();
  fileStream.open(file, air.FileMode.READ);
  fileContents = new air.ByteArray();
  fileStream.readBytes(fileContents, 0, file.size);
  fileStream.close();

  buffer.writeUTFBytes( "--"+boundary+"\r\n" );
  buffer.writeUTFBytes( "content-disposition: form-data; name=\"Filedata\"; filename=\""+file.name+"\"\r\n" );
  buffer.writeUTFBytes( "Content-Transfer-Encoding: binary\r\n" );
  buffer.writeUTFBytes( "Content-Length: "+file.size+"\r\n" );
  buffer.writeUTFBytes( "Content-Type: application/octet-stream\r\n" );
  buffer.writeUTFBytes( "\r\n" );

  buffer.writeBytes(fileContents, 0, fileContents.length);

  buffer.writeUTFBytes( "\r\n--"+boundary+"--\r\n" );

  request.data = buffer;

  var loader = new air.URLLoader();
  loader.addEventListener(air.ProgressEvent.PROGRESS , function(e){
  });
  loader.addEventListener(air.IOErrorEvent.IO_ERROR , function (e){
    air.trace( 'error: '+ e.text );                
  });
  loader.addEventListener(air.Event.COMPLETE, function(e){
    air.trace( loader.data );
  });
  loader.load( request );
}

function doLoad() {
  document.getElementById('UI').contentWindow.parentSandboxBridge = Exposed;
}
</script>
</head>
<body class="appSandbox">
<iframe id="UI" class="classicSandbox" width="100%" height="90%"
  frameborder="0" src="classicSandbox.html"
  sandboxRoot="http://localhost/" documentRoot="app:/" onload="doLoad()">
</iframe>
</body>
</html>

There are two key elements to this file. The first is the uploadToServer method, which uploads a file to the server using the Adobe AIR Request and Uploader objects. This function also uses the Adobe AIR File object to read the contents of the file to be uploaded and format the upload request for the server. I’ve chosen to manage the file upload myself, but you can use the upload() method on the File object to upload the file.

The second important part is the IFRAME at the bottom of the page, which includes the classicSandbox.html file. That file contains the user interface for the application and the drag-and-drop code.

But before I get into that, I want to explain why there are two HTML files instead of just one. The answer is security. Because Adobe AIR applications are run on users’ desktops and have access to all their files, the applications need to be secure. That means that any JavaScript code that has direct access to the Adobe AIR APIs needs to be secure. So Adobe has wisely created a “sandbox” scheme in which the application sandbox has access to the complete Adobe AIR API but can’t “eval” the code. The JavaScript in the “classic” sandbox can run the eval command but can’t access the Adobe AIR methods that are security-critical.

That’s a really brief discussion of why there are the two files; the Adobe web site contains a lot more information about this subject. The long and the short of it is that you need two files, and Listing 5 shows the second file.

Listing 5. classicSandbox.html

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>Drop Uploader</title>
<script type="text/javascript" src="js/AIRAliases.js"></script>
<script>
function doDragEnter(event) { event.preventDefault(); }
function doDragOver(event) { event.preventDefault(); }
function doDragLeave(event) { event.preventDefault(); }
function doDragEnd(event) { }      
function doDrop(event)
{
var fileList = event.dataTransfer.getData("application/x-vnd.adobe.air.file-list");
window.parentSandboxBridge.uploadToServer(fileList[0]);
}

</script>
</head>

<body ondragenter="doDragEnter(event)"
ondragover ="doDragOver(event)" ondragleave="doDragLeave(event)"
   ondrop="doDrop(event)" ondragend="doDragEnd(event)">
Drop a file here
</body>
</html>

This file is certainly a lot smaller because most of the hard work was done in the appSandbox.html file in Listing 4. All this code needs to do is handle dragging and dropping the file and then passing the first file reference off to the uploader code in the appSandbox.html file.

To test it, I use adl application.xml to launch the application on my desktop. I then drop a file onto it, as Figure 3 shows.


Figure 3. The simplest possible uploader

There really isn’t any feedback at this point. To see whether the file has been uploaded, I just look in the uploads directory to make sure it’s there.

Obviously, this still isn’t a great user experience. It needs to handle multiple files and provide some feedback as to how the uploads are going. So let’s add those features to the application.

Adding Some Feedback

Because most of the modifications I’m going to make are to the user interface, I’ll show the modifications to the classicSandbox.html file, as Listing 6 shows.

Listing 6. classicSandbox.html

...
<script>
function updateFileList( files )
{
  var elHTML = '';
  if ( files.length == 0 ) elHTML = 'Drop files here';
  else
     for( var f in files ) { elHTML += files[f]+'<br/>'; }
  document.getElementById( 'elFileList' ).innerHTML = elHTML;
}

childSandboxBridge = { updateFileList: updateFileList };

function doDragEnter(event) { event.preventDefault(); }
...
function doDragEnd(event) { }      
function doDrop(event)
{
var fileList = event.dataTransfer.getData("application/x-vnd.adobe.air.file-list");
window.parentSandboxBridge.uploadToServer(fileList);
}
</script>
</head>

<body ...>
<div id="elFileList">
Drop files here
</div></body></html>

At the bottom I wrap the Drop files here text in the <div> tag. Then, at the top of the file, I have a new function called updateFileList, which updates the <div> tag with the list of files that are waiting to be uploaded. The updateFileList function is called from the appSandbox.html file. Listing 7 shows the modifications I made to the file.

Listing 7. appSandbox.html

...
<script>
var g_fileList = [];

var Exposed = {};

Exposed.uploadToServer = function( fileList )
{
  for( var f in fileList ) { g_fileList.push( fileList[f] ); }
  uploadFile();
}

function updateFileListInWindow()
{
  var files = [];
  for( var f in g_fileList ) {
    var airFile = new air.File( g_fileList[f].nativePath );
    files.push( airFile.name );
  }
  document.getElementById('UI').contentWindow.childSandboxBridge.updateFileList( files );
}

function uploadFile()
{
  updateFileListInWindow();

  if ( g_fileList.length == 0 )
    return;

  var file = g_fileList.pop();

  var url = "http://localhost/basicFileUpload.php";

  ...
  loader.addEventListener(air.Event.COMPLETE, function(e){
    uploadFile();
  });
  loader.load( request );
}
...
</script>
...

I used the bridge between the application sandbox and the classic sandbox to ask the uploader to update the interface as each of the files is uploaded. At the bottom of the file you can see where I hook onto the COMPLETE event to start the upload of the next file in the list and to update the interface to show which files remain in the queue.

To test this, I launch the updated code using the adl application in the Adobe AIR SDK and drag four images onto it, as Figure 4 shows.


Figure 4. An uploader that handles multiple files

I then see a list of the four files, as Figure 5 shows.


Figure 5. The list during the upload

The file names disappear one by one until the Drop files here text returns to indicate that all the files were uploaded and the application is ready to accept some more files.

But still I’m not happy because I really want to see what files are up on the server so that I can make sure my files really got there.

Listing the Uploaded Files

To get the list of files I need something on the server that I can call to get the list of files. This code, called uploadedFileList.php, is shown in Listing 8.

Listing 8. uploadedFileList.php

<?php
header("Content-type","text/xml");
?>
<content>
<?php
if ($handle = opendir('uploads')) {
while (false !== ($file = readdir($handle))) {
 if ( preg_match( "/[.]jpg$/", $file ) ) {
?>
<image>
<path>http://localhost/uploads/<?php echo $file; ?></path>
</image>
<?php
} }
closedir($handle);
}
?>

This is pretty simple stuff. I just scan the uploads directory and create an XML tag for each of the images in the directory.

The user interface HTML and JavaScript code need to be updated as well, and that’s shown in Listing 9.

Listing 9. classicSandbox.html

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>Drop Uploader</title>
<script type="text/javascript" src="js/AIRAliases.js"></script>
<script>
function updateFileList( files )
{
  var elHTML = '';
  if ( files.length == 0 ) elHTML = 'Drop files here';
  else
     for( var f in files ) { elHTML += files[f]+'<br/>'; }
  document.getElementById( 'elFileList' ).innerHTML = elHTML;
}

function updateUploadedFiles( urlList )
{
  var elHTML = '';
  for( var u in urlList )
	elHTML += urlList[u]+'<br/>';
  document.getElementById( 'elUplodedFiles' ).innerHTML = elHTML;
}

childSandboxBridge = { updateFileList: updateFileList, updateUploadedFiles:updateUploadedFiles };

function doDragEnter(event) { event.preventDefault(); }
function doDragOver(event) { event.preventDefault(); }
function doDragLeave(event) { event.preventDefault(); }
function doDragEnd(event) { }      
function doDrop(event)
{
var fileList = event.dataTransfer.getData("application/x-vnd.adobe.air.file-list");
window.parentSandboxBridge.uploadToServer(fileList);
}
</script>
</head>

<body ondragenter="doDragEnter(event)"
ondragover ="doDragOver(event)" ondragleave="doDragLeave(event)"
   ondrop="doDrop(event)" ondragend="doDragEnd(event)">
<div id="elFileList" style="height:200px;border:1px solid #999;padding:5px;">
Drop files here
</div>
<h1>Uploaded files</h1>
<div id="elUplodedFiles">
</div>
</body>
</html>

The new code includes a <div> tag at the bottom that contains the list of uploaded files. At the top of the file is a new function called updateUploadedFiles, which updates the <div> tag with the list of files returned from the server. This is the complete code for the classicSandbox.html file. Listing 10 shows the complete code for the appSandbox.html file.

Listing 10. appSandbox.html

<html>
<head>
<title>Drop Uploader</title>
<script src="js/AIRAliases.js"></script>
<script>
var g_fileList = [];

var Exposed = {};

Exposed.uploadToServer = function( fileList )
{
  for( var f in fileList ) { g_fileList.push( fileList[f] ); }
  uploadFile();
}

function updateFileListInWindow()
{
  var files = [];
  for( var f in g_fileList ) {
    var airFile = new air.File( g_fileList[f].nativePath );
    files.push( airFile.name );
  }
  document.getElementById('UI').contentWindow.childSandboxBridge.updateFileList( files );

  var loader = new air.URLLoader();
  var fileListUrl = "http://localhost/uploadedFileList.php";
  var request = new air.URLRequest(fileListUrl);
  request.useCache = false;
  request.shouldCacheResponse = false;
  loader.addEventListener(air.Event.COMPLETE, function(e){
	 var paths = loader.data.match( /\<path\>(.*?)\<\/path\>/g );
air.trace( paths );
document.getElementById('UI').contentWindow.childSandboxBridge.updateUploadedFiles( paths );
  });
  loader.load( request );
}

function uploadFile()
{
  updateFileListInWindow();
  
  if ( g_fileList.length == 0 )
    return;

  var file = g_fileList.pop();

  var url = "http://localhost/basicFileUpload.php";

  var boundary = '--------------======-------------------AaB03x';

  var request = new air.URLRequest(url);
  request.useCache = false;
  request.contentType = 'multipart/form-data, boundary='+boundary;
  request.shouldCacheResponse = false;
  request.method='POST';

  var buffer = new air.ByteArray();
  
  var file = new air.File(file.nativePath);

  var fileStream = new air.FileStream();
  fileStream.open(file, air.FileMode.READ);
  var fileContents = new air.ByteArray();
  fileStream.readBytes(fileContents, 0, file.size);
  fileStream.close();

  buffer.writeUTFBytes( "--"+boundary+"\r\n" );
  buffer.writeUTFBytes( "content-disposition: form-data; name=\"Filedata\"; filename=\""+file.name+"\"\r\n" );
  buffer.writeUTFBytes( "Content-Transfer-Encoding: binary\r\n" );
  buffer.writeUTFBytes( "Content-Length: "+file.size+"\r\n" );
  buffer.writeUTFBytes( "Content-Type: application/octet-stream\r\n" );
  buffer.writeUTFBytes( "\r\n" );

  buffer.writeBytes(fileContents, 0, fileContents.length);

  buffer.writeUTFBytes( "\r\n--"+boundary+"--\r\n" );

  request.data = buffer;

  var loader = new air.URLLoader();
  loader.addEventListener(air.ProgressEvent.PROGRESS , function(e){
  });
  loader.addEventListener(air.IOErrorEvent.IO_ERROR , function (e){
    air.trace( 'error: '+ e.text );                
  });
  loader.addEventListener(air.Event.COMPLETE, function(e){
    uploadFile();
  });
  loader.load( request );
}

function doLoad() {
  document.getElementById('UI').contentWindow.parentSandboxBridge = Exposed;
}
</script>
</head>
<body class="appSandbox">
<iframe id="UI" class="classicSandbox" width="100%" height="90%"
  frameborder="0" src="http://localhost/classicSandbox.html"
  sandboxRoot="http://localhost/" documentRoot="app-resource:/" onload="doLoad()">
</iframe>
</body>
</html>

The updateFileListInWindow function is the big new addition to this file. It makes the request of the PHP script on the server, then parses the XML response to get the URLs of the uploaded files.

To test this final version of the uploader application, I again launch it on my desktop, as Figure 6 shows.


Figure 6. An uploader with a drop region and a list of uploaded files

Now I can drop files onto the window and see then disappear from the list of files to upload and reappear in the complete list of uploaded files on the server.

Where to Go from Here

This simple example just scratches the surface of what can be done with Adobe AIR in combination with JavaScript and HTML. From here you could add code to authenticate the user against the system. Adobe AIR allows for child windows, which make building that type of functionality a lot easier. In addition, you can alert the user about the status of uploads by bouncing the dock icon on the Mac or showing icons in the system tray on Windows. In fact, almost every aspect of the local operating system is available to you through simple JavaScript objects.

I’m really excited about Adobe AIR, particularly because it uses the technologies we are already familiar with, like JavaScript and HTML, to build applications. In fact, you could re-use parts of the HTML or JavaScript code you use on your web site to build desktop applications. If you use this example as a basis for your code, which I encourage you to do, feel free to contact me either via the comments or directly, to let me know. Then we can update this article with our work to show more engineers how to use Adobe AIR.

About Cal Evans

Many moons ago, at the tender age of 14, Cal touched his first computer. (We're using the term "computer" loosely here, it was a TRS-80 Model 1) Since then his life has never been the same. He graduated from TRS-80s to Commodores and eventually to IBM PC's. For the past 10 years Cal has worked with PHP and MySQL on Linux OSX, and when necessary, Windows. He has built on a variety of projects ranging in size from simple web pages to multi-million dollar web applications. When not banging his head on his monitor, attempting a blood sacrifice to get a particular piece of code working, he enjoys building and managing development teams using his widely imitated but never patented management style of "management by wandering around". Cal is currently based in Nashville, TN and is gainfully unemployed as the Chief Marketing Officer of Blue Parabola, LLC. Cal is happily married to wife 1.28, the lovely and talented Kathy. Together they have 2 kids who were both bright enough not to pursue a career in IT. Cal blogs at http://blog.calevans.com and is the founder and host of Day Camp 4 Developers

View all posts by Cal Evans

12 Responses to “Desktop Image Uploaders Using Adobe AIR and JavaScript”

  1. gijs25 Says:

    Hello,

    A nice tutorial but the final code is not working i tryed almost every way to initialize the iframe localhost urls app / app-resource etc but it is just not working

    I want o create a uploader for my family so they can upload images to my webserver. After reading about the sandbox this should be possible and with your code i got of to a great start. But it’s not working

    I get the same error as the other guys and a permission.

    Any idea’s on how to solve this?

    The php files are on the server and all html files in the air app.

    Thanks

  2. mattcrane Says:

    Ok narrowed it down to this line which appears twice in appSandbox.html

    request.shouldCacheResponse = false;

    is causing the error

    [app:/appSandbox.html][18:39:59]ReferenceError: Error #1056: Cannot create property shouldCacheResponse on flash.net.URLRequest.
    ReferenceError: Error #1056: Cannot create property shouldCacheResponse on flash.net.URLRequest

    I guess this is also what is causing my Uploaded Files panel not to refresh

    So any help of fixing this error would make me the happiest guy alive

    Cheers

    Matt

  3. mattcrane Says:

    Hey, right well it seems to upload fine but, the only problem is i had to diable a couple of lines of code.

    and this seems to stop the panel below updating

    Any ideas

    //FROM appSandbox.html

    var request = new air.URLRequest(url);
    //request.useCache = false;
    request.contentType = ‘multipart/form-data, boundary=’+boundary;
    //request.shouldCacheResponse = false;
    request.method=’POST’;

  4. mattcrane Says:

    Hi

    Love the tutorial and am really excited about trying it out, but I cant seem to get it to work.

    I want to use it to upload image to my web server (remotely)

    I have got the air application running up until the drop of the file where, an error pops up

    [error][app:/appSandbox.html][18:08:32]: ReferenceError: Error #1056: Cannot create property shouldCacheResponse on flash.net.URLRequest.

    I’m not sure what im doing wrong, where should the files be

    I currently have

    appSandbox.html (Local in Air App)
    appSandbox.html (Local in Air App)
    AIRAliases.html (Local in Air App)
    application.xml (Local in Air App)

    UploadedFileList.php (On web server)
    UploadPost.php (On web server) – Proccesses the uploaded file

    Also I’m 100% sure of the paths i should be using

    Any help would be greatly appreciated

    Thanks

    Matt

  5. jagwire16 Says:

    I am receiving the same error as anonymous above:

    SecurityError: fileRead
    doDrop at http://localhost/classicSandbox.html : 36
    ondrop at http://localhost/classicSandbox.html : 42

    Please help!

  6. _____anonymous_____ Says:

    There’s another cool AIR tutorial on SP that does this kinda stuff, and some more — http://www.sitepoint.com/article/learn-adobe-air-part-1. The author’s done some PHP stuff on ZDZ in the past.

  7. _____anonymous_____ Says:

    Great article! Thank you for your contribution.

    I’m receiving the following error:

    SecurityError: fileRead
    doDrop at http://localhost/classicSandbox.html : 36
    ondrop at http://localhost/classicSandbox.html : 42

    This happens when I drag the file on the window, then drop. It never gets uploaded, and I’m assuming this error is the reason.

    Any ideas? I’m stuck!

    Thanks!
    Michael

  8. dgeorgit Says:

    Hi Jack,
    This is a good article and I’m glad you like Adobe AIR and programming with HTML/JS.

    I want to point out that in your particular sample there is no need for the classic sandbox (we call it a non-application sandbox).
    This sandbox was only designed in order to allow developers to use some of the code generation techniques (like eval, Function constructor, etc) that are considered to be unsafe and that are disabled inside application sandbox after onload event (you can read more about it in this book: http://www.tostring.org/books/adobe-air-for-javascript-developers-pocketguide/)

    As you don’t use eval or other string-to-code techniques that are disabled in application sandbox, you can code everything in application sandbox; it’s even easier because you are not required to add the exposing functionality via SandboxBridge.

    Here’s a quick overview of the capabilities for two sandboxes -
    Application sandbox:
    - has full access to AIR APIs
    - can execute cross domain requests (XHR)
    - in exchange for these powerfull APIs, code generation techniques are restricted after onload (eval, etc..)
    - major Ajax frameworks work in this sandbox

    Non-application sandbox
    - doesn’t have access to any AIR APIs
    - doesn’t have restrictions for eval, Function constructor, etc..
    - can communicate with Application sandbox and can access only functions carefully exposed by the developer via SandboxBridge

    Best regards,
    Dragos Georgita | Adobe AIR Engineering

  9. funkatron Says:

    mikiccc: The iframe method is the only approach outlines by Adobe. I don’t know of any other.

    Jack: Can you point out what code in this project has to execute in the classic sandbox? I don’t see any here after looking it over a couple times. Maybe I’m missing something.

    -Ed

  10. mikiccc Says:

    with IFRAME the page does not respect WCAG

    is it possible do the same thing (or similar) without IFRAME ?

  11. sevenforty Says:

    Sorry, forgot to add that it’s still an interesting article.

  12. sevenforty Says:

    One of the fundamental problems of doing file uploads boils down to utilizing HTTP to perform the data transfer. HTTP was not designed for doing things such as binary file transfers or even large transfers of data. Performing such tasks should be delegated to a protocol designed for such activities, namely FTP.

    Transferring a file via HTTP is basically a huge hack which piggy backs your intended file into the request/response operation. For small files, it works OK, however, as most developers know, you run into problems with transferring large data files via HTTP.

    Point being, transferring files through a website (via the standard request/response method) will never be efficient.