How to produce a file to download containing a JSON structure? - c#

I have this method in my controller.
public IActionResult Download()
{
return Json(_context.Users);
}
I noticed that it produces the correct JSON structure but it's being rendered in the browser as common text. I want it to be downloaded to the client's computer. How do I do that?
I'm not sure if is should make my object to stream somehow like this or maybe create a file on my hard drive and serve it like this.
I can't find anything that strikes me as straight-forward and simple like we're used to in C#. So I fear that I'm missing a concept here.

You can just write json object to a stream or array and use one of File method overloads. Add convenient Serialize method
private byte[] Serialize(object value, JsonSerializerSettings jsonSerializerSettings)
{
var result = JsonConvert.SerializeObject(value, jsonSerializerSettings);
return Encoding.UTF8.GetBytes(result);
}
And use it as following
public IActionResult Download()
{
var download = Serialize(_context.Users, new JsonSerializerSettings());
return File(download , "application/json", "file.json");
}
If you set special json serializer settings in Startup using .AddJsonOptions() you would like to use them as ASP.NET framework uses them in Json method. Inject MvcJsonOptions in controller
IOptions<MvcJsonOptions> _options;
public YourController(IOptions<MvcJsonOptions> options)
{
_options = options;
}
And pass settings to method
public IActionResult Download()
{
var download = Serialize(_context.Users, _options.Value.SerializerSettings);
return File(download , "application/json", "file.json");
}

Convert the data into bytes then those bytes into a FileResult. You return the FileResult and the browser will do whatever it does normally when presented with a 'file', usually either prompt the user or download.
Example below:
public ActionResult TESTSAVE()
{
var data = "YourDataHere";
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(data);
var output = new FileContentResult(bytes, "application/octet-stream");
output.FileDownloadName = "download.txt";
return output;
}
In your case you would simply take your JSON data as a string.

Related

Returning a PDF from an ASP.NET Core 2 Controller

I am trying to return a PDF file from my ASP.NET Core 2 controller.
I have this code
(mostly borrowed from this SO question):
var net = new System.Net.WebClient();
//a random pdf file link
var fileLocation = "https://syntera.io/documents/T&C.pdf";/
var data = net.DownloadData(fileLocation);
MemoryStream content = null;
try
{
content = new MemoryStream(data);
return new FileStreamResult(content, "Application/octet-stream");
}
finally
{
content?.Dispose();
}
This code above is part of a service class that my controller calls. This is the code from my controller.
public async Task<IActionResult> DownloadFile(string fileName)
{
var result = await _downloader.DownloadFileAsync(fileName);
return result;
}
But I keep getting ObjectDisposedException: Cannot access a closed Stream.
The try and finally block was an attempt to fix it , from another SO question .
The main question is A) Is this the right way to send a PDF file back to the browser and B) if it isn't, how can I change the code to send the pdf to the browser?
Ideally , I don't want to first save the file on the server and then return it to the controller. I'd rather return it while keeping everything in memory.
The finally will always get called (even after the return) so it will always dispose of the content stream before it can be sent to the client, hence the error.
Ideally , I don't want to first save the file on the server and then return it to the controller. I'd rather return it while keeping everything in memory.
Use a FileContentResult class to take the raw byte array data and return it directly.
FileContentResult: Represents an ActionResult that when executed will write a binary file to the response.
async Task<IActionResult> DownloadFileAsync(string fileName){
using(var net = new System.Net.WebClient()) {
byte[] data = await net.DownloadDataTaskAsync(fileName);
return new FileContentResult(data, "application/pdf") {
FileDownloadName = "file_name_here.pdf"
};
}
}
No need for the additional memory stream
You must specify :
Response.AppendHeader("content-disposition", "inline; filename=file.pdf");
return new FileStreamResult(stream, "application/pdf")
For the file to be opened directly in the browser.

How use the Byte Array of a image?

