从多部分/表格读取文件输入
我通过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--
问题是我不确定如何从流中提取文件字节。 我需要这样做才能将文件写入磁盘。
您可以查看以下博文,其中阐述了一种可用于使用Multipart Parser解析服务器上的multipart/form-data
的技术:
public void Upload(Stream stream)
{
MultipartParser parser = new MultipartParser(stream);
if (parser.Success)
{
// Save the file
SaveFile(parser.Filename, parser.ContentType, parser.FileContents);
}
}
另一种可能是启用aspnet兼容性并使用HttpContext.Current.Request
但这不是一个非常WCFish的方式。
对不起,迟到加入派对,但有一种方法可以通过微软公开API来实现 。
以下是您需要的内容:
System.Net.Http.dll
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);
}
}
}
至于用法,假设您有以下WCF REST方法:
[OperationContract]
[WebInvoke(Method = WebRequestMethods.Http.Post, UriTemplate = "/Upload")]
void Upload(Stream data);
你可以像这样实现它
public void Upload(Stream data)
{
MultipartParser.ParseFiles(
data,
WebOperationContext.Current.IncomingRequest.ContentType,
MyProcessMethod);
}
我在解析器方面遇到了一些问题,这些问题基于字符串解析,特别是对于大型文件,我发现它会耗尽内存,无法解析二进制数据。
为了解决这些问题,我已经在这里打开了一个C#multipart / form-data解析器的尝试
特征:
限制:
只需使用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.
}
在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);
...
}
或者像这样(稍微慢一些,但代码更友好):
public ResponseClass MyMethod(Stream multipartData)
{
var parser = new MultipartFormDataParser(data, Encoding.UTF8);
}
文档也可用,当您克隆存储库时,只需导航到HttpMultipartParserDocumentation/Help/index.html