ASP.NET Return image from .aspx link - asp.net

Is it possible to output an image (or any file type) to a download link when a user clicks on a link from another ASP.NET page?
I have the file name and byte[].
Get File
...where getfile returns the file instead of going to the getfile.aspx page.

You would want .ashx for that really ;)
public class ImageHandler : IHttpHandler
{
public bool IsReusable { get { return true; } }
public void ProcessRequest(HttpContext ctx)
{
var myImage = GetImageSomeHow();
ctx.Response.ContentType = "image/png";
ctx.Response.OutputStream.Write(myImage);
}
}

How to Create Text Image on the fly with ASP.NET
Something like this:
string Path = Server.MapPath(Request.ApplicationPath + "\image.jpg");
Bitmap bmp = CreateThumbnail(Path,Size,Size);
Response.ContentType = "image/jpeg";
bmp.Save(Response.OutputStream,System.Drawing.Imaging.ImageFormat.Jpeg);
bmp.Dispose();

Here is how I have done this in the past:
Response.Clear();
Response.Buffer = true;
Response.AddHeader("Content-Disposition", string.Format("inline;filename=\"{0}.pdf\"",Guid.NewGuid()));
Response.ContentType = #"application/pdf";
Response.WriteFile(path);

Yeah, you have to clear the response completely and replace it with the image byte data as a string, and you need to make sure to set the response header for content-type according to the type of image

Yes, this is possible. There are two parts of the Response object you need to set: the Content-Type and the HTTP Header. The MSDN documentation has the details on the response object but the main concept is pretty simple. Just set the code to something like this (for a Word doc).
Response.ContentType="application/ms-word";
Response.AddHeader("content-disposition", "attachment; filename=download.doc");
There is a more complete example here

the codebehind code for getfile.aspx has to have a content-type and the browser will know that it is an image or a unknown file and will let you save it.
In asp.net you can set the ContentType by using the Response object, i.e.
Response.ContentType = "image/GIF"
Here you have a tutorial for dynamically generated image

ashx...
public class ImageHandler : IHttpHandler
{
public void ProcessRequest(HttpContext ctx)
{
string path = ".....jpg";
byte[] imgBytes = File.ReadAllBytes(path);
if (imgBytes.Length > 0)
{
ctx.Response.ContentType = "image/jpeg";
ctx.Response.BinaryWrite(imgBytes);
}
}
public bool IsReusable
{
get {return false;}
}
}

Related

How do I write an image's MemoryStream to the page body?