So, i am getting the byte array of a LongRaw image from Oracle...
I am using a webapi to this. After get the array, how i use it on the Client-side ?
Do Its better i convert to base64string and pass this value converting just at the client side ?
cmd.InitialLONGFetchSize = -1;
var reader = cmd.ExecuteReader();
if (reader.Read())
{
// Fetch the LONG RAW
OracleBinary imgBinary = reader.GetOracleBinary(0);
// Get the bytes from the binary obj
byte[] imgBytes = imgBinary.IsNull ? null : imgBinary.Value;
//var imgString = Uri.EscapeDataString(Convert.ToBase64String(imgBytes));
}
//CRIO A LISTA
lretorno.Load(reader, LoadOption.OverwriteChanges, "BUSCAFOTO");
reader.Close();
connection.Close();
connection.Dispose();
var teste = lretorno.Tables[0].AsEnumerable().Select(row => new FotoEnvolvido
{
FOTO = (byte[])(row["FOTO"]),
//FOTO = Convert.ToString(row["FOTO"]),
});
return teste;
You can write a Web API Controller that returns the binary data of an image. Base64 strings impose a overhead of the amount of bytes that have to be transmitted. Please avoid this.
A sample controller can look like this example:
public class WebApiController : ApiController
{
public async Task<HttpResponseMessage> Get(string id)
{
var bytes = await GetBytesFromDataLayerAsync(id);
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new MemoryStream(bytes);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("image/jpeg");
return result;
}
private async Task<byte[]> GetBytesFromDataLayerAsync(string id)
{
// put your Oracle logic here
return ...
}
}
Depending on what your doing as rboe said writing the bytes directly to the client will save some data size(approx. 37%) and computing overhead. If your not only displaying jpeg images you should also set the mime-type to the correct value... take a look at this source for a rather complete set of extension to mime-type mappings. If you do not know the mime-type you can try "application/octet-stream" as that is the general mime-type for binary data.
If your displaying your content via web browser you could just use an <img> tag something like <img src="view_image.aspx?id=5"> you can even create the dynamically with javascript/jQuery.
If you really do want the image data embedded in a json request which might be useful if you have a lot of little icons and don't want a ton of requests (with http/2 I don't think this will matter) or another reason, then yes first encode the binary data using...
string base64EncodedData = Convert.ToBase64String(bytes);
If the client is javascript you can decode using the latest browsers native functions
var decodedImageData = window.atob(base64EncodedData);
See:
mozilla.org docs
This answer
This answer
If you are however just sending it to another c# endpoint you can use...
byte[] decodedImageData = Convert.FromBase64String(base64EncodedData);
And like I mentioned in the comment to ensure it's encrypted just make the site only support https:// and if you don't have a SSL cert you can get one for free from http://startssl.com

Access the RAW XML data in Web API

