1

I have a zip file that I can read with DotNetZipLib from the file system. However, when I POST it via a form to my MVC application it can't be read as a stream. My best guess at the moment is that the HTTP upload is somehow corrupting the zip file. There's no shortage of questions with the same problem, and I thought I'd accounted for the stream properly but perhaps I'm not using the .NET object(s) here as intended.

Here's my WebAPI POST handler:

public void Post(HttpRequestMessage request)
{
    using(var fileData = request.Content.ReadAsStreamAsync().Result)
        if (fileData.Length > 0)
        {
            var zip = ZipFile.Read(fileData); // exception
        }
}

The exception, of course, is from the DotNetZipLib ZipFile just saying that the stream can't be read as a zip. If I replace fileData with just a path to the file (this is all being tested on the same machine) then it reads it, so it has to be the HTTP upload.

In FireBug, the headers for the POST are:

Response Headers:
    Cache-Control       no-cache
    Content-Length      1100
    Content-Type        application/xml; charset=utf-8
    Date                Sat, 01 Feb 2014 23:18:32 GMT
    Expires             -1
    Pragma              no-cache
    Server              Microsoft-IIS/8.0
    X-AspNet-Version    4.0.30319
    X-Powered-By        ASP.NET
    X-SourceFiles       =?UTF-8?B?QzpcRGF0YVxDb2RlXE9yZ1BvcnRhbFxPcmdQb3J0YWxTZXJ2ZXJcYXBpXGFwcHg=?=

Request Headers
    Accept              text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Encoding     gzip, deflate
    Accept-Language     en-US,en;q=0.5
    Connection          keep-alive
    Cookie              uvts=ENGUn8FXEnEQFeS
    Host                localhost:48257
    Referer             http://localhost:48257/Home/Test
    User-Agent          Mozilla/5.0 (Windows NT 6.3; WOW64; rv:26.0) Gecko/20100101 Firefox/26.0

Request Headers From Upload Stream
    Content-Length      31817
    Content-Type        multipart/form-data; boundary=---------------------------265001916915724

And the form is simple enough:

<form action="/api/appx" method="post" enctype="multipart/form-data">
    <input name="postedFile" type="file" />
    <input type="submit" />
</form>

Am I doing something wrong with the steam? Pulling data from the HttpRequestMessage incorrectly? Or perhaps I should be receiving the upload in an entirely different way?

David
  • 208,112
  • 36
  • 198
  • 279
  • As far as I know, you are supposed to use an HttpPostedFileBase for the file. See darins answer here: http://stackoverflow.com/a/9500609/1026459 . Is using that approach a possibility here? – Travis J Feb 01 '14 at 23:37
  • @TravisJ: That may indeed be the way to go, though I get a completely different error (`UnsupportedMediaTypeException`) when trying to POST to it: `"The request entity's media type 'multipart/form-data' is not supported for this resource. No MediaTypeFormatter is available to read an object of type 'HttpPostedFileBase' from content with media type 'multipart/form-data'."` When I'd researched that previously, various other articles online had led me to my current approach. Darin's answer is for an MVC action, it looks like a WebAPI handler may be different? – David Feb 01 '14 at 23:44
  • Did you try the `IEnumerable`? Perhaps it is sent as a collection of 1. – Travis J Feb 01 '14 at 23:49
  • 2
    Ah, I see, from multiple posts I read it would seem the model binder is not setup for the web API to support the HttpPostedFileBase. Some of the suggestions I encountered were to use an approach similar to this answer: http://stackoverflow.com/a/13734209/1026459 , which suggests using the MultipartFormDataStreamProvider class http://msdn.microsoft.com/en-us/library/system.net.http.multipartformdatastreamprovider(v=vs.118).aspx and then getting the MultipartFileData out of it. – Travis J Feb 01 '14 at 23:55
  • 1
    I have a suspicion that there might be a requirement for the stream to be seekable for a zip read. Try writing the upload to a MemoryStream and operating on that. If it works, then you've found your problem. – spender Feb 01 '14 at 23:56
  • @spender: I was wondering the same thing just now, actually. I instantiate a `MemoryStream` and then called `fileData.CopyTo(memoryStream);` but the `zip` library throws the same error on the `MemoryStream` as well. – David Feb 02 '14 at 00:00
  • @TravisJ: Excellent find, I hadn't seen those links yet. If nothing else, that'll give me a good deal more to tinker with tonight. I'll report back with my progress, thanks! – David Feb 02 '14 at 00:03

2 Answers2

1

When you post a file using a HTML form the media type is multipart/form-data which has some special formatting syntax, as you can see from your Firebug details. You can't just read it as a stream and expect it to match the file that was sent. There are a set of ReadAsMultipartAsync extension methods for handling this media type.

Darrel Miller
  • 139,164
  • 32
  • 194
  • 243
  • That did the trick, thanks! I needed to `ReadAsMultipartAsync()` first, then in the `Contents` collection of the result grab the first entry and `ReadAsStreamAsync()` on that. – David Feb 02 '14 at 14:33
0

The below code worked fine for both Zip and Text file. you may try this out

        public HttpStatusCode Post(string fileName)
        {
                    var task = this.Request.Content.ReadAsStreamAsync();
                    task.Wait();
                    Stream requestStream = task.Result;

                    try
                    {
                        Stream fileStream =    File.Create(HttpContext.Current.Server.MapPath("~/" + fileName));
                        requestStream.CopyTo(fileStream);
                        fileStream.Close();
                        requestStream.Close();
                    }
                    catch (IOException)
                    {
                        throw new HttpResponseException(HttpStatusCode.InternalServerError);
                    }

                    HttpResponseMessage response = new HttpResponseMessage();
                    response.StatusCode = HttpStatusCode.Created;
                    return response.StatusCode;

        }
  • I'm afraid the same error persists with this as well. The more I attempt this, the more it looks like I'm really fighting against the WebAPI model binding with file streams. It seems to be a bit of an edge case, and just to get unblocked I may just turn it into a regular MVC controller instead of a WebAPI controller. – David Feb 02 '14 at 13:13
  • do you have multiples files being uploaded, i haven't checked that part. whether ReadAsMultipartAsync suggested by Darrel fixed your issue? – Arunkumar Ravikumar Feb 03 '14 at 04:06