Upload file from Windows desktop Forms app

classic Classic list List threaded Threaded
17 messages Options
Reply | Threaded
Open this post in threaded view
|

Upload file from Windows desktop Forms app

Glenn Mason

Hi Dean-

 

First of all - thanks for creating NeatUpload. I've been using it for the last year for an important project at my company and it has worked out great. Now I have a new need for NeatUpload and am hoping you can provide a little guidance so I don't waste too much time.

I have a need to upload files from a Windows desktop forms application that will post to an ASP.NET page that employs NeatUpload to stream the file to our SQL Server database (much like we do now with our web application). I read the post 'Upload with no GUI?' from a couple of years back, and have the following questions:

1. I am using NeatUpload 1.2x currently; other than the bug fixes and other features in the newer version, is there anything in the new release that would be essential to have in order to use NeatUpload for my Windows desktop upload project?

2. Some of what you have said in the old post makes sense to me, but since I haven't developed any Windows forms desktop apps, I have a little learning to do. I was wondering if there are any examples, sample code, aditional help, etc., that might be available that could help me get a running start with this project?

3. Have you or anyone else posted any code or examples anywhere that do an upload from a desktop app?

 

I am getting started on this today and will probably have more follow-up questions. Thanks,

Glenn

Reply | Threaded
Open this post in threaded view
|

Re: Upload file from Windows desktop Forms app

Dean Brettle
Administrator
1. If you are planning to display progress, NeatUpload-1.3.13 contains NeatUpload/ProgressJsonHandler.ashx which provides a JSON representation of you the upload state.  It would probably be easier to use some existing JSON library to parse what it returns than to create your own custom progress page.  See Using NeatUpload from JavaScript for details.
2 & 3. Sorry, afraid not.  Contributions welcome.

--Dean
Reply | Threaded
Open this post in threaded view
|

Re: Upload file from Windows desktop Forms app

Glenn Mason

Hi again-

I am currently putting together a small demo to get the upload working from a simple winforms app. I am currently using NeatUpload 1.2x that I already have installed just to get the actual upload and streaming to SQL Server working before I upgrade to the new version to address the progress display issue. I have a web app that is working very well streaming to SQL server using the Hitone SqlServerUploader. I tried creating a new page within this web app for my new upload page that my winfrms app is calling. I am using HttpWebRequest to stream the upload file and some other form fields. I tested the client part by simple saving the form fields to my database and saving off the uploaded file to a server directory without neatupload yet at this stage. That all works fine.

I then added the following to my uploader.aspx file:

<%@ Register TagPrefix="Upload" Namespace="Brettle.Web.NeatUpload" Assembly="Brettle.Web.NeatUpload" %>

<%@ Register TagPrefix="SqlUpload" Namespace="Hitone.Web.SqlServerUploader" Assembly="Hitone.Web.SqlServerUploader" %>

 

<SqlUpload:SqlServerInputFile ID="inputFile" Height="22px" size='50' runat="server" />

 

In my codebehind I have the following:

if (inputFile.HasFile)

{

   System.IO.Stream content = inputFile.FileContent;

   int bytesToRead = (int)inputFile.ContentLength;

   byte[] buf = new byte[4096];

   while (bytesToRead > 0)

   {

      bytesToRead -= content.Read(buf, 0, Math.Min(bytesToRead, buf.Length));

   }

   content.Close();

   inputFile.Verify();

}

 

This doesn't work for reasons that I hope are obvious to you. What should I be doing here? This works perfectly for my web app, but inputFile.HasFile returns false when uploading from the winforms app. Skipping this line, inputFile.FileContent is also null. I have verified that the filename attribute is correct and the name attribute is "inputFile" which matches the name ofthe NeatUpload control. Am I doing something stupid here? I haven't done desktop development using winforms previously. Can you show me how I should be handling the neatupload control to accept the file upload stream in this case?

 

Thanks,

Glenn

Reply | Threaded
Open this post in threaded view
|

Re: Upload file from Windows desktop Forms app

Dean Brettle
Administrator
Per Uploading from a Client Application, you need to add a NeatUpload_PostBackID query param.  In NeatUpload-1.3, you can use a form field instead of a query param as long as the form field precedes the file form field.

--Dean
Reply | Threaded
Open this post in threaded view
|

Re: Upload file from Windows desktop Forms app

Glenn Mason

Dooooooooooooooooooooowl!

