I am really a newbie with streams, so I don't really know what I am doing here. :)
I have a XElement
containing XML. I want it to return it as a file to the user.
XElement xml = IndicesXMLGenerator.XML();
//Code for creating a memorystream for returning to browser as file
MemoryStream stream = new MemoryStream();
XmlTextWriter writer = new XmlTextWriter(stream, System.Text.Encoding.Unicode);
xml.Save(writer);
writer.Close();
//Code for direct saving to harddisk
FileStream filestream = new FileStream(@"D:\indices.xml", FileMode.Create);
XmlTextWriter writer2 = new XmlTextWriter(filestream, System.Text.Encoding.Unicode);
xml.Save(writer2);
writer2.Close();
filestream.Close();
//Return memorystream as fileresult
return base.File(new MemoryStream(stream.GetBuffer()), "text/xml", "AlleIndices.xml");
}
When I open the file that I got from my browser, it is totally mangled.
like: �< ? X M L
When I change the encoding in the code to UTF8
it gives me a normal looking document, but at the end I get a lot of 0x0
characters that make the document invalid.
Strange thing is that the XML file that I saved directly to the harddisk from within the code is:
- Perfe开发者_如何学JAVActly fine in it's encoding
- Doesn't contain any strange
0x0
characters
So, what's going on here? Why can't I easily stream my XElement to the browser as file?
Calling GetBuffer
will return the stream's internal buffer, which will be larger than the actual data. (In case you write some more to the stream)
You need to replace the call to GetBuffer
with ToArray()
, which will copy the used portion of the buffer to a new array.
However, the best way to do this is return the original MemoryStream, but first set the Position
property to 0, like this:
stream.Position = 0;
return File(stream, "text/xml", "AlleIndices.xml");
You don't need to create a newMemoryStream
, you can just reuse the same (you just need toSeek
to the beginning of the stream)The XML appears mangled because the browser doesn't know that it's encoded as Unicode. You need to add a Content-Encoding header to the response to specify the encoding
The MemoryStream
implements an array doubling technique so that it can resize efficiently as you add bytes. This means that the buffer is usually larger than the actual data.
There are a few solutions:
- Use
ToArray
instead ofGetBuffer
. - Use the
MemoryStream
constructor that takes an offset and length.
For example:
new MemoryStream(stream.GetBuffer(), 0, (int)stream.Length)
You will be much happier in the long run if you write your code like this:
XElement xml = IndicesXMLGenerator.XML();
using (MemoryStream ms = new MemoryStream())
using (XmlWriter xw = XmlWriter.Create(ms))
{
xml.Save(xw);
}
- Use
XmlWriter.Create
, notXmlTextWriter
, and useXmlWriterSettings
to control encoding and formatting. - Use
using
blocks rather than explicitly callingFlush
andClose
- that way you don't have to explicitly call them.
That said, none of that's actually your problem, which as noted is the use of GetBuffer
instead of ToArray
. But as I said, you'll be happier in the long run.
精彩评论