Reading file input from a multipart/form

I'm POSTing a file to a WCF REST service through a HTML form, with enctype set to multipart/form-data and a single component: <input type="file" name="data"> . The resulting stream being read by the server contains the following:

------WebKitFormBoundary
Content-Disposition: form-data; name="data"; filename="DSCF0001.JPG"
Content-Type: image/jpeg

<file bytes>
------WebKitFormBoundary--

The problem is that I'm not sure how do extract the file bytes from the stream. I need to do this in order to write the file to the disk.


You may take a look at the following blog post which illustrates a technique that could be used to parse multipart/form-data on the server using the Multipart Parser:

public void Upload(Stream stream)
{
    MultipartParser parser = new MultipartParser(stream);
    if (parser.Success)
    {
        // Save the file
        SaveFile(parser.Filename, parser.ContentType, parser.FileContents);
    }
}

Another possibility is to enable aspnet compatibility and use HttpContext.Current.Request but that's not a very WCFish way.


Sorry for joining the party late, but there is a way to do this with Microsoft public API .

Here's what you need:

  • System.Net.Http.dll
  • Included in .NET 4.5
  • For .NET 4 get it via NuGet
  • System.Net.Http.Formatting.dll
  • For .NET 4.5 get this NuGet package
  • For .NET 4 get this NuGet package
  • Note The Nuget packages come with more assemblies, but at the time of writing you only need the above.

    Once you have the assemblies referenced, the code can look like this (using .NET 4.5 for convenience):

    public static async Task ParseFiles(
        Stream data, string contentType, Action<string, Stream> fileProcessor)
    {
        var streamContent = new StreamContent(data);
        streamContent.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType);
    
        var provider = await streamContent.ReadAsMultipartAsync();
    
        foreach (var httpContent in provider.Contents)
        {
            var fileName = httpContent.Headers.ContentDisposition.FileName;
            if (string.IsNullOrWhiteSpace(fileName))
            {
                continue;
            }
    
            using (Stream fileContents = await httpContent.ReadAsStreamAsync())
            {
                fileProcessor(fileName, fileContents);
            }
        }
    }
    

    As for usage, say you have the following WCF REST method:

    [OperationContract]
    [WebInvoke(Method = WebRequestMethods.Http.Post, UriTemplate = "/Upload")]
    void Upload(Stream data);
    

    You could implement it like so

    public void Upload(Stream data)
    {
        MultipartParser.ParseFiles(
               data, 
               WebOperationContext.Current.IncomingRequest.ContentType, 
               MyProcessMethod);
    }
    

    I've had some issues with parser that are based on string parsing particularly with large files I found it would run out of memory and fail to parse binary data.

    To cope with these issues I've open sourced my own attempt at a C# multipart/form-data parser here

    Features:

  • Handles very large files well. (Data is streamed in and streamed out while reading)
  • Can handle multiple file uploads and automatically detects if a section is a file or not.
  • Returns files as a stream not as a byte[] (good for large files).
  • Full documentation for the library including a MSDN-style generated website.
  • Full unit tests.
  • Restrictions:

  • Doesn't handle non-multipart data.
  • Code is more complicated then Lorenzo's
  • Just use the MultipartFormDataParser class like so:

    Stream data = GetTheStream();
    
    // Boundary is auto-detected but can also be specified.
    var parser = new MultipartFormDataParser(data, Encoding.UTF8);
    
    // The stream is parsed, if it failed it will throw an exception. Now we can use
    // your data!
    
    // The key of these maps corresponds to the name field in your
    // form
    string username = parser.Parameters["username"].Data;
    string password = parser.Parameters["password"].Data
    
    // Single file access:
    var file = parser.Files.First();
    string filename = file.FileName;
    Stream data = file.Data;
    
    // Multi-file access
    foreach(var f in parser.Files)
    {
        // Do stuff with each file.
    }
    

    In the context of a WCF service you could use it like this:

    public ResponseClass MyMethod(Stream multipartData)
    {
        // First we need to get the boundary from the header, this is sent
        // with the HTTP request. We can do that in WCF using the WebOperationConext:
        var type = WebOperationContext.Current.IncomingRequest.Headers["Content-Type"];
    
        // Now we want to strip the boundary out of the Content-Type, currently the string
        // looks like: "multipart/form-data; boundary=---------------------124123qase124"
        var boundary = type.Substring(type.IndexOf('=')+1);
    
        // Now that we've got the boundary we can parse our multipart and use it as normal
        var parser = new MultipartFormDataParser(data, boundary, Encoding.UTF8);
    
        ...
    }
    

    Or like this (slightly slower but more code friendly):

    public ResponseClass MyMethod(Stream multipartData)
    {
        var parser = new MultipartFormDataParser(data, Encoding.UTF8);
    }
    

    Documentation is also available, when you clone the repository simply navigate to HttpMultipartParserDocumentation/Help/index.html

    链接地址: http://www.djcxy.com/p/75396.html

    上一篇: 数据解析器在C#中

    下一篇: 从多部分/表格读取文件输入