That did it ... you already mentioned that in the 'Upload with no GUI' post. My bad ..

Thanks for that rapid response also!

Glenn

Reply | Threaded
Open this post in threaded view
|

Re: Upload file from Windows desktop Forms app

Glenn Mason

Hi Dean-

Thanks again for writing that json web handler .. that has made my job a lot easier getting a nice progress bar updating within my winforms app. I do have one (hopefully) last question though ..

I set this thing up as you suggested in another post with the upload on a separate asynchronous thread while I am making calls to the web handler polling for the json progress messages on the main thread. This all works perfectly, but I am trying to figure out how to enable a Cancel button to allow a user to terminate an upload for whatever reason. My first question is - what is the preferred way to cancel an in-progress upload from my winforms app, and also (more of a winforms issue) I have a disabled Cancel button on my form that I enable only after the user selects a file and I initiate the upload, however, for some reason, the button does not appear enabled and will not seem to repaint as the label with progress info is doing. Do you have any ideas there? Do you think it would work better if both the upload and the progress polling were each on their own thread other than the main thread?

Thanks again for an awesome product,

Glenn

Reply | Threaded
Open this post in threaded view
|

Re: Upload file from Windows desktop Forms app

Dean Brettle
Administrator
Put the upload and the progress update each on their own thread separate from the main thread. That should allow the cancel button to repaint. To request cancellation of an upload, send a request to the json handler with cancel=true added to the query string. Then, if when your progress thread gets a status of "Cancelled" it should stop the upload, perhaps by terminating the upload thread (there might be a better way). You should also stop the upload if you get a status of "Rejected" or "Failed". --Dean
Reply | Threaded
Open this post in threaded view
|

Re: Upload file from Windows desktop Forms app

Guest-963

Does sending the cancel=true to the json handler actually do anything on the server side other than sending back a status of Cancelled in the json response? I'm just curious if this is simply a mechanism that allows a more graceful upload termination or is there something that happens server side when the cancel param is passed that is important. I am guessing I could just terminate the thread if the user clicked the Cancel button, but would that preclude any cleanup processing or other necessary activity on the web side of the upload process?

Glenn

Reply | Threaded
Open this post in threaded view
|

Re: Upload file from Windows desktop Forms app

Dean Brettle
Administrator
If you just stop the upload, files will still get cleaned up.  However, ASP.NET or IIS might log an error because the upload was stopped unexpectedly.  Also, any upload state retrieved after stopping the upload without cancelling it first might have a status of Failed.

Also, it's useful to stop uploads that have a status of Failed or Rejected anyway because otherwise you are just wasting bandwidth.

--Dean
Reply | Threaded
Open this post in threaded view
|

Re: Upload file from Windows desktop Forms app

Guest-963

Hi and thanks again for all the great pointers. I moved the upload and the polling each to its own thread and implemented the cancel as you suggested sending ther cancal parm to the json handler. The app is working really well. I did hit a snag with large upload timing out with the 100 sec timeout and had to do some fishing around before figuring out how to alleviate the timeout restriction on the HttpWebRequest.

Now I am wondering if my web page with my upload control could be transitioned to an ashx page. Is there a way to make the upload work in a handler without using the form upload control? Right now my upload page is an actual asp page .. REST-like, but not really. I could swear I read something alluding to this in the documentation but can't seem to locate it currently.

Glenn

Reply | Threaded
Open this post in threaded view
|

Re: Upload file from Windows desktop Forms app

Dean Brettle
Administrator
Yes. Just use the static UploadModule.Files collection to access the uploaded files. Search the 1.3 manual for StaticUploadHandler.ashx (I think) for more info and pointer to some example code. --Dean
Reply | Threaded
Open this post in threaded view
|

Re: Upload file from Windows desktop Forms app

Glenn Mason

Hey Dean-

I tried a version of the StaticUploadHandler.ashx, but it is behaving differently than my aspx page with upload control. The file seems to upload normally but then hangs in my progress polling loop. In my handler, I have:

UploadedFile file = UploadModule.Files[0];
if (file.IsUploaded)
{
 System.IO.Stream content = file.InputStream;
 int bytesToRead = (int)file.ContentLength;
 byte[] buf = new byte[4096];
 while (bytesToRead > 0)
 {
  bytesToRead -= content.Read(buf, 0, Math.Min(bytesToRead, buf.Length));
 }
 content.Close();
}

