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

发布于 2025-01-04 19:47:29 字数 628 浏览 1 评论 0原文

使用 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另一个副本。

Using HttpWebRequest, I can call XDocument.Save() to write directly to the request stream:

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

Is it possible to do the same thing with HttpClient? The straight-forward way is

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

But this creates another copy of the XDocument in the MemoryStream.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

谁许谁一生繁华 2025-01-11 19:47:29

XDocument.Save() 需要一个可写入的 StreamStreamContent 需要一个可以读取的流。因此,您可以使用两个 Stream,其中一个充当另一个 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);

XDocument.Save() expects a Stream that can be written to. StreamContent expects a stream that can be read. So, you can use a two Streams, where one acts as as a forwarder for the other one. I don't think such type exists in the framework, but you can write one yourself:

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

Unfortunately, you can't read and write to those streams at the same time from the same thread. But you can use Task to write from another thread:

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);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文