Display the content of a memory stream in web browser - c#

I'm working on a MVC project which creates a memory stream which is used for XSLT Transform. At the end, I want to display the results of the transformation on any web browser.
Following is memory stream is being created.
XslCompiledTransform xsl = new XslCompiledTransform();
xsl.Load(xsltpath);
MemoryStream stream = new MemoryStream();
XmlWriter xmlWriter = XmlWriter.Create(stream);
xsl.Transform(InputMessagePath, xmlWriter);
xmlWriter.Close();
// Pass or set the content of stream as a string or any other compatible type to the view to diplay
stream.Close();
Is it possible to display the contents of a memory stream in a web browser? If it's not possible what would be the best way to do that? I'm thinking about creating temporary file and pass its path to System.Diagnostics.Process.Start(path) as a parameter. But before that, it would be great to know the possibility of using the the stream object instead of creating a file to display the contents in a web browser.
Thank you.

You return a 'FileResult' and set content-type in the response:
You can find documentation here:
http://msdn.microsoft.com/en-us/library/system.web.mvc.controller.file%28v=vs.118%29.aspx
There's an overload that takes a stream.
return File(stream, "text/html; charset=utf-8");

Thanks for the replies. Just thought that it would be useful for someone, if I posted my implementation.
Following is procedure I used to display the content of memorystream. My requirement was to get the encoded html content of XSLT transform and display in a browser. Following is not exactly what I implemented. I simplified it to demonstrate the above requirement.
------------- Model ----------------------------------------
public class MyModel
{
public string EncodedOutputMessage { get; set; }
Public Transform()
{
// Do something
SetTransformResult(xsl, intputMsgPath);
}
private void SetTransformResult(XslCompiledTransform xsl, string intputMsgPath)
{
MemoryStream stream = new MemoryStream();
XmlWriter xmlWriter = XmlWriter.Create(stream);
xsl.Transform(intputMsgPath, xmlWriter);
xmlWriter.Close();
XmlDocument root = new XmlDocument();
stream.Position = 0; // rewind the pointer to the beginning of the stream
root.Load(stream);
XmlNodeList nodes = root.GetElementsByTagName("BodyXhtml");
if (nodes.Count == 1)
{
EncodedOutputMessage = HttpUtility.HtmlEncode(nodes[0].InnerXml);
}
stream.Close();
}
}
-------------- Controller -----------------------------------
public class MyTestController : Controller
{
[HttpPost]
public ActionResult ShowResult()
{
MyModel model = new MyModel();
model.Transform();
ViewBag.DecodedOutputMessage = HttpUtility.HtmlDecode(model.EncodedOutputMessage);
return View("MyView");
}
}
----- MyView --------------------------------------------------------------
#model Tester.Models.MyModel
// Other fields to display
#Html.Raw(ViewBag.DecodedOutputMessage);
The action method do the transform and get the encoded string, then decode it and send it to the view. I have removed most of the methods to simplify this procedure.
If you don't have any other content to display in the browser, you can pass only the HTML string, something like this.
public string Result()
{
string htmlRes = "<html><body><font color=\"red\"> Testing color... </font></body></html>";
return htmlRes ;
}

I do this:
MemoryStream stream = new MemoryStream(System.Text.UTF8Encoding.Default.GetBytes(xml));
XPathDocument document = new XPathDocument(stream);
StringWriter writer = new StringWriter();
XslCompiledTransform transform = new XslCompiledTransform();
transform.Load(Server.MapPath("~/NFCe.xsl"));
transform.Transform(document, null, writer);
Response.Write(writer.ToString());
Th XML result is sent to the client. In that case I have a variable 'xml' with my original xml, them I'm applying a transformation contained in the file NFCE.xsl. The Response.Write writes a string to the output stream, which is sent to the client, instead of the rendered aspx.

Related

AddAttachment from MemoryStream

