从multipart/form-data POST读取文件输入

raf*_*ale 44 c# forms wcf http-post

我正在通过HTML表单将文件发布到WCF REST服务,enctype设置为multipart/form-data和单个组件:<input type="file" name="data">.由服务器读取的结果流包含以下内容:

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

<file bytes>
------WebKitFormBoundary--
Run Code Online (Sandbox Code Playgroud)

问题是我不确定如何从流中提取文件字节.我需要这样做才能将文件写入磁盘.

Oha*_*der 43

很抱歉很晚才加入派对,但有一种方法可以使用Microsoft公共API.

这就是你需要的:

  1. System.Net.Http.dll
    • 包含在.NET 4.5中
    • 对于.NET 4,通过NuGet获取它
  2. System.Net.Http.Formatting.dll

注意 Nuget包附带了更多的程序集,但在撰写本文时,您只需要上面的内容.

一旦引用了程序集,代码就会如下所示(为方便起见使用.NET 4.5):

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

至于用法,请说你有以下WCF REST方法:

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

你可以像这样实现它

public void Upload(Stream data)
{
    MultipartParser.ParseFiles(
           data, 
           WebOperationContext.Current.IncomingRequest.ContentType, 
           MyProcessMethod);
}
Run Code Online (Sandbox Code Playgroud)

  • @GerardONeill你可以随时做Task.Wait()当你希望同步.例如:`ParseFiles(data,contentType,fileProcessor).Wait()`.不知道为什么你想要这样做... (3认同)

Dar*_*rov 32

您可以查看以下博客文章,其中说明了一种可用于multipart/form-data使用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

另一种可能性是启用aspnet兼容性和使用,HttpContext.Current.Request但这不是一种非常WCFish的方式.

  • @rafale,问题是明天你决定将你的WCF服务托管在除IIS之外的其他东西(比如Windows服务或其他什么),你必须重写它.除此之外没什么不对. (3认同)
  • File.WriteAllBytes(filepath,parser.FileContents)是我想要做的(将文件保存在磁盘上) (2认同)

Jak*_*ods 25

我有一些基于字符串解析的解析器问题,特别是对于大文件我发现它会耗尽内存而无法解析二进制数据.

为了应对这些问题,我已经开源了在C#中的multipart/form-data的解析器我自己尝试在这里

特征:

  • 处理非常大的文件.(数据在阅读时流入并流出)
  • 可以处理多个文件上传,并自动检测某个部分是否为文件.
  • 将文件作为流返回而不是byte [](适用于大文件).
  • 该库的完整文档包括MSDN样式生成的网站.
  • 完整的单元测试.

限制:

  • 不处理非多部分数据.
  • 代码比洛伦佐更复杂

只需使用MultipartFormDataParser类,如下所示:

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.
}
Run Code Online (Sandbox Code Playgroud)

在WCF服务的上下文中,您可以像这样使用它:

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);

    ...
}
Run Code Online (Sandbox Code Playgroud)

或者像这样(稍慢但代码更友好):

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

当您克隆存储库时,只需导航到,也可以使用文档 HttpMultipartParserDocumentation/Help/index.html

  • 值得注意的是,这个解析器也可以通过Nuget获得:[HttpMultipartParser](https://www.nuget.org/packages/HttpMultipartParser/).(如果有人像我一样,只是按ctrl + f寻找nuget :)) (5认同)
  • 最后是工作,非常感谢您的出色工作,非常感谢@Jake Woods。 (2认同)

Lor*_*ori 16

在这里开源了一个C#Http表单解析器.

这比在CodePlex上提到的另一个稍微灵活一些,因为您可以将它用于Multipart和Non-Multipart form-data,并且它还为您提供在Dictionary对象中格式化的其他表单参数.

这可以使用如下:

非多

public void Login(Stream stream)
{
    string username = null;
    string password = null;

    HttpContentParser parser = new HttpContentParser(stream);
    if (parser.Success)
    {
        username = HttpUtility.UrlDecode(parser.Parameters["username"]);
        password = HttpUtility.UrlDecode(parser.Parameters["password"]);
    }
}
Run Code Online (Sandbox Code Playgroud)

public void Upload(Stream stream)
{
    HttpMultipartParser parser = new HttpMultipartParser(stream, "image");

    if (parser.Success)
    {
        string user = HttpUtility.UrlDecode(parser.Parameters["user"]);
        string title = HttpUtility.UrlDecode(parser.Parameters["title"]);

        // Save the file somewhere
        File.WriteAllBytes(FILE_PATH + title + FILE_EXT, parser.FileContents);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这消耗了多少堆:byte [] data = Misc.ToByteArray(stream); string content = encoding.GetString(data); (2认同)