C# StreamContent 和 File.OpenRead() 不产生 HTTP'able 多部分内容
Posted
技术标签:
【中文标题】C# StreamContent 和 File.OpenRead() 不产生 HTTP\'able 多部分内容【英文标题】:C# StreamContent and File.OpenRead() Not Producing HTTP'able multipart contentC# StreamContent 和 File.OpenRead() 不产生 HTTP'able 多部分内容 【发布时间】:2019-02-26 01:19:10 【问题描述】:我有一个 .Net Core 2.0 应用程序,它使用多部分内容将文件发送到 Web API 端点。我看过的所有地方,例如C# HttpClient 4.5 multipart/form-data upload,都让它看起来应该像将 FileStream 传递给 StreamContent 一样简单。但是,当我发帖时,看起来文件是作为文本附加的,而不是位。
实际代码:
var request = new HttpRequestMessage()
Method = HttpMethod.Post,
RequestUri = new Uri( "http://localhost:10442/filetest" )
;
var multiContent = new MultipartFormDataContent();
var filestream = File.OpenRead( path );
var filename = Path.GetFileName( path );
var streamContent = new StreamContent( filestream );
streamContent.Headers.Add( "Content-Type", "application/octet-stream" );
streamContent.Headers.Add( "Content-Disposition", $"form-data; name=\"file1\"; filename=\"filename\"" );
multiContent.Add( streamContent, "file", filename );
request.Content = multiContent;
var response = await new HttpClient().SendAsync( request );
请求看起来像这样,正如您可能注意到的那样,它并不都在一行中(我认为这是一个/THE 问题):
POST http://localhost:10442/filetest HTTP/1.1
Content-Type: multipart/form-data; boundary="c5295887-425d-4ec7-8638-20c6254f9e4b"
Content-Length: 88699
Host: localhost:10442
--c5295887-425d-4ec7-8638-20c6254f9e4b
Content-Type: application/octet-stream
Content-Disposition: form-data; name="file1"; filename="somepdf.pdf"
%PDF-1.7
%
1 0 obj
<</Type/Catalog/Version/1.7/Pages 3 0 R/Outlines 2 0 R/Names 8 0 R/Metadata 31 0 R>>
endobj
Fiddler 将整个帖子一直显示到末尾边界,但端点中的 await Request.Content.ReadAsStringAsync()
仅显示前几十个字节(看起来好像流还没有完成,但如果 Fiddler 得到了所有内容,我的端点不应该也有吗?)。
我在尝试访问远程端点时遇到了类似的问题;我构建了这个端点来进行本地测试。
我得到的例外是:“MIME 多部分流意外结束。MIME 多部分消息不完整。”对我来说,如果我真的只获得我的流的一部分,或者换行符正在抛出一些东西,这都是有意义的。
我也尝试将一些 Idisposables 放入 Usings,但正如预期的那样,这会关闭流并且我会以这种方式得到异常。
为了完整起见,这里是我调用的端点:
public async void ReceiveFiles()
// exception happens here:
var mpData = await Request.Content.ReadAsMultipartAsync();
await Task.FromResult( 0 );
【问题讨论】:
为什么要使用表单正文而不是分块传输编码,或者只是设置一个 Content-Length 标头并将文件字节放在一个请求正文中? 在答案***.com/a/59308653/5333683中将参数作为multipear dotnet 3.0 发送示例 【参考方案1】:试试这样的:
static int Main(string[] args)
var request = new HttpRequestMessage()
Method = HttpMethod.Post,
RequestUri = new Uri("http://localhost:10442/filetest")
;
var path = "c:\\temp\\foo.bak";
using (var filestream = File.OpenRead(path))
var length = filestream.Length.ToString();
var streamContent = new StreamContent(filestream);
streamContent.Headers.Add("Content-Type", "application/octet-stream");
streamContent.Headers.Add("Content-Length", length);
request.Content = streamContent;
Console.WriteLine($"Sending length bytes");
var response = new HttpClient().SendAsync(request).Result;
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
Console.WriteLine("Hit any key to exit");
Console.ReadKey();
return 0;
和
[HttpPost]
public async Task<IActionResult> Upload()
var buf = new byte[1024 * 64];
long totalBytes = 0;
using (var rs = Request.Body)
while (1 == 1)
int bytesRead = await rs.ReadAsync(buf, 0, buf.Length);
if (bytesRead == 0) break;
totalBytes += bytesRead;
var uploadedData = new
BytesRead = totalBytes
;
return new JsonResult(uploadedData) ;
【讨论】:
【参考方案2】:我正在尝试解决一个类似的问题,但我还不是 100% 的解决方案,但也许我的一些研究可以帮助你。
阅读 .NET 核心文件上传的 microsoft 文档对我很有帮助,特别是对于使用流和多部分表单数据的大文件: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-2.1#uploading-large-files-with-streaming
您已经引用了它,但此答案中有一些相关的有用信息: C# HttpClient 4.5 multipart/form-data upload
这解释了 content-disposition 标头的详细信息以及它如何与多部分表单数据请求一起使用:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#As_a_header_for_a_multipart_body
至于您将文件作为文本而不是位发送的具体问题,因为 http 是基于文本的,它只能作为文本发送,但该文本可以按照您认为合适的方式进行编码。也许您的 StreamContent 需要使用特定的编码,例如 base64 编码或类似的?我确实相信换行符在多部分请求中很重要,因此希望根据需要设置文件内容的编码就足够了。
另一种可能性:您是否需要在文件部分的标题或 StreamContent 的定义中设置附加信息以指示它应该继续,或者边界信息没有正确输入?见Multipart forms from C# client
【讨论】:
【参考方案3】:我使用这个库:https://github.com/jgiacomini/Tiny.RestClient 发送多部分文件更容易发送多部分文件。
这里有一个示例: 等待 client.PostRequest("MultiPart/Test")。 AsMultiPartFromDataRequest()。 AddStream(stream1, "request", "request2.bin")。 AddStream(stream2, "request", "request2.bin") 执行异步();
【讨论】:
以上是关于C# StreamContent 和 File.OpenRead() 不产生 HTTP'able 多部分内容的主要内容,如果未能解决你的问题,请参考以下文章
HttpClient StreamContent 附加文件名两次
C# Azure BlobClient 覆盖时在上传时设置 AccessTier
error while loading shared libraries: libg2o_core.so: cannot open shared object file: No such file o