The SendGrid API docs specify you can add attachments from a Stream. The example it gives uses a FileStream object.
I have some blobs in Azure Storage which I'd like to email as attachments. To achieve this I'm trying to use a MemoryStream:
var getBlob = blobContainer.GetBlobReferenceFromServer(fileUploadLink.Name);
if(getBlob != null)
{
// Get file as a stream
MemoryStream memoryStream = new MemoryStream();
getBlob.DownloadToStream(memoryStream);
emailMessage.AddAttachment(memoryStream, fileUploadLink.Name);
}
emailTransport.Deliver(emailMessage);
It sends fine but when the email arrives, the attachment appears to be there but it's actually empty. Looking at the email source, there is no content for the attachment.
Is using a MemoryStream a known limitation when using the SendGrid C# API to send attachments? Or should I be approaching this in some other way?
You probably just need to reset the stream position back to 0 after you call DownloadToStream:
var getBlob = blobContainer.GetBlobReferenceFromServer(fileUploadLink.Name);
if (getBlob != null)
{
var memoryStream = new MemoryStream();
getBlob.DownloadToStream(memoryStream);
memoryStream.Seek(0,SeekOrigin.Begin); // Reset stream back to beginning
emailMessage.AddAttachment(memoryStream, fileUploadLink.Name);
}
emailTransport.Deliver(emailMessage);
You might want to check who cleans up the stream as well and if they don't you should dispose of it after you've called Deliver().
According to their API, they have implemented void AddAttachment(Stream stream, String name).
You are probably using a MemoryStream which you have written to before. I suggest resetting the position inside the stream to the beginning, like:
memoryStream.Seek(0, SeekOrigin.Begin);
I ended up with the following which fixed the issue for me:
fileByteArray = new byte[getBlob.Properties.Length];
getBlob.DownloadToByteArray(fileByteArray, 0);
attachmentFileStream = new MemoryStream(fileByteArray);
emailMessage.AddAttachment(attachmentFileStream, fileUploadLink.Name);
The thread is a bit old, but I use a varient with NReco PDF converter:
private async Task SendGridasyncBid(string from, string to, string displayName, string subject, **byte[] PDFBody**, string TxtBody, string HtmlBody)
{
...
var myStream = new System.IO.MemoryStream(**PDFBody**);
myStream.Seek(0, SeekOrigin.Begin);
myMessage.AddAttachment(myStream, "NewBid.pdf");
...
}
convert the html to pdf and return it instead of writing it for download...
private byte[] getHTML(newBidViewModel model)
{
string strHtml = ...;
HtmlToPdfConverter pdfConverter = new HtmlToPdfConverter();
pdfConverter.CustomWkHtmlArgs = "--page-size Letter";
var pdfBytes = pdfConverter.GeneratePdf(strHtml);
return **pdfBytes**;
}
I am not sure how efficient this is, but it is working for me and I hope it helps someone else get their attachments figured out.

How to return xml as UTF-8 instead of UTF-16

I am using a routine that serializes <T>. It works, but when downloaded to the browser I see a blank page. I can view the page source or open the download in a text editor and I see the xml, but it is in UTF-16 which I think is why browser pages show blank?
How do I modify my serializer routine to return UTF-8 instead of UTF-16?
The XML source returned:
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>January</string>
<string>February</string>
<string>March</string>
<string>April</string>
<string>May</string>
<string>June</string>
<string>July</string>
<string>August</string>
<string>September</string>
<string>October</string>
<string>November</string>
<string>December</string>
<string />
</ArrayOfString>
An example call to the serializer:
DateTimeFormatInfo dateTimeFormatInfo = new DateTimeFormatInfo();
var months = dateTimeFormatInfo.MonthNames.ToList();
string SelectionId = "1234567890";
return new XmlResult<List<string>>(SelectionId)
{
Data = months
};
The Serializer:
public class XmlResult<T> : ActionResult
{
private string filename = DateTime.Now.ToString("ddmmyyyyhhss");
public T Data { private get; set; }
public XmlResult(string selectionId = "")
{
if (selectionId != "")
{
filename = selectionId;
}
}
public override void ExecuteResult(ControllerContext context)
{
HttpContextBase httpContextBase = context.HttpContext;
httpContextBase.Response.Buffer = true;
httpContextBase.Response.Clear();
httpContextBase.Response.AddHeader("content-disposition", "attachment; filename=" + filename + ".xml");
httpContextBase.Response.ContentType = "text/xml";
using (StringWriter writer = new StringWriter())
{
XmlSerializer xml = new XmlSerializer(typeof(T));
xml.Serialize(writer, Data);
httpContextBase.Response.Write(writer);
}
}
}
You can use a StringWriter that will force UTF8. Here is one way to do it:
public class Utf8StringWriter : StringWriter
{
// Use UTF8 encoding but write no BOM to the wire
public override Encoding Encoding
{
get { return new UTF8Encoding(false); } // in real code I'll cache this encoding.
}
}
and then use the Utf8StringWriter writer in your code.
using (StringWriter writer = new Utf8StringWriter())
{
XmlSerializer xml = new XmlSerializer(typeof(T));
xml.Serialize(writer, Data);
httpContextBase.Response.Write(writer);
}
answer is inspired by Serializing an object as UTF-8 XML in .NET
Encoding of the Response
I am not quite familiar with this part of the framework. But according to the MSDN you can set the content encoding of an HttpResponse like this:
httpContextBase.Response.ContentEncoding = Encoding.UTF8;
Encoding as seen by the XmlSerializer
After reading your question again I see that this is the tough part. The problem lies within the use of the StringWriter. Because .NET Strings are always stored as UTF-16 (citation needed ^^) the StringWriter returns this as its encoding. Thus the XmlSerializer writes the XML-Declaration as
<?xml version="1.0" encoding="utf-16"?>
To work around that you can write into an MemoryStream like this:
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream, Encoding.UTF8))
{
XmlSerializer xml = new XmlSerializer(typeof(T));
xml.Serialize(writer, Data);
// I am not 100% sure if this can be optimized
httpContextBase.Response.BinaryWrite(stream.ToArray());
}
Other approaches
Another edit: I just noticed this SO answer linked by jtm001. Condensed the solution there is to provide the XmlSerializer with a custom XmlWriter that is configured to use UTF8 as encoding.
Athari proposes to derive from the StringWriter and advertise the encoding as UTF8.
To my understanding both solutions should work as well. I think the take-away here is that you will need one kind of boilerplate code or another...
To serialize as UTF8 string:
private string Serialize(MyData data)
{
XmlSerializer ser = new XmlSerializer(typeof(MyData));
// Using a MemoryStream to store the serialized string as a byte array,
// which is "encoding-agnostic"
using (MemoryStream ms = new MemoryStream())
// Few options here, but remember to use a signature that allows you to
// specify the encoding
using (XmlTextWriter tw = new XmlTextWriter(ms, Encoding.UTF8))
{
tw.Formatting = Formatting.Indented;
ser.Serialize(tw, data);
// Now we get the serialized data as a string in the desired encoding
return Encoding.UTF8.GetString(ms.ToArray());
}
}
To return it as XML on a web response, don't forget to set the response encoding:
string xml = Serialize(data);
Response.ContentType = "application/xml";
Response.ContentEncoding = System.Text.Encoding.UTF8;
Response.Output.Write(xml);

