C#StreamContent和File.OpenRead()无法产生HTTP'able多部分内容

时间:2018-09-21 15:33:02

标签: c# httpclient .net-core-2.0

我有一个.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分段消息不完整。”对我来说,这既有意义,无论我是真的只是流中的一部分,还是换行符引发了一些麻烦。

我也曾尝试将一些Idisposable扔到Usings中,但是,正如预期的那样,它关闭了流并以这种方式获得异常。

为了完整起见,这是我要呼叫的端点:

public async void ReceiveFiles()
{
    // exception happens here:
    var mpData = await Request.Content.ReadAsMultipartAsync();
    await Task.FromResult( 0 );
}

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

答案 1 :(得分:0)

我正在尝试解决类似的问题,但我还没有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

这说明了内容处理标头的详细信息以及如何将其用于多部分表单数据请求: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

答案 2 :(得分:0)

我使用这个lib:https://github.com/jgiacomini/Tiny.RestClient 发送多文件文件和多部分文件变得更加容易。

这里有一个样本: 等待client.PostRequest(“ MultiPart / Test”)。               AsMultiPartFromDataRequest()。               AddStream(stream1,“ request”,“ request2.bin”)。               AddStream(stream2,“ request”,“ request2.bin”)               ExecuteAsync();