MVC3 HttpPostedFileBase First Upload Gives No Data But Subsequent Do
Asked Answered
O

3

28

Background of the issue.

EDIT 3 I can verify that this appears to be working again in Chrome 20. Thanks!

TLDR Update

EDIT 2 After even further mucking about this appears to be a bug in Chrome 19. Please see here:

http://groups.google.com/a/chromium.org/group/chromium-bugs/browse_thread/thread/15bc4992bcd4be1d/b905e8de20ee58fa?lnk=raot

Nothing like an up to the minute bug! :-)

Firefox-latest and IE8/9 work as expected. The Fiddler logs show the same behavior with the major difference that the 2nd 401 authorization check does not send the form payload garbled and it works as expected.

It sounds like a test case and a fix is in the works! So, hopefully for any of you out there that may have run into this, this helps!

END TLDR

I have a controller action that takes in solely an uploaded file which is a CSV file of "events". Behind the scenes, this CSV gets parsed out, turned into Event objects and the data is sync'd in the database. As a result output, the user is prompted to download an Excel spreadsheet that contains all the errors on each line that occurred.

This all works fine locally on my development machine. Once deploying to the DEV environment however, I'm getting different results. The first attempt to upload a file results in the stream being, what's the word that I'm looking for here, unreadable? It correctly reports its length but attempts to pull the information out gets nothing. Subsequent reads do have the data and everything works as expected.

I'll give to you the pertinent code pices. First, the gen'd HTML form simplified:

<form action="/Events/ImportLocal" enctype="multipart/form-data" method="post">

<input id="uploadFile" name="uploadFile" type="file" />
<input type="submit" value="Upload Events" />    

</form>

Pretty straight forward:

Controller action (forgive the mess, I've been hacking at this for a couple hours now):

[HttpPost]
public ActionResult ImportLocal(HttpPostedFileBase uploadFile)
{
    if (uploadFile == null || uploadFile.ContentLength <= 0)
    {
        BaseLogger.Info("uploadFile is null or content length is zero...");
        //Error content returned to user
    }

    BaseLogger.InfoFormat("Community Import File Information: Content Length: {0}, Content Type: {1}, File Name: {2}",
    uploadFile.ContentLength, uploadFile.ContentType, uploadFile.FileName);

//This logger line is always reporting as correct. Even on the attempts where I can't pull out the stream data, I'm seeing valid values here.
//EXAMPLE output on failed attempts:
//Import File Information: Content Length: 315293, Content Type: application/vnd.ms-excel, File Name: Export_4-30-2012.csv

    BaseLogger.InfoFormat("Upload File Input Stream Length: {0}", uploadFile.InputStream.Length);           
//This is reporting the correct length on failed attempts as well


//I know the below is overly complicated and convoluted but I'm at a loss for why the inputstream in HttpPostedFileBase is not pulling out as expected
//so I've been testing
        var target = new MemoryStream();
        uploadFile.InputStream.CopyTo(target);
        byte[] data = target.ToArray();
        BaseLogger.InfoFormat("File stream resulting length = {0}", data.Length);
//This reports correctly. So far so good.

        StringReader stringOut;
        var stream = new MemoryStream(data) {Position = 0};

        using (var reader = new StreamReader(stream))
        {
            string output = reader.ReadToEnd();
            BaseLogger.InfoFormat("Byte[] converted to string = {0}", output);
//No go...output is reported to be empty at this point so no data ever gets sent on to the service call below
            stringOut = new StringReader(output);
        }


        //Build up a collection of CommunityEvent objects from the CSV file
        ImportActionResult<Event> importActionResults = _eventImportServices.Import(stringOut);

The goal is to pass a textreader or a stringreader to the service method as behind the scenes this is using a CSV processing implementation that wants these types.

Any ideas why the first request fails but subsequent work? I feel I need a fresh set of eyes as I'm running dry on ideas.

Last point, IIS 7.5 is the target both locally via IIS Express and on the destination server.

Thanks

EDIT for the bounty:

I'm copying my bounty message here and the Fiddler output for each request

I've added a few comments to this question. The issue appears related to NTLM. What I see in Fiddler is a 401 Unauthorized on request 1 with valid form data (point, it's my understanding that these first two 401 Unauthorized requests are "normal" under scenarios where only Windows Authentication is on; verify please?). Request 2 listed is again a 401 with the same form data except the uploaded file data is now just a bunch of boxes, the exact same size worth of data, just not the exact uploaded data. Request 3 is a 200 OK that contains the garbled data and this is what my controller action is getting. How do I get NTLM to play nice with file uploads?

Here's the Fiddler output for each request:

Request 1) enter image description here

Request 2) enter image description here

And finally request 3 that is the processed HTTP 200 OK enter image description here

EDIT 2 After even further mucking about this appears to be a bug in Chrome 19. Please see here:

http://groups.google.com/a/chromium.org/group/chromium-bugs/browse_thread/thread/15bc4992bcd4be1d/b905e8de20ee58fa?lnk=raot

Nothing like an up to the minute bug! :-)

Firefox-latest and IE8/9 work as expected. The Fiddler logs show the same behavior with the major difference that the 2nd 401 authorization check does not send the form payload garbled and it works as expected.

It sounds like a test case and a fix is in the works! So, hopefully for any of you out there that may have run into this, this helps!

Thanks

Ossian answered 17/5, 2012 at 22:30 Comment(4)
As a followup everyone, this appears to be related to using strictly Windows authentication. Windows Authentication is on and Anonymous is off on the site. After firing up Fiddler I see the standard 2 failed 401 authorize requests with NTLM. This is where the problem occurs. On the first failed 401 authorize request, I see the form posted with the proper file upload data. On the second failed 401 authorize request however, the form data is garbled. Its the proper size but not the correct data. I get 309KB (or whatever the size was) or NUL characters when viewing the output in notepad++Ossian
Continued: We have multiple levels of authorization in the app that relies on the user's identity so changing the application to run under a service account that would get me by this is not an option either, at least I don't believe so. Is there a way to not have to turn impersonation on and use a service account to allow for a successful upload?Ossian
+1 for a significantly well documented question and for keeping everything updated.Rumrunner
I don't see a try catch in the code you posted. Have you tried that to see the error?Pes
K
1

This is just an idea to try. Note that StreamReader has many constructors:

http://msdn.microsoft.com/en-us/library/system.io.streamreader.aspx

In some of the constructors it attempts to autodetect the encoding (if specified) and if it cannot detect it then it falls back to the one specified. The constructor used uses UTF-8 encoding. You could change the constructor to autodetect the encoding and if not then use UTF-8.

Krafftebing answered 18/5, 2012 at 4:18 Comment(0)
C
1

I think you need to check permissions on IIS folder / Virtual Web Site in which files are uploaded and dir itself if they are in a distincst places.

Corkscrew answered 23/5, 2012 at 10:6 Comment(0)
B
1

Good question. Authentication was the first thing that came to my mind. It's normal to get two unauthorized and the 3rd authorized. This is just the way NTLM works.

If one needs to make it work on Chrome as well and cannot wait for the fix I would suggest to enable Forms authentication that redirects to the same domain but on a different port, where LOGIN application with windows authentication resides. This app sets the form cookie or not and sends you back to you main app. The process is well described here: http://msdn.microsoft.com/en-us/library/ms972958.aspx

This way, your NTLM is done once and then you have a cookie, so NTLM is not used with your POST request and everything should work as expected.

Just thought I would share this one :)

Bottali answered 2/7, 2012 at 9:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.