MemoryStream to virtual file

I have the following:
using(var memoryStream = new MemoryStream())
{
gc.CreatePackage(memoryStream);
}
MemoryStream spits out an excel fil. From the memoryStream, how do I go about actually showing the file that is produced. Note that I do not want to actually save the file to disk but merely display it.
So far I have the following but doesn't seem to work:
using (var memoryStream = new MemoryStream())
{
gc.CreatePackage(memoryStream);
using (var fileStream = File.OpenWrite("Test.xlsx"))
{
memoryStream.WriteTo(fileStream);
}
}
But not sure if I am on the right direction. I get an error saying:
System.Web.Mvc.Controller.File(byte[], string)' is a 'method', which
is not valid in the given context
I am not sure if I am going about this in the right direction.
Making the assumption that you are using ASP.NET MVC, you probably want the File response helper:
using (var memoryStream = new MemoryStream())
{
gc.CreatePackage(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
return File(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
}
If you want to have the file source actually appear in the browser, you can also lie about the MIME type and use text/plain instead. The browser will most likely render this as plain text.
You can also add a third parameter to File in order to specify the filename the download should appear to the end-user to be.
Since the error you post seems to indicate that you're trying to return a generated file from an MVC controller, I think this may be what you're looking for.
public ActionResult MyAction()
{
using (var memoryStream = new MemoryStream())
{
gc.CreatePackage(memoryStream);
//Make sure the position of the stream is at 0
memoryStream.Position = 0;
//Return the contents of the stream with the appropriate MIME type
return File(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
}
}
You could use a MemoryMappedFile to create a virtual file.
example code:
Write:
using (var mmf = MemoryMappedFile.CreateNew("MappedFileName", size, MemoryMappedFileAccess.ReadWriteExecute))
{
using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor())
{
accessor.Write(Write your data to mapped file);
}
}
Read:
using (var mmf = MemoryMappedFile.OpenExisting("MappedFileName"))
{
using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor())
{
accessor.Read......
}
}

Modifying Word Document XML using Packages

I am attempting to modify a simple MS word templates XML. I realize there are SDK's available that could make this process easier but what I am tasked with maintaining uses packages and I was told to do the same.
I have a basic test document with two placeholders mapped to the following XML:
<root>
<element>
Fubar
</element>
<second>
This is the second placeholder
</second>
</root>
What I am doing is creating a stream using the word doc, removing the existing XML, getting some hard coded test XML and trying to write that to the stream.
Here is the code I am using:
string strRelRoot = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
byte[] buffer = File.ReadAllBytes("dev.docx");
//stream with the template
MemoryStream stream = new MemoryStream(buffer, true);
//create a package using the stream
Package package = Package.Open(stream, FileMode.Open, FileAccess.ReadWrite);
PackageRelationshipCollection pkgrcOfficeDocument = package.GetRelationshipsByType(strRelRoot);
foreach (PackageRelationship pkgr in pkgrcOfficeDocument)
{
if (pkgr.SourceUri.OriginalString == "/")
{
Uri uriData = new Uri("/customXML/item1.xml", UriKind.Relative);
//remove the existing part
if (package.PartExists(uriData))
{
// Delete template "/customXML/item1.xml" part
package.DeletePart(uriData);
}
//create a new part
PackagePart pkgprtData = package.CreatePart(uriData, "application/xml");
//test data
string xml = #"<root>
<element>
Changed
</element>
<second>
The second placeholder changed
</second>
</root>";
//stream created from the xml string
MemoryStream fromStream = new MemoryStream();
UnicodeEncoding uniEncoding = new UnicodeEncoding();
byte[] fromBuffer = uniEncoding.GetBytes(xml);
fromStream.Write(fromBuffer, 0, fromBuffer.Length);
fromStream.Seek(0L, SeekOrigin.Begin);
Stream toStream = pkgprtData.GetStream();
//copy the xml to the part stream
fromStream.CopyTo(toStream);
//copy part stream to the byte stream
toStream.CopyTo(stream);
}
}
This is currently not modifying the document although I feel like I am close to a solution. Any advice would be very much appreciated. Thanks!
Edit: To clairify, the result I am getting is the document is unchanged. I get no exceptions or the like, but the documents XML is not modified.
OK, so not quite the timely response I promised, but here goes!
There are several aspects to the problem. Sample code is from memory and documentation, not necessarily compiled and tested.
Read the template XML
Before you delete the package part containing the template XML, you need to open its stream and read the XML. How you get the XML if the part doesn't exist to begin with is up to you.
My example code uses classes from the LINQ to XML API, though you could use whichever set of XML APIs you prefer.
XElement templateXml = null;
using (Stream stream = package.GetPart(uriData))
templateXml = XElement.Load(stream);
// Now you can delete the part.
At this point you have an in-memory representation of the template XML in templateXml.
Substitute values into the placeholders
templateXml.SetElementValue("element", "Replacement value of first placeholder");
templateXml.SetElementValue("second", "Replacement value of second placeholder");
Check out the methods on XElement if you need to do anything more advanced than this, e.g. read the original content in order to determine the replacement value.
Save the document
This is your original code, modified and annotated.
// The very first thing to do is create the Package in a using statement.
// This makes sure it's saved and closed when you're done.
using (Package package = Package.Open(...))
{
// XML reading, substituting etc. goes here.
// Eventually...
//create a new part
PackagePart pkgprtData = package.CreatePart(uriData, "application/xml");
// Don't need the test data anymore.
// Assuming you need UnicodeEncoding, set it up like this.
var writerSettings = new XmlWriterSettings
{
Encoding = Encoding.Unicode,
};
// Shouldn't need a MemoryStream at all; write straight to the part stream.
// Note using statements to ensure streams are flushed and closed.
using (Stream toStream = pkgprtData.GetStream())
using (XmlWriter writer = XmlWriter.Create(toStream, writerSettings))
templateXml.Save(writer);
// No other copying should be necessary.
// In particular, your toStream.CopyTo(stream) appeared
// to be appending the part's data to the package's stream
// (the physical file), which is a bug.
} // This closes the using statement for the package, which saves the file.

How can I save HtmlDocument to memory? Html Agility Pack

I am using HTML Agility Pack to parse and HTML document, make a change to a node, and then save the HTML document. I would like to save the document to memory so I can write the HTML out as a string later in the application. My current implementation always returns a string == "". I can see that the HtmlDocument object is not empty when debugging. Can someone provide some insight?
private string InitializeHtml(HtmlDocument htmlDocument)
{
string currentUserName = User.Identity.Name;
HtmlNode scriptTag = htmlDocument.DocumentNode.SelectSingleNode("//script[#id ='HwInitialize']");
scriptTag.InnerHtml =
string.Format("org.myorg.application = {{}}; org.myorg.application.init ={{uid:\"{0}\", application:\"testPortal\"}};",currentUserName);
MemoryStream memoryStream = new MemoryStream();
htmlDocument.Save(memoryStream);
StreamReader streamReader = new StreamReader(memoryStream);
return streamReader.ReadToEnd();
}
Try
memoryStream.Seek(0, System.IO.SeekOrigin.Begin)
Before creating the StreamReader and calling ReadToEnd()
The stream pointer is likely getting left at the end of the stream by the Save method (it's best practise for a component to do this - in case you want to append more data to the stream) therefore when you call ReadToEnd, it's already at the end and nothing gets read.

Categories

Resources