I'm converting ASP.NET WebForms code to ASP.NET Core Razor pages which is new to me. I'm trying to retrieve an image MemoryStream from a business class (based on SixLabors awesome ImageSharp) and have the page render the JPEG -- no HTML, just the image. I intend to use this page elsewhere as an <img> src, like <img src="Render?imageID=42&mode=invert" />
In Render.cshtml.cs:
public class RenderModel : PageModel
{
public void OnGet()
{
//snip
Stream stream = new MemoryStream();
using (Image image1 = Image.Load(imagePath))
{
SixLabors.ImageSharp.Formats.Jpeg.JpegEncoder encoder = new SixLabors.ImageSharp.Formats.Jpeg.JpegEncoder();
encoder.Quality = 75;
image1.Save(stream, encoder);
//image.Save("/temp/xxx.jpg", encoder); //test to see image. it works
}
Response.Clear();
//Response.Headers.ContentLength = stream.Length;
Response.ContentType = "image/jpeg";
Response.Body = stream;
}
}
...but this is not working, I get:
System.InvalidOperationException: Response Content-Length mismatch: too few bytes written (0 of 135408).
135408 is the stream.Length.
I'm probably not doing this correctly in the ASP.NET Core/Razor way. Can anyone set me straight as to how to do this? Thanks!
EDIT: commenting out the Headers.ContentLength fixes the error. But now I get a broken-image icon in the browser. Closer...
You need to write to the Response.Body isntead of replacing it.
stream.Seek(0, SeekOrigin.Begin);
await stream.CopyToAsync(Response.Body);
await Response.Body.FlushAsync();
I think Razor pages are intented to return html content.
However it seems to be possible to return different types of result in OnGet e.g. you could return a new FileContentReset (FileStreamResult seems to have issues with contentlength)
// read as bytes
public FileContentResult OnGet()
{
var image = System.IO.File.ReadAllBytes(#"c:\temp\myimage.jpeg");
return new FileContentResult(image.ToArray(), "image/jpeg");
}
// example if image comes from stream
public FileContentResult OnGet()
{
using var imageStr = System.IO.File.Open(#"c:\temp\myimage.jpeg", FileMode.Open);
using var memStr = new MemoryStream();
imageStr.CopyTo(memStr);
return new FileContentResult(memStr.ToArray(), "image/jpeg");
}
Even better maybe it to not use a Razor page and to add a MVC controller to return the result.

How do I provide security in the aspx file downloads?

I have web form project, example my link is www.qweqwe.com/setup.exe
if i can write url "www.qweqwe.com/setup.exe" file is downloading automatically.
I don't want this, I need session for download this. I musn't change file name, when session["scr"] is "Ok" than i must let.
(I will use ClickOnce but i don't want can be download from everypeople)
Thanks in advance to everyone
//First check for Session Variable
if(Session["scr"]!=null)
{
//Now if you want file only downloaded by only some specific person then
if(Session["scr"].ToString()=="You Specific Session value")
{
//Here Your File download code
}
}
Build a Generic Handler for downloading files (ASHX).
In the ProcessRequest method you can check the session and serve the file. Here is an example:
public class Download : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string file = "setup.exe";
if (File.Exists(context.Server.MapPath(file)) && Session["scr"].ToString() == "Ok")
{
context.Response.Clear();
context.Response.ContentType = "application/octet-stream";
context.Response.AddHeader("content-disposition", "attachment;filename=" + Path.GetFileName(file));
context.Response.WriteFile(context.Server.MapPath(file));
context.Response.End();
}
else
{
context.Response.ContentType = "text/plain";
context.Response.Write("File cannot be found!");
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
Don't forget to remove the MIME for the exe file in your IIS so nobody can download it, directly.

MVC 4 Export To CSV - Save As dialogue box not working in Chrome and Firefox

I am trying Export csv file to the User with Open/Save option.
My issue is some what similar to how-to-force-chrome-to-open-an-open-file-dialog-when-downloading-a-file-via-as(It is downloading the file in Chrome and Firefox), I have tried with the solution suggested by #Dev but it is not working.
I wrote my code as below:-
return File(new System.Text.UTF8Encoding().GetBytes(csvData),
"text/csv", filename);
But, it was not working in Chrome. The file is getting downloaded by default.
Then after googling , I found returning-a-file-to-view-download-in-mvc, from which I was trying to do something like below:-
var csvData = "hello";// I am filling this variable with ,y values from DB!
var cd = new System.Net.Mime.ContentDisposition
{
// for example foo.bak
FileName = "test",
Inline = false,
};
Response.AppendHeader("Content-Disposition",
cd.ToString());
return File(new System.Text.UTF8Encoding().GetBytes(csvData),
"text/csv");
but still it was downloading the file in Chrome. then I came across how-to-display-open-save-dialog-asp-net-mvc-4, where #JoãoSimões mentioned as:-
That is browser dependent. If you set to download automatically to a
given folder, the browser will download automatically. Firefox and
Chrome are some browsers with this behavior. – João Simões Jan 3 at
13:09
If the above is true, then how can I overcome my problem? How can I get the open/save dialogue ?
I am unable to Export my CSV with open/save option.
Edit 1
I was trying to do something like this (got it here):-
public class ExcelResult : ActionResult
{
public string FileName { get; set; }
public string Path { get; set; }
public string Data { get; set; }
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.Buffer = true;
context.HttpContext.Response.Clear();
context.HttpContext.Response.AddHeader("content-disposition", "attachment; filename=" + FileName);
context.HttpContext.Response.ContentType = "text/csv";
context.HttpContext.Response.Write(new System.Text.UTF8Encoding().GetBytes(Data));
}
}
and My controller code:-
return new ExcelResult
{
FileName = "sample.xls",
Path = "",
Data = csvData
};
but still, it is downloading the Excel ...
Edit 2
Tried opening the excel with HttpContext.Current.Response
/// <summary>
/// Export CSV
/// </summary>
/// <returns></returns>
public void DownloadCSV()
{
try
{
var csvData = Session["CSVData"].ToString();
byte[] getContent = new System.Text.UTF8Encoding().GetBytes(csvData);
System.Web.HttpContext.Current.Response.ClearContent();
System.Web.HttpContext.Current.Response.ClearHeaders();
System.Web.HttpContext.Current.Response.Buffer = true;
System.Web.HttpContext.Current.Response.ContentType = "application/vnd.ms-excel";
System.Web.HttpContext.Current.Response.AddHeader("Content-Length", getContent.Length.ToString());
System.Web.HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment; filename=" + "testing.csv");
System.Web.HttpContext.Current.Response.BinaryWrite(getContent);
System.Web.HttpContext.Current.Response.Flush();
}
catch (Exception ex)
{
HttpResponseMessage message = new HttpResponseMessage()
{
Content = new StringContent("Error Exporting Data")
};
throw new System.Web.Http.HttpResponseException(message);
}
}
but, still not working!!!
#shubh have you tried How to force Chrome to open an "open file dialog" when downloading a file vía ASP.NET codebehind? second solution they have put image in where they show how to open dialog box in chrome. I have chrome Version 30.0.1599.101 m in that if you go to setting in that advance setting then down you will find check box which was given in above link answer, that will solve your problem I think.
If still not working then might be problem with your browser just update it to latest version then try again.
Edit 1:
If you put your file name extension .xls then it will open in excel for csv you need to put file name as FileName = "sample.csv", then it will open in csv format.
Response.Clear();
Response.Buffer = true;
Response.AddHeader("content-disposition", "attachment;filename=GridViewtoCSVExport.csv");
Response.Charset = string.Empty;
Response.ContentType = "application/text";
for more check this http://surajdeshpande.wordpress.com/2013/09/03/export-gridview-data-to-csv-file-in-asp-net/
If the user has configured his browser to automatically download files, there's absolutely nothing you could do on the server to force this dialog to appear. I am afraid that what you are trying to achieve is impossible.
Try to supply another value for your content-disposition header:
Response.AppendHeader("Content-Disposition", "attachment");

Create Text File in Memory and return it over ajax, possible?

I can create a text file easily enough but I want to avoid having to keep the file on the server.
How can I create a text file in memory and return it over ajax so the file itself is returned and no file is kept on server? It doesn't need to be ajax but I want to avoid a postback if at all possible.
You can use below code to download text file in webforms
MemoryStream ms = new MemoryStream();
TextWriter tw = new StreamWriter(ms);
tw.WriteLine("HELLO WORLD!");
tw.WriteLine("I WANT TO SAVE THIS FILE AS A .TXT FILE!");
tw.Flush();
var bytes = ms.GetBuffer();
Response.ClearContent();
Response.AddHeader("Content-Disposition", "attachment; filename=test.txt");
Response.AddHeader("Content-Length", bytes.Length.ToString());
Response.ContentType = "text/plain";
Response.BinaryWrite(bytes);
Vijay's answer is correct if you're using either MVC or WebForms.
About preventing postback, You don't have to use ajax to prevent postback.
If you're using MVC, You just have to use window.location and point it to your action method in js. Something like:
// In your controller:
public class HomeController : Controller
{
public FileResult GetFile2(int id)
{
if (SomeCondition)
{
return null;
}
var fileName = "MyResult.txt";
var content = "Here's the result";
var contentType = "text/plain";
return File(Encoding.ASCII.GetBytes(content), contentType, fileName);
}
// And in your view/js file:
window.location.href = ('/Home/GetFile?id=1');
And if you're using webforms, I think best way is to create a HttpHandler to handle download links. A good tutorial can be found here.

Return XML from a controller's action in as an ActionResult?

What is the best way to return XML from a controller's action in ASP.NET MVC? There is a nice way to return JSON, but not for XML. Do I really need to route the XML through a View, or should I do the not-best-practice way of Response.Write-ing it?
return this.Content(xmlString, "text/xml");
Use MVCContrib's XmlResult Action.
For reference here is their code:
public class XmlResult : ActionResult
{
private object objectToSerialize;
/// <summary>
/// Initializes a new instance of the <see cref="XmlResult"/> class.
/// </summary>
/// <param name="objectToSerialize">The object to serialize to XML.</param>
public XmlResult(object objectToSerialize)
{
this.objectToSerialize = objectToSerialize;
}
/// <summary>
/// Gets the object to be serialized to XML.
/// </summary>
public object ObjectToSerialize
{
get { return this.objectToSerialize; }
}
/// <summary>
/// Serialises the object that was passed into the constructor to XML and writes the corresponding XML to the result stream.
/// </summary>
/// <param name="context">The controller context for the current request.</param>
public override void ExecuteResult(ControllerContext context)
{
if (this.objectToSerialize != null)
{
context.HttpContext.Response.Clear();
var xs = new System.Xml.Serialization.XmlSerializer(this.objectToSerialize.GetType());
context.HttpContext.Response.ContentType = "text/xml";
xs.Serialize(context.HttpContext.Response.Output, this.objectToSerialize);
}
}
}
If you're building the XML using the excellent Linq-to-XML framework, then this approach will be helpful.
I create an XDocument in the action method.
public ActionResult MyXmlAction()
{
// Create your own XDocument according to your requirements
var xml = new XDocument(
new XElement("root",
new XAttribute("version", "2.0"),
new XElement("child", "Hello World!")));
return new XmlActionResult(xml);
}
This reusable, custom ActionResult serialises the XML for you.
public sealed class XmlActionResult : ActionResult
{
private readonly XDocument _document;
public Formatting Formatting { get; set; }
public string MimeType { get; set; }
public XmlActionResult(XDocument document)
{
if (document == null)
throw new ArgumentNullException("document");
_document = document;
// Default values
MimeType = "text/xml";
Formatting = Formatting.None;
}
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.Clear();
context.HttpContext.Response.ContentType = MimeType;
using (var writer = new XmlTextWriter(context.HttpContext.Response.OutputStream, Encoding.UTF8) { Formatting = Formatting })
_document.WriteTo(writer);
}
}
You can specify a MIME type (such as application/rss+xml) and whether the output should be indented if you need to. Both properties have sensible defaults.
If you need an encoding other than UTF8, then it's simple to add a property for that too.
If you are only interested to return xml through a request, and you have your xml "chunk", you can just do (as an action in your controller):
public string Xml()
{
Response.ContentType = "text/xml";
return yourXmlChunk;
}
There is a XmlResult (and much more) in MVC Contrib. Take a look at http://www.codeplex.com/MVCContrib
I've had to do this recently for a Sitecore project which uses a method to create an XmlDocument from a Sitecore Item and its children and returns it from the controller ActionResult as a File. My solution:
public virtual ActionResult ReturnXml()
{
return File(Encoding.UTF8.GetBytes(GenerateXmlFeed().OuterXml), "text/xml");
}
use one of these methods
public ContentResult GetXml()
{
string xmlString = "your xml data";
return Content(xmlString, "text/xml");
}
or
public string GetXml()
{
string xmlString = "your xml data";
Response.ContentType = "text/xml";
return xmlString;
}
Finally manage to get this work and thought I would document how here in the hopes of saving others the pain.
Environment
VS2012
SQL Server 2008R2
.NET 4.5
ASP.NET MVC4 (Razor)
Windows 7
Supported Web Browsers
FireFox 23
IE 10
Chrome 29
Opera 16
Safari 5.1.7 (last one for Windows?)
My task was on a ui button click, call a method on my Controller (with some params) and then have it return an MS-Excel XML via an xslt transform. The returned MS-Excel XML would then cause the browser to popup the Open/Save dialog. This had to work in all the browsers (listed above).
At first I tried with Ajax and to create a dynamic Anchor with the "download" attribute for the filename,
but that only worked for about 3 of the 5 browsers(FF, Chrome, Opera) and not for IE or Safari.
And there were issues with trying to programmatically fire the Click event of the anchor to cause the actual "download".
What I ended up doing was using an "invisible" IFRAME and it worked for all 5 browsers!
So here is what I came up with:
[please note that I am by no means an html/javascript guru and have only included the relevant code]
HTML (snippet of relevant bits)
<div id="docxOutput">
<iframe id="ifOffice" name="ifOffice" width="0" height="0"
hidden="hidden" seamless='seamless' frameBorder="0" scrolling="no"></iframe></div>
JAVASCRIPT
//url to call in the controller to get MS-Excel xml
var _lnkToControllerExcel = '#Url.Action("ExportToExcel", "Home")';
$("#btExportToExcel").on("click", function (event) {
event.preventDefault();
$("#ProgressDialog").show();//like an ajax loader gif
//grab the basket as xml
var keys = GetMyKeys();//returns delimited list of keys (for selected items from UI)
//potential problem - the querystring might be too long??
//2K in IE8
//4096 characters in ASP.Net
//parameter key names must match signature of Controller method
var qsParams = [
'keys=' + keys,
'locale=' + '#locale'
].join('&');
//The element with id="ifOffice"
var officeFrame = $("#ifOffice")[0];
//construct the url for the iframe
var srcUrl = _lnkToControllerExcel + '?' + qsParams;
try {
if (officeFrame != null) {
//Controller method can take up to 4 seconds to return
officeFrame.setAttribute("src", srcUrl);
}
else {
alert('ExportToExcel - failed to get reference to the office iframe!');
}
} catch (ex) {
var errMsg = "ExportToExcel Button Click Handler Error: ";
HandleException(ex, errMsg);
}
finally {
//Need a small 3 second ( delay for the generated MS-Excel XML to come down from server)
setTimeout(function () {
//after the timeout then hide the loader graphic
$("#ProgressDialog").hide();
}, 3000);
//clean up
officeFrame = null;
srcUrl = null;
qsParams = null;
keys = null;
}
});
C# SERVER-SIDE (code snippet)
#Drew created a custom ActionResult called XmlActionResult which I modified for my purpose.
Return XML from a controller's action in as an ActionResult?
My Controller method (returns ActionResult)
passes the keys parameter to a SQL Server stored proc that generates an XML
that XML is then transformed via xslt into an MS-Excel xml (XmlDocument)
creates instance of the modified XmlActionResult and returns it
XmlActionResult result = new XmlActionResult(excelXML, "application/vnd.ms-excel");
string version = DateTime.Now.ToString("dd_MMM_yyyy_hhmmsstt");
string fileMask = "LabelExport_{0}.xml";
result.DownloadFilename = string.Format(fileMask, version);
return result;
The main modification to the XmlActionResult class that #Drew created.
public override void ExecuteResult(ControllerContext context)
{
string lastModDate = DateTime.Now.ToString("R");
//Content-Disposition: attachment; filename="<file name.xml>"
// must set the Content-Disposition so that the web browser will pop the open/save dialog
string disposition = "attachment; " +
"filename=\"" + this.DownloadFilename + "\"; ";
context.HttpContext.Response.Clear();
context.HttpContext.Response.ClearContent();
context.HttpContext.Response.ClearHeaders();
context.HttpContext.Response.Cookies.Clear();
context.HttpContext.Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);// Stop Caching in IE
context.HttpContext.Response.Cache.SetNoStore();// Stop Caching in Firefox
context.HttpContext.Response.Cache.SetMaxAge(TimeSpan.Zero);
context.HttpContext.Response.CacheControl = "private";
context.HttpContext.Response.Cache.SetLastModified(DateTime.Now.ToUniversalTime());
context.HttpContext.Response.ContentType = this.MimeType;
context.HttpContext.Response.Charset = System.Text.UTF8Encoding.UTF8.WebName;
//context.HttpContext.Response.Headers.Add("name", "value");
context.HttpContext.Response.Headers.Add("Last-Modified", lastModDate);
context.HttpContext.Response.Headers.Add("Pragma", "no-cache"); // HTTP 1.0.
context.HttpContext.Response.Headers.Add("Expires", "0"); // Proxies.
context.HttpContext.Response.AppendHeader("Content-Disposition", disposition);
using (var writer = new XmlTextWriter(context.HttpContext.Response.OutputStream, this.Encoding)
{ Formatting = this.Formatting })
this.Document.WriteTo(writer);
}
That was basically it.
Hope it helps others.
A simple option that will let you use streams and all that is return File(stream, "text/xml");.
Here is a simple way of doing it:
var xml = new XDocument(
new XElement("root",
new XAttribute("version", "2.0"),
new XElement("child", "Hello World!")));
MemoryStream ms = new MemoryStream();
xml.Save(ms);
return File(new MemoryStream(ms.ToArray()), "text/xml", "HelloWorld.xml");
A small variation of the answer from Drew Noakes that use the method Save() of XDocument.
public sealed class XmlActionResult : ActionResult
{
private readonly XDocument _document;
public string MimeType { get; set; }
public XmlActionResult(XDocument document)
{
if (document == null)
throw new ArgumentNullException("document");
_document = document;
// Default values
MimeType = "text/xml";
}
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.Clear();
context.HttpContext.Response.ContentType = MimeType;
_document.Save(context.HttpContext.Response.OutputStream)
}
}

Resources