views:

629

answers:

1

Trying to upload files using Google Gears and ASP.NET... I assume you can as the HttpRequest API accepts blobs.

I have FileUpload control in the page.

<asp:FileUpload runat="server" ID="File1" />

Then the JavaScript

var file1 = document.getElementById("<%# File1.ClientID %>");
var desktop = google.gears.factory.create('beta.desktop');

file1.onclick = function()
{
    desktop.openFiles(openFilesCallback,
        {
            singleFile: true,
            filter: ["image/jpeg"]
        }
    );
    return false;
}

function openFilesCallback(files)
{
    if(files.length == 0)
    {
     alert("No files selected");
    }
    else
    {
     // 1MB = 1024 * 1024 bytes
     if(files[0].blob.length > (1024 * 1024)) 
     {
      alert("File '" + files[0].name + "' is too big to upload");
     }
     else
     {
      uploadFile(files[0]);
     }
    }
}

function uploadFile(file)
{
    var up = google.gears.factory.create("beta.httprequest");
    up.open("POST", "upload.ashx");
    up.send(file.blob);
}

However, I am not sure how to handle it in the handler.

public void ProcessRequest (HttpContext ctx)
{
    ctx.Response.ContentType = "text/plain";
    ctx.Response.Write("Hello World");
    ctx.Response.Write(ctx.Request.Files.Count.ToString());
    ctx.Response.Write(ctx.Request.Form.Count.ToString());
}

If I set a breakpoint on either of the last two statements, both Files.Count and Form.Count return 0. When I don't I get an exception in Firebug: Component returned failure code: 0x80004001 (NS_ERROR_NOT_IMPLEMENTED)

If I can't use POST to upload via Gears, can it be done using PUT?

Edit: PHP Code will be fine as well (since I want to do it in both languages)

+1  A: 

I think the root of your issue is that with gears your not actually using the multipart-form/data upload mechanism, instead your actually sending your file data in the POST request itself.

So what you need to do is actually read the request stream from your input handler, and grab the data. Your code should look something like this:

public void ProcessRequest (HttpContext ctx)
{
    var filePath = "SomePath/ToWrite/Filedata.dat";
    var stream = ctx.Request.InputStream;
    var buffer = new byte[stream.Length];
    stream.Read(buffer,0,buffer.Lenght);
    using(var fileStream = new FileStream(filePath,FileMode.Create))
    {
        fileStream.Write(buffer, 0, buffer.Length);
    }
}

Of course whether you write to a file or a database is up to you, as well as exception handling and figuring out whether you want to create a new file or append an existing file, etc. Also, since your code had a 1MB limit on file size, I didn't bother to limit the buffer length. If your potentially dealing with very large blocks you may need to pick a smaller buffer and read from the stream in chunks until you run out of data (this would be for the case when your stream length was > int.maxvalue, so probably not likely).

If you need to include things like the name of the original file you have a couple options. One is just to send it with the URL as a query parameter (make sure it's encoded properly). Then you can read it from ctx.Request.QueryString["fileName"] or some such.

Another option would be to put the data you need into a more complex object on the client side, and then use JSON to serialize it all and put it into the POST. So your client code would look something like:

function uploadFile(file)
{
    var up = google.gears.factory.create("beta.httprequest");
    up.open("POST", "upload.ashx");
    var fileInfo = { Name: file.name, Data: file.blob, someOtherData: ...};
    up.send(JSON.stringify(fileInfo));
}

You would need to deserialize it on the server side, so you would most likely need the DataContractJsonSerializer from System.Web.Services. Another option would be the Json.Net library from James Newton-King (http://james.newtonking.com/pages/json-net.aspx).

internal class FileInfo
{
    public string Name { get; set; }
    public byte[] Data { get; set; }
    // .. some other data
}


public void ProcessRequest(HttpContext ctx)
{
    var serializer = new DataContractJsonSerializer(typeof(FileInfo));
    FileInfo fInfo = (FileInfo)serializer.ReadObject(ctx.Request.InputStream);
    var path = basePath + fInfo.Name;
    using(var fileStream = new FileStream(path,FileMode.Create))
    {
        fileStream.Write(fInfo.Data,0,fInfo.Data.Length);
    }
}

I'm afraid I can't help much on the PHP side....It's been a long, long, long time since I've been in PHP land :)

ckramer
Thanks, that seemed to work (it was already using multipart-form/data so that wasn't the problem). Although rather than using JSON and deserializing the data I am setting request headers for the additional meta data.
Sam