I am using Web API to receive XML data and convert it to an Object. Which is working fine.
public void Post([FromBody] trackermessages model)
{
try
{
How do I get the RAW XML data? Is there a way to get the XML data as the Request begins or inside this action?
I tried this:
public void Post([FromBody] trackermessages model, [FromBody] string rawText)
{
try
{
But this is not allowed.
I also tried this:
public void Post([FromBody] trackermessages model)
{
try
{
var strean = new StreamReader(HttpContext.Current.Request.InputStream).ReadToEnd();
But I get the Exception:
This method or property is not supported after
HttpRequest.GetBufferlessInputStream has been invoked.
EDIT:
I am getting the Exception:
var stream = await Request.Content.ReadAsStreamAsync();
stream.Seek(0, System.IO.SeekOrigin.Begin); // On this Line
StreamReader reader = new StreamReader(stream);
string text = reader.ReadToEnd();
This is how I did it, because I had to read the RAW data then convert to Object:
public void Post(HttpRequestMessage request)
{
// Reading data as XML string to log to files - In case message structure is changed
var xmlDoc = new XmlDocument();
xmlDoc.Load(request.Content.ReadAsStreamAsync().Result);
var str = xmlDoc.InnerXml;
// Convert to model
var model = XMLHelper.FromXml<trackermessages>(str);
}
And the XMLHelper was copied from another question on stackoverflow: https://stackoverflow.com/a/3187539/1910735
Yes, you can get the raw XML. You do need to seek back to the start of the stream since it will have been read to the end when processing the Model.
public async void Post([FromBody]TestModel value)
{
var stream = await this.Request.Content.ReadAsStreamAsync();
stream.Seek(0, System.IO.SeekOrigin.Begin);
StreamReader reader = new StreamReader(stream);
string text = reader.ReadToEnd();
Console.Write(text);
}
The problem then is that your application is using GetBufferlessInputStream to read uploads without buffering them. While that is good for memory usage on the server, it means that after you've read the stream once it will no longer be available.
Your stream is being read like this when populating your model. By default GetBufferedInputStream is used which is why it works for me.
I suggest that you take the raw XML as input into the action and then manually deserialize into the model. Alternatively you can switch back to accepting posted data into a buffer.
You probably followed something like this to turn it on : https://blogs.msdn.microsoft.com/kiranchalla/2012/09/04/receiving-request-file-or-data-in-streamed-mode-at-a-web-api-service/ and should undo that to turn it off.

How to return json result with unicode characters escaped as \u1234

I'm implementing a method that is returning a json result like:
public JsonResult MethodName(Guid key){
var result = ApiHelper.GetData(key); //Data is stored in db as varchar with åäö
return Json(new { success = true, data = result },"application/json", Encoding.Unicode, JsonRequestBehavior.AllowGet );
}
The displayed result:
{"success":true,"data":[{"Title":"Here could be characters like åäö","Link":"http://www.url.com/test",...},
But I would like to display it like:
{"success":true,"data":[{"Title":"Here could be characters like \u00e5\u00e4\u00f6","Link":"http:\/\/www.url.com\/test",...},
How can I accomplish this?
Can I convert it, parse it or change the responseEncoding in web.config to get it to display unicode characters?
There is no need for escaping if the client is a web browser, or any other client that handles http correctly, as long as your server correctly tells the client about content type and content encoding, and the encoding you select supports the codepoints in the outgoing data.
If the client does not behave correctly, and it really needs the strings to be escaped like that, you will have to write your own ActionResult class and do the escaping yourself. Inherit from JsonResult to start with, and use reflection to create the JSON document as you like it.
It's a chore!
EDIT: This will get you started
public class MyController : Controller {
public JsonResult MethodName(Guid key){
var result = ApiHelper.GetData(key);
return new EscapedJsonResult(result);
}
}
public class EscapedJsonResult<T> : JsonResult {
public EscapedJsonResult(T data) {
this.Data = data;
this.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
}
public override ExecuteResult(ControllerContext context) {
var response = context.HttpContext.Response;
response.ContentType = "application/json";
response.ContentEncoding = Encoding.UTF8;
var output = new StreamWriter(response.OutputStream);
// TODO: Do some reflection magic to go through all properties
// of the this.Data property and write JSON to the output stream
// using the StreamWriter
// You need to handle arrays, objects, possibly dictionaries, numbers,
// booleans, dates and strings, and possibly some other stuff... All
// by your self!
}
// Finds non-ascii and double quotes
private static Regex nonAsciiCodepoints =
new Regex(#"[""]|[^\x20-\x7f]");
// Call this for encoding string values
private static string encodeStringValue(string value) {
return nonAsciiCodepoints.Replace(value, encodeSingleChar);
}
// Encodes a single character - gets called by Regex.Replace
private static string encodeSingleChar(Match match) {
return "\\u" + char.ConvertToUtf32(match.Value, 0).ToString("x4");
}
}
There are some ways of escaping, but none of them do exactly what you want (HtmlEncode and UrlEncode) You'll need a user-defined function to do such escape.
Not sure if this helps but I was getting unicode characters by using below lines:
System.Web.Script.Serialization.JavaScriptSerializer jsonSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
return Json(jsonSerializer.Serialize(validationResult));

How to render an ASP.NET MVC View in PDF format

I'm working with ExpertPDF's Html-to-PDF conversion utility for this question (although I'm open to other libraries if there's sufficient documentation).
In short, I have a view that is formatted a specific way and I would like to render it as a PDF document the user can save to disk.
What I have so far is a PrintService (which implements an IPrintService interface) and this implementation has two overloads for PrintToPDF(), one that takes just a URL and another that takes an HTML string, and both of which return a byte[]. I've only worked out the details of the second overload which requires the HTML string.
What I would like to do from my controller is something like:
public FileStreamResult Print(int id)
{
var model = _CustomRepository.Get(id);
string renderedView = SomethingThatRendersMyViewAsAString(model);
Stream byteStream = _PrintService.PrintToPdf(renderedView);
HttpContext.Response.AddHeader("content-disposition",
"attachment; filename=report.pdf");
return new FileStreamResult(byteStream, "application/pdf");
}
which in theory would render a PDF to the page. It's the "SomethingThatRendersMyViewAsAString" that I'm looking for help with. Is there a quick way to get the string representation of a View? Or perhaps I should just stick with the URL overload and pass in a URL to the view... Any other thoughts?
Thanks!
I packaged my solution in a Nuget package: Rotativa http://nuget.org/packages/Rotativa. It's based on wkhtmltopdf.
Usage is really simple.
Having an action you would like to serve as Pdf, instead of Html page. You can define an action that returns an ActionResult of the type ActionAsPdf (RouteAsPdf is also available).
So the code is just:
public ActionResult PrintIndex()
{
return new ActionAsPdf("Index", new { name = "Giorgio" }) { FileName = "Test.pdf" };
}
With name = "Giorgio" being a route parameter.
It works even if the action to print is protected by web forms authentication ([Authorize] attribute)
You might be able to tap into the Response during OnResultExecuting and replace the Filter property with something that stores the resultant HTML in a MemoryStream. Then you could clear the Response during OnResultExecuted and replace it with the results of your PDF conversion. I'm not sure that this would be better than just getting the HTML from the URL, though.
public FileStreamResult Print(int id)
{
var model = _CustomRepository.Get(id);
this.ConvertToPDF = true;
return View( "HtmlView" );
}
public override OnResultExecuting( ResultExecutingContext context )
{
if (this.ConvertToPDF)
{
this.PDFStream = new MemoryStream();
context.HttpContext.Response.Filter = new PDFStreamFilter( this.PDFStream );
}
}
public override OnResultExecuted( ResultExecutedContext context )
{
if (this.ConvertToPDF)
{
context.HttpContext.Response.Clear();
this.PDFStream.Seek( 0, SeekOrigin.Begin );
Stream byteStream = _PrintService.PrintToPDF( this.PDFStream );
StreamReader reader = new StreamReader( byteStream );
context.HttpContext.Response.AddHeader( "content-disposition",
"attachment; filename=report.pdf" );
context.HttpContext.Response.AddHeader( "content-type",
"application/pdf" );
context.HttpContext.Response.Write( reader.ReadToEnd() );
}
}
The PDFStreamFilter would need to override the "Write" method(s) and send the data to the memory stream instead.
This sounds like a similar problem I had where I wanted to use Views as email templates. The best answer I found for getting the string representation of a View was here: Render a view as a string
The best package I've found is the RazorPDF, available as a package at NuGet.org, based on iTextSharp. Works on Azure Web Sites:
https://nuget.org/packages/RazorPDF

Categories