1
votes

I have an HTTP Handler that is binding an XmlTextWriter instance to Response.Output like so...

Sub GenerateXml(ByRef Response As HttpWebResponse)
    Using Writer As New XmlTextWriter(Response.Output)
        ' Build XML
    End Using
End Sub

And now I also want the same XML to be saved to the local hard drive (in addition to being streamed to the response). How can I do it?

Things I have tried

1) The obvious, copying the Response.OutputStream to a new FileStream...

    Sub GenerateXml(ByRef Response As HttpWebResponse)
        Using Writer As New XmlTextWriter(Response.Output)
            Build XML Here
        End Using

        // Also tried this inside the Using, with and without .Flush()
        CopyStream(Response.OutputStream, File.Create("C:\test.xml"))
    End Sub

    Sub CopyStream(ByRef Input As Stream, ByRef Output As Stream)
        Dim Buffer(1024) As Byte
        Dim Read As Integer

        Using Input
            Using Output
                While (Read = Input.Read(Buffer, 0, Buffer.Length)) > 0
                    Output.Write(Buffer, 0, Read)
                End While
            End Using
        End Using
    End Sub

I get errors like "Method not supported" and "NullReference"

2) Using a Memory Stream and then copying it...

    Using Memory As New MemoryStream()
        Using Writer As New XmlTextWriter(Memory, Encoding.UTF8)
            Build XML Here
        End Using

        // Tried outside of the Using, with and without Flush()
        CopyStream(Memory, Response.OutputStream)
        CopyStream(Memory, File.Create("C:\test.xml"))
    End Using

I get errors like "Cannot access a closed Stream."

Why is something so simple such a PITA?!

4
Do I really have to resort to writing the file to disk and then using Response.TransmitFile()?Josh Stodola

4 Answers

2
votes

You can't wire up Response.OutputStream in that manner. However, you could do one of two things:

  1. Create a method that calls Stream.Write for both your test file and Response.OutputStream.

    using (var writerResponse = new XmlTextWriter(Response.OutputStream))
    using (var writerFile = new XmlTextWriter(File.Create(@"c:\test.xml"))
    {
        StartElement(writerResponse, writerFile, @"test");
    }
    
  2. Create a wrapper class which encapsulates two streams to be written to and provides all of the stream methods you require (Write etc).

    public class DualStream : Stream
    {
        public DualStream(Stream first, Stream second)
        {
        }
    
        public override bool CanRead { get { return false; } }
        public override bool CanWrite { get { return true; } }
    
        public override void Write(byte[] buffer, int offset, int count)
        {
            this.first.Write(buffer, offset, count);
            this.second.Write(buffer, offset, count);
        }
    }
    
    // ...
    
    using (var writerFile = new XmlTextWriter(File.Create(@"c:\test.xml")))
    using (var writer = new XmlTextWriter(
          new DualStream(Response.OutputStream, writerFile)), null))
    {
         // use writer
    }
    
1
votes

You need to reset the memorystream position to 0.

i.e.:

Memory.Position = 0
0
votes

Have you tried the save method?

http://msdn.microsoft.com/en-us/library/system.xml.xmldocument.save%28VS.71%29.aspx

I don't know if you will be able to use it in your context.

0
votes

I found this EchoStream class on codeproject by googling for .net stream tee (tee being the unix-y word for what you want).