2012-02-11 60 views
3

使用HttpWebRequest的,我可以调用XDocument.Save()写直接请求流:使用HttpClient,如何将XDocument直接保存到请求流?

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

但是这创造XDocument的另一副本在MemoryStream

回答

1

XDocument.Save()预计可以写入的StreamStreamContent需要可以读取的流。所以,你可以使用两个Stream,其中一个充当另一个的转发者。我不认为在框架中存在这样的类型,但你可以自己写一个:

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); 
+0

呜!这是很多额外的代码。 – 2012-02-11 20:30:35

+0

看起来很多,但大部分只是样板代码。 – svick 2012-02-11 20:33:19

+1

@downvoter,关心评论? – svick 2012-02-11 21:08:37