On a side note, I changed this line in the ASPX file:

System.IO.Stream content = inputFile.FileContent;

to this in the ASHX file since the UploadedFile has no FileContent member (tell me if this isn't correct):

System.IO.Stream content = file.InputStream;

 

In my winforms app, I am polling using this method:

private void UpdateProgress()
{
    // get json progress message
    UploadProgress deserializedProgress = JsonConvert.DeserializeObject<UploadProgress>(GetJsonProgress());

    // invoke update delegate
    UpdateStatusDelegate updateStatus = new UpdateStatusDelegate(UpdateStatusHandler);
    NotificationEventArgs e = new NotificationEventArgs(deserializedProgress.Status.ToLower());
    Object sender = System.Threading.Thread.CurrentThread;

    while (deserializedProgress.PercentComplete < 100 && strPostBackId.Length > 0)
    {
        PopulateNotificationEvent(ref e, deserializedProgress);
        updateStatus(sender, e);

        // suspend thread for 2 seconds before re-checking progress
        // TODO: might want to increase the sleep time proportional to the file upload size (doesn't
        // make sense to poll every other second for a file that is 100MB)
        System.Threading.Thread.Sleep(2000);

        // poll the upload progress
        deserializedProgress = JsonConvert.DeserializeObject<UploadProgress>(GetJsonProgress());
    }

    // send final 100% update to trigger file hasing message
    PopulateNotificationEvent(ref e, deserializedProgress);
    updateStatus(sender, e);
}

The problem that I am having now is that I never exit the While loop. Where using the ASPX page had always ended the upload with the condition of PercentComplete reaching 100, calling the ASHX handler for the upload, the PersentComplete progresses and eventually resets to 0 and I never exit my While loop (I assume the state between PercentComplete being 100 and then being reset happens between json responses, so my app never receives a PercentComplete state exactly equal to 100).

My guess is that this has something to do with processing of multiple upload files as opposed to a single file upload? While my handler need only process a single file upload, does the use of the Files collection throwing off my loop control variables? What is the preferred loop control variable for monitoring and exiting progress polling?

 

Fyi - I use the strPostBackId > 0 because I empty this string in my cancel event, which sends a cancel=true to the json handler and then exits my polling loop and gracefully terminates my worker thread.

 

Glenn

 

 

 

Reply | Threaded
Open this post in threaded view
|

Re: Upload file from Windows desktop Forms app

Dean Brettle
Administrator
Poll until status is Completed, Cancelled, Rejected, or Failed. Not sure why PercentComplete isn't making it to 100. How high does it get? What are the max values for BytesRead and TotalBytes? --Dean
Reply | Threaded
Open this post in threaded view
|

Re: Upload file from Windows desktop Forms app

Glenn Mason

PercentComplete *is* making it to 100 .. it just resets back to 0 immediately thereafter. Since my While loop is controlled by this variable, one json update may return PercentComplete = 86, while the next may be 98 and the next it will be 0. It's just resetting and I'm not catching the state when it is 100 because it rolls over without warning and obviously the json request is not instantaneous.

This problem showed up after moving from the neatUpload control on an ASPX page to accessing the Files collection on the ASHX handler. I have never upoloaded multiple files, but are you doing anything with an assumption that the Files collection may contain more than one file and rolling over the PercentComplete state?

 

Reply | Threaded
Open this post in threaded view
|

Re: Upload file from Windows desktop Forms app

Glenn Mason

I changed the polling While loop - no affect. The Status reverts to Unknown and remains in the loop.

The real problem though is that the upload thread fails to end. I know exactly when it stops because I have an async callback function that gets invoked. It worked fine with the input control on the ASPX page, but doesn't work any longer using the StaticUploadHandler.ashx and the Files collection. There must be a major difference in operation between these 2 approaches.

Using the upload control, the PercentComplete reaches 100 and *stays there* and the json response at the end remains with all of the final progress you'd expect to see - status (Completed), total bytes, file names, content types, etc. This all allows a graceful termination of upload and polling threads.

Using the ashx handler and the Files colection PercentComplete rolls over after reaching 100 and the json response is also reset to all zeroes and nulls (as in an initialized state). This causes the problems as stated of the upload thread never ending and the polling thread difficult to determine when to end (the lesser problem than the run-away upload thread).

Basically it seems that I need a way to keep the upload state from rolling back to the originally initialized state after my file upload completes and stay frozen at the end of the upload with the *final* status values. This was the behavior I was getting when using the upload control on an aspx page.

Any ideas?

Reply | Threaded
Open this post in threaded view
|

Re: Upload file from Windows desktop Forms app

Glenn Mason

Hey Dean-

Here is some further info that I hope might help..

When my upload thread hangs up, it hangs on the red bolded line in this Upload function:

public static HttpWebResponse Upload(HttpWebRequest req, UploadFile[] files, NameValueCollection form)

 

{

 

    List<MimePart> mimeParts = new List<MimePart>();

 

 

    try

 

    {

 

        foreach (string key in form.AllKeys)

 

        {

 

            StringMimePart part = new StringMimePart();

 

 

            part.Headers["Content-Disposition"] = "form-data; name=\"" + key + "\"";

 

            part.StringData = form[key];

 

 

            mimeParts.Add(part);

 

        }

 

 

        int nameIndex = 0;

 

 

        foreach (UploadFile file in files)

 

        {

 

            StreamMimePart part = new StreamMimePart();

 

 

            if (string.IsNullOrEmpty(file.FieldName))

 

                file.FieldName = "file" + nameIndex++;

 

 

            part.Headers["Content-Disposition"] = "form-data; name=\"" + file.FieldName + "\"; filename=\"" + file.FileName + "\"";

 

            part.Headers["Content-Type"] = file.ContentType;

 

 

            part.SetStream(file.Data);

 

 

            mimeParts.Add(part);

 

        }

 

 

        string boundary = "----------" + DateTime.Now.Ticks.ToString("x");

 

 

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

 

        req.Method = "POST";

 

 

        long contentLength = 0;

 

 

        byte[] _footer = Encoding.UTF8.GetBytes("--" + boundary + "--\r\n");

 

 

        foreach (MimePart part in mimeParts)

 

        {

 

            contentLength += part.GenerateHeaderFooterData(boundary);

 

        }

 

 

        req.ContentLength = contentLength + _footer.Length;

 

 

        byte[] buffer = new byte[8192];

 

        byte[] afterFile = Encoding.UTF8.GetBytes("\r\n");

 

        int read;

 

 

        using (Stream s = req.GetRequestStream())

 

        {

 

            foreach (MimePart part in mimeParts)

 

            {

 

                s.Write(part.Header, 0, part.Header.Length);

 

 

                while ((read = part.Data.Read(buffer, 0, buffer.Length)) > 0)

 

                    s.Write(buffer, 0, read);

 

 

                part.Data.Dispose();

 

 

                s.Write(afterFile, 0, afterFile.Length);

 

            }

 

 

            s.Write(_footer, 0, _footer.Length);

 

        }

 

 

        return (HttpWebResponse)req.GetResponse();

 

    }

 

    catch

 

    {

 

        foreach (MimePart part in mimeParts)

 

            if (part.Data != null)

 

                part.Data.Dispose();

 

 

        throw;

 

    }

 

}

Eventually the app throws an error trying to convert an int64 to an Int32 because the SecsRemaining value in the json response ends up coming back eventually with a huge number. Other fields are getting set to 1 and -1. The final json response I get back when I get this crash is:

{
      "Status" : "Unknown",
      "BytesRead" : 1,
      "BytesTotal" : -1,
      "PercentComplete" : 0,
      "BytesPerSec" : 0,
      "Message" : "",
      "SecsRemaining" : 922337203685,
      "SecsElapsed" : 0,
      "CurrentFileName" : "",
      "ProcessingState" : null,
      "Files" : [] 
    }

This response and crash are happening about 1-2 minutes after the streaming has actually finished.

The upload file I am testing with is actually the NeatUpload 1.3 zip archive. Bytes reported uploading using the ASPX approach were 2233554. Everything works consistently and perfectly with the ASPX approach but fails consistently at the same place and for the same reason with the ASHX approach. I am testing with this NeatUpload archive only now, but have previously tested 50+MB file uploads with the ASPX implementation and there is never a problem.

I hope something in this info might help.

Thanks again,

Glenn

Reply | Threaded
Open this post in threaded view
|

Re: Upload file from Windows desktop Forms app

Dean Brettle
Administrator
Please post or email me (dean at brettle dot com) the full source for your StaticUploadHandler.ashx.  I suspect that it is hanging for some reason.

--Dean