Posts tagged ‘flex’

File uploads… Flash or Silverlight?

It has been a while, but recently I had to look into Flash again. I was happy to find out Adobe has done some effort to allow the Flash developer to access the local filesystem. This is very useful to implement a file upload (see this example).

In previous versions of Flash, the file upload was about the only thing one could do with local files. Due to security reasons, only Adobe Air (the desktop variant) applications where allowed to read the bytes of a local file. But all this changed quite recently, because in Flash 10 you can finally read a file into a ByteArray and interpret the bytes. This is really cool stuff and will certainly inspire a lot of programmers/designers to come up with innovative applications.

The reason I was excited about it was that I needed an uploader that allows to upload large – like in several gigabytes – files. I thought it would be possible to just read in part of the file and upload that part to the server, and then send the next part. Or send some parts in parallel to speed things up. I already found a nice example online:

http://blog.kevinhoyt.org/2004/12/17/file-upload-using-flexcentraljava/ uploads files in chunks to a server. It needs some modification to work with the new security constraints of Flash 10 (all loader.load calls should go in one function and they should be initiated by a user interaction) but it illustrates splitting a file in chunks very well.

But I hit the wall when looking for a way to load only part of a local file into a ByteArray. As my files will be several gigabytes large, it is impossible to load them entirely into the memory, so I need something to read a part of the file. After some digging around it appears that the Adobe guys didn’t provide this (yet), so I guess my super-uploader for monstrously large files will have to wait :-s

Ok, maybe I should take a look at Silverlight, the Microsoft guys just released the beta of version 3. It didn’t take too many googles to find the open-source Silverlight File Upload project. This Silverlight uploader is pretty amazing, it splits large files into chunks and uploads them in separate POSTs. Serverside code for the asp.net platform is included in the project but not necessary (a basic PHP server side is available on the site as well).

As there is not many information on the project’s site, here is some things I learned about it today:

When you download the project, and open it in Visual Studio, you’ll see three projects. The FileUpload project is the client-side, the other two are necessary for the asp.net server side. As I’m not interested in these we’ll delete them and use the php server side. Put the php code in the htdocs folder of your Apache, change the upload.php file to upload the file to an existing folder on your harddrive and run it. The Silverlight client generates a TestPage.html which includes the Silverlight component. Because the component expects some parameters to be passed to it, you’ll have to add a <param> tag to it:

<param name="initParams"
value="id=FileUpload,UploadPage=http://localhost/PhpFileUpload/upload.php,MaxConcurrentUploads=1,Filter=Images  (*.jpg;*.gif)|*.jpg;*.gif|All Files (*.*)|*.*,"/>

Now you can just open TestPage.html in a browser and adore the uploading :).

The component will first send a HTTP GET to your server. The server answers with 0 if the file does not exist yet or with the filesize if it does. If the file already exists, the Silverlight component will show a message box asking the user if she wants to overwrite it.

In a second step the actual upload is performed. This is done with a HTTP POST which contains the filename and some other parameters in the URL and the bytes of the file in the body of the POST request. This is very efficient as no parsing of the request body is necessary to extract any fields like filename etc. as would be necessary if the upload was done with a regular multipart/form-data upload.

It is however more difficult than I was hoping for to let the component work together with my Rails application. If anyone knows a good way to write the entire POST body to a file, let me know in the comments :p

edit: found out how to rewrite the method to support multipart upload.


private void writeStringToStream(string s,Stream stream)
{
    UTF8Encoding encoding = new UTF8Encoding();
    byte[] z = encoding.GetBytes(s);

    for (int i = 0; i < z.Length; i++)
    {
        stream.WriteByte(z[i]);
        stream.Flush();
    }
}

//this method sends the data.
//changed the original code so the POST is multipart with fields. 
private void WriteCallback(IAsyncResult asynchronousResult)
{
    HttpWebRequest webrequest = (HttpWebRequest)asynchronousResult.AsyncState;

    webrequest.ContentType = "multipart/form-data; boundary=" + MULTIPART_BOUNDARY;

    // End the operation.
    Stream requestStream = webrequest.EndGetRequestStream(asynchronousResult);

    byte[] buffer = new Byte[4096];
    int bytesRead = 0;
    int tempTotal = 0;

    Stream fileStream = resizeStream != null ? (Stream)resizeStream : File.OpenRead();

    string dataName = File.Name;// +".part" + partCounter;
    long fileLen = File.Length;
    long currentLength = ChunkSize;

    //determine content-range
    long currentIndex = BytesUploaded;
    long chunkLength = (currentIndex + ChunkSize > fileLen) ? fileLen - currentIndex : ChunkSize;

    //datafile[file] field
    string theString = "--" + MULTIPART_BOUNDARY + "\r\n";
    theString += "Content-Disposition: form-data; name=\"datafile[file]\"; filename=\"" + dataName + "\"\r\n";
    theString += "Content-Type: binary/octet-stream\r\n";
    theString += "Content-Range: " + currentIndex + "-" + (currentIndex+chunkLength-1) + "/" + fileLen + "\r\n\r\n";
    writeStringToStream(theString,requestStream);

    fileStream.Position = BytesUploaded;
    while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0 && tempTotal + bytesRead < ChunkSize && !cancel)
    {
        requestStream.Write(buffer, 0, bytesRead);
        requestStream.Flush();
        
        BytesUploaded += bytesRead;
        tempTotal += bytesRead;
        if (UploadProgressChanged != null)
        {
            int percent = (int)(((double)BytesUploaded / (double)FileLength) * 100);
            UploadProgressChangedEventArgs args = new UploadProgressChangedEventArgs(percent, bytesRead, BytesUploaded, FileLength, file.Name);
            this.Dispatcher.BeginInvoke(delegate()
            {
                UploadProgressChanged(this, args);
            });
        }
    }

    writeStringToStream("\r\n--" + MULTIPART_BOUNDARY + "--\r\n", requestStream);

    // only close the stream if it came from the file, don't close resizestream so we don't have to resize it over again.
    if (resizeStream == null)
        fileStream.Close();
    requestStream.Close();
    partCounter++;
    webrequest.BeginGetResponse(new AsyncCallback(ReadCallback), webrequest);
}

Advertisements

March 31, 2009 at 9:10 pm Leave a comment


Feeds

Articles to be written…

Twitter – kr3l

my del.icio.us

RSS Google Reader Shared Stuff

  • An error has occurred; the feed is probably down. Try again later.

RSS Listening to..

  • An error has occurred; the feed is probably down. Try again later.