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

Ðаn*_*Ðаn 5 .net rest httpwebrequest .net-4.5 dotnet-httpclient

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

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

是否可以用 做同样的事情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);
Run Code Online (Sandbox Code Playgroud)

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

svi*_*ick 2

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

\n\n
class ForwardingStream\n{\n    private readonly ReaderStream m_reader;\n    private readonly WriterStream m_writer;\n\n    public ForwardingStream()\n    {\n        // bounded, so that writing too much data blocks\n        var buffers = new BlockingCollection<byte[]>(10);\n        m_reader = new ReaderStream(buffers);\n        m_writer = new WriterStream(buffers);\n    }\n\n    private class ReaderStream : Stream\n    {\n        private readonly BlockingCollection<byte[]> m_buffers;\n        private byte[] m_currentBuffer;\n        private int m_readFromCurrent;\n\n        public ReaderStream(BlockingCollection<byte[]> buffers)\n        {\n            m_buffers = buffers;\n        }\n\n        public override void Flush()\n        {}\n\n        public override long Seek(long offset, SeekOrigin origin)\n        {\n            throw new NotSupportedException();\n        }\n\n        public override void SetLength(long value)\n        {\n            throw new NotSupportedException();\n        }\n\n        public override int Read(byte[] buffer, int offset, int count)\n        {\n            if (m_currentBuffer == null)\n            {\n                if (!m_buffers.TryTake(out m_currentBuffer, -1))\n                {\n                    return 0;\n                }\n                m_readFromCurrent = 0;\n            }\n\n            int toRead = Math.Min(count, m_currentBuffer.Length - m_readFromCurrent);\n\n            Array.Copy(m_currentBuffer, m_readFromCurrent, buffer, offset, toRead);\n\n            m_readFromCurrent += toRead;\n\n            if (m_readFromCurrent == m_currentBuffer.Length)\n                m_currentBuffer = null;\n\n            return toRead;\n        }\n\n        public override void Write(byte[] buffer, int offset, int count)\n        {\n            throw new NotSupportedException();\n        }\n\n        public override bool CanRead\n        {\n            get { return true; }\n        }\n\n        public override bool CanSeek\n        {\n            get { return false; }\n        }\n\n        public override bool CanWrite\n        {\n            get { return false; }\n        }\n\n        public override long Length\n        {\n            get { throw new NotSupportedException(); }\n        }\n\n        public override long Position\n        {\n            get { throw new NotSupportedException(); }\n            set { throw new NotSupportedException(); }\n        }\n    }\n\n    private class WriterStream : Stream\n    {\n        private readonly BlockingCollection<byte[]> m_buffers;\n\n        public WriterStream(BlockingCollection<byte[]> buffers)\n        {\n            m_buffers = buffers;\n        }\n\n        public override void Flush()\n        {}\n\n        public override long Seek(long offset, SeekOrigin origin)\n        {\n            throw new NotSupportedException();\n        }\n\n        public override void SetLength(long value)\n        {\n            throw new NotSupportedException();\n        }\n\n        public override int Read(byte[] buffer, int offset, int count)\n        {\n            throw new NotSupportedException();\n        }\n\n        public override void Write(byte[] buffer, int offset, int count)\n        {\n            if (count == 0)\n                return;\n\n            var copied = new byte[count];\n            Array.Copy(buffer, offset, copied, 0, count);\n\n            m_buffers.Add(copied);\n        }\n\n        public override bool CanRead\n        {\n            get { return false; }\n        }\n\n        public override bool CanSeek\n        {\n            get { return false; }\n        }\n\n        public override bool CanWrite\n        {\n            get { return true; }\n        }\n\n        public override long Length\n        {\n            get { throw new NotSupportedException(); }\n        }\n\n        public override long Position\n        {\n            get { throw new NotSupportedException(); }\n            set { throw new NotSupportedException(); }\n        }\n\n        protected override void Dispose(bool disposing)\n        {\n            m_buffers.CompleteAdding();\n\n            base.Dispose(disposing);\n        }\n    }\n\n    public Stream Reader\n    {\n        get { return m_reader; }\n    }\n\n    public Stream Writer\n    {\n        get { return m_writer; }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
XDocument doc = \xe2\x80\xa6;\n\nvar forwardingStream = new ForwardingStream();\n\nvar client = new HttpClient();\nvar content = new StreamContent(forwardingStream.Reader);\n\nTask.Run(() => doc.Save(forwardingStream.Writer));\n\nvar response = client.Post(url, content);\n
Run Code Online (Sandbox Code Playgroud)\n