使用 HttpClient,如何将 XDocument 直接保存到请求流中?

Posted

技术标签:

【中文标题】使用 HttpClient,如何将 XDocument 直接保存到请求流中?【英文标题】:using HttpClient, how can I save a XDocument directly to the request stream? 【发布时间】:2012-02-11 18:28:34 【问题描述】:

使用 HttpWebRequest,我可以调用 XDocument.Save() 将直接写入请求流:

XDocument doc = ...;
var request = (HttpWebRequest)WebCreate.Create(uri);
request.method = "POST";
Stream requestStream = request.GetRequestStream();
doc.Save(requestStream);

HttpClient 可以做同样的事情吗?直接的方法是

XDocument doc = ...;
Stream stream = new MemoryStream();
doc.Save(stream);
var content = new System.Net.Http.StreamContent(stream);
var client = new HttpClient();
client.Post(uri, content);

但这会在MemoryStream 中创建XDocument另一个副本。

【问题讨论】:

【参考方案1】:

XDocument.Save() 需要一个可以写入的StreamStreamContent 需要一个可以读取的流。因此,您可以使用两个Streams,其中一个充当另一个的转发器。我认为框架中不存在这种类型,但您可以自己编写一个:

class ForwardingStream

    private readonly ReaderStream m_reader;
    private readonly WriterStream m_writer;

    public ForwardingStream()
    
        // bounded, so that writing too much data blocks
        var buffers = new BlockingCollection<byte[]>(10);
        m_reader = new ReaderStream(buffers);
        m_writer = new WriterStream(buffers);
    

    private class ReaderStream : Stream
    
        private readonly BlockingCollection<byte[]> m_buffers;
        private byte[] m_currentBuffer;
        private int m_readFromCurrent;

        public ReaderStream(BlockingCollection<byte[]> buffers)
        
            m_buffers = buffers;
        

        public override void Flush()
        

        public override long Seek(long offset, SeekOrigin origin)
        
            throw new NotSupportedException();
        

        public override void SetLength(long value)
        
            throw new NotSupportedException();
        

        public override int Read(byte[] buffer, int offset, int count)
        
            if (m_currentBuffer == null)
            
                if (!m_buffers.TryTake(out m_currentBuffer, -1))
                
                    return 0;
                
                m_readFromCurrent = 0;
            

            int toRead = Math.Min(count, m_currentBuffer.Length - m_readFromCurrent);

            Array.Copy(m_currentBuffer, m_readFromCurrent, buffer, offset, toRead);

            m_readFromCurrent += toRead;

            if (m_readFromCurrent == m_currentBuffer.Length)
                m_currentBuffer = null;

            return toRead;
        

        public override void Write(byte[] buffer, int offset, int count)
        
            throw new NotSupportedException();
        

        public override bool CanRead
        
            get  return true; 
        

        public override bool CanSeek
        
            get  return false; 
        

        public override bool CanWrite
        
            get  return false; 
        

        public override long Length
        
            get  throw new NotSupportedException(); 
        

        public override long Position
        
            get  throw new NotSupportedException(); 
            set  throw new NotSupportedException(); 
        
    

    private class WriterStream : Stream
    
        private readonly BlockingCollection<byte[]> m_buffers;

        public WriterStream(BlockingCollection<byte[]> buffers)
        
            m_buffers = buffers;
        

        public override void Flush()
        

        public override long Seek(long offset, SeekOrigin origin)
        
            throw new NotSupportedException();
        

        public override void SetLength(long value)
        
            throw new NotSupportedException();
        

        public override int Read(byte[] buffer, int offset, int count)
        
            throw new NotSupportedException();
        

        public override void Write(byte[] buffer, int offset, int count)
        
            if (count == 0)
                return;

            var copied = new byte[count];
            Array.Copy(buffer, offset, copied, 0, count);

            m_buffers.Add(copied);
        

        public override bool CanRead
        
            get  return false; 
        

        public override bool CanSeek
        
            get  return false; 
        

        public override bool CanWrite
        
            get  return true; 
        

        public override long Length
        
            get  throw new NotSupportedException(); 
        

        public override long Position
        
            get  throw new NotSupportedException(); 
            set  throw new NotSupportedException(); 
        

        protected override void Dispose(bool disposing)
        
            m_buffers.CompleteAdding();

            base.Dispose(disposing);
        
    

    public Stream Reader
    
        get  return m_reader; 
    

    public Stream Writer
    
        get  return m_writer; 
    

不幸的是,您不能从同一个线程同时读取和写入这些流。但是你可以使用Task从另一个线程写:

XDocument doc = …;

var forwardingStream = new ForwardingStream();

var client = new HttpClient();
var content = new StreamContent(forwardingStream.Reader);

Task.Run(() => doc.Save(forwardingStream.Writer));

var response = client.Post(url, content);

【讨论】:

看起来很多,但大部分只是样板代码。

以上是关于使用 HttpClient,如何将 XDocument 直接保存到请求流中?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 HttpClient 将哈希值作为标头发送? [复制]

如何将异步服务用于角度 httpClient 拦截器

如何使用 HttpClient 将 JSON 数据发布到 Web API

如何使用 HttpClient 将带有 JSON 的 DELETE 发送到 REST API

如何使用 C# HttpClient 或 AWS SDK 通过 CloudFront 将视频上传到 S3

如何在Apache HttpClient中设置TLS版本