.NET 中如何获取可写 Stream 将内容发送到 HTTP 服务器?

问题描述 投票:0回答:3

我已经能够使用以下方法从 HTTP API 读取原始二进制内容:

var response = httpClient.GetStreamAsync("http://...");

但是如何做同样的写作

到目前为止,我还没有找到打开

Stream
来异步写入服务器的方法。

方法

HttpClient.PostAsync(...)
对我来说没有用,因为它将控制服务器的读写过程。

我的目标是拥有一个可以写入的

Stream
,就像任何其他流一样。

这可能吗?

c# .net http httpclient
3个回答
3
投票

查看

HttpContent
类,这实际上应该很容易实现。您可以在使用回调方法的情况下进行自己的实现。然后,将使用您需要写入的
Stream
来调用此回调。

实施:

public class HttpContentCallback : HttpContent
{
    private readonly Func<Stream, Task> _callback;

    public HttpContentCallback(Func<Stream, Task> callback)
    {
        _callback = callback;
    }

    protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context)
    {
        return _callback(stream);
    }

    protected override bool TryComputeLength(out long length)
    {
        length = 0;
        return false;
    }
}

按照 @Charlieface 的建议,具有取消令牌支持(.NET 5 或更高版本):

public class HttpContentCallback : HttpContent
{
    private readonly Func<Stream, CancellationToken, Task> _callback;

    public HttpContentCallback(Func<Stream, CancellationToken, Task> callback)
    {
        _callback = callback;
    }

    protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context)
    {
        return SerializeToStreamAsync(stream, context, CancellationToken.None);
    }

    protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context, CancellationToken token)
    {
        return _callback(stream, token);
    }

    protected override bool TryComputeLength(out long length)
    {
        length = 0;
        return false;
    }
}

1
投票

使用当前的 API 做你想做的事情有点困难。

根据您的要求,您可能只想“传递”现有流。在这种情况下使用 StreamContent。请注意,在进行调用时需要填充流(或以其他方式完全可读)。例如,您可以通过这种方式传递

FileStream
using var fs = new FileStream("someFile");
using var message = new HttpRequestMessage(HttpMethod.Post, "http://someurl.com");
message.Content = new StreamContent(fs);
using var resp = await _client.SendAsync(message);
// etc

这将是发送大多数请求的最简单方法,因为您通常要发送另一个流。

如果您希望能够拦截调用以检索流并填充它,那么情况会更复杂一些。您需要创建自己的
HttpContent

类,至少实现必要的最小覆盖。

HttpContent
的文档中给出了一个示例。


0
投票
HttpClient

类不支持以您想要的方式写入流,您必须使用旧的

HttpWebRequest
类。
using System;
using System.IO;
using System.Net;

internal class Program
{
    public static void Main(string[] args)
    {
        string filePath = "C:/example.bin"; 
        string uploadUrl = "http://example.com/upload"; 
        
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uploadUrl);        
        request.Method = "POST";        
        request.ContentType = "application/octet-stream";
        
        byte[] fileData = File.ReadAllBytes(filePath);
        request.ContentLength = fileData.Length;
        
        using (Stream requestStream = request.GetRequestStream())
        {
            int bufferSize = 4096; 
            int bytesWritten = 0; 
            while (bytesWritten < fileData.Length)
            {
                int bytesToWrite = Math.Min(bufferSize, fileData.Length - bytesWritten); 
                requestStream.Write(fileData, bytesWritten, bytesToWrite); 
                bytesWritten += bytesToWrite; 
            }
        }
        
        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        
        using (Stream responseStream = response.GetResponseStream())
        using (StreamReader reader = new StreamReader(responseStream))
        {
            Console.WriteLine(reader.ReadToEnd());
        }
        
        response.Close();
    }
}

请注意,
Microsoft 建议

您不要在新开发中使用此类。 但是,这个问题可能是一个

XY问题

如果您的根本问题是“如何实现进度条”,并且您正在寻求帮助来实现“在循环中使用流”的解决方案,那么有一个替代解决方案可以实现效果很好的进度条与

HTTPClient

班一起。

private async Task UploadFileWithProgress(string url, string filePath)
{
    using (var httpClient = new HttpClient())
    {
        using (var fileStream = File.OpenRead(filePath))
        {
            var content = new StreamContent(fileStream);
            content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
            {
                FileName = Path.GetFileName(filePath)
            };
            content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");

            var progressContent = new ProgressableStreamContent(content, 4096, UploadProgressCallback);
            var response = await httpClient.PostAsync(url, progressContent);
            var responseContent = await response.Content.ReadAsStringAsync();
            Console.WriteLine(responseContent);
        }
    }
}

private void UploadProgressCallback(long bytesUploaded, long totalBytes)
{
    // Update progress bar here
}

	
© www.soinside.com 2019 - 2024. All rights reserved.