ASP.NET show PDF file to user instead of "save as" dialog - asp.net

My ASP.NET application return PDF file to user using code below
Context.Response.Clear();
Context.Response.ContentType = "application/pdf";
Context.Response.TransmitFile(optionEntityCmd.PathToSave);
Context.Response.End();
This code show Save As browser dialog, is it possible instead of Save As dialog load PDF file directly in browser?

You could append the Content-Disposition header:
Context.Response.AppendHeader(
"Content-Disposition",
"inline; filename=foo.pdf"
);

Is it a dynamically created file? If not, you can just hyperlink or Response.Redirect to it I believe.

I do not know for sure for classic asp.net but using mvc, streaming it to the user does what you want:
MemoryStream stream = PDF.GeneratePDFByStream();
stream.Flush(); //Always catches me out
stream.Position = 0; //Not sure if this is required
return stream;
with
public static MemoryStream GeneratePDFByStream() {
var doc1 = new Document();
//use a variable to let my code fit across the page...
string path = AppDomain.CurrentDomain.BaseDirectory + "PDFs";
MemoryStream stream = new MemoryStream();
PdfWriter writer = PdfWriter.GetInstance(doc1, stream);
writer.CloseStream = false;
// Actual Writing
doc1.Open();
// writing comes here, you will probably just load the PDF in a stream?
doc1.Close();
return stream;
}
And your MVC controller returns something like
return File(GetPDFStream(id), "application/pdf");
So, I know this is not the exact answer you are looking for, but maybe you should try to stream your PDF to the user as it will open it in a new tab as far as I ever tested it.
From the top of my head, you should get something like:
Response.Clear();
Response.ContentType = "application/pdf";
Response.OutputStream.Write( objMemoryStream.ToArray(), 0,
Convert.ToInt32(objMemoryStream.Length));
Response.Flush();
try { Response.End(); } catch{}

Related

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.

Any info on the chinese '360 secure browser'? Having issues with it downloading files I am streaming

There is a desktop browser called '360 secure browser'. They have a fairly large share of the market in China, and we are required to support them.
It says the layout engine is Trident (IE), which is what I expected, but I can't verify that right now (on a mac!).
The reason for this is that I have some forms that kick off a download, streaming bytes to the client, and they work in the other major browsers. The code that causes the issue is below, or similar. Is this doing something wrong that I don't notice? The byte streams are usually on the order of 50-100KB, and we haven't had issues with it yet.
This code is called in response to a PostBack event (eg, button click in a grid, etc)
This function is called with bytestreams from files, generated in memory, or read from db.
The function:
public static bool DownloadStream(byte[] packageStream, string fileName) {
var response = HttpContext.Current.Response;
response.Clear();
response.AddHeader("Accept-Ranges", "bytes");
response.AddHeader("Content-Disposition", "inline; filename=" + HttpUtility.UrlEncode(fileName, Encoding.UTF8));
response.AddHeader("Content-Length", packageStream.Length.ToString());
response.ContentType = "application/xlsx";
response.BinaryWrite(packageStream);
response.Flush();
HttpContext.Current.ApplicationInstance.CompleteRequest();
return true;
}
Does anyone have any experience supporting this browser? I can't find any information on it when searching in english on google. No specs, no docs, nothing. I have to go to Baidu to find info, and I can't read that level of chinese!
EDIT:
The issue is with the downloader that 360 uses, apparently. I would like to know if there is something that should be changed in the streaming code, though. A header that I am missing, or something else.
This is only happening for small files. Same page, bigger download = no issues.
Changing to the built-in IE downloader causes the issue to go away.
Hi i tried your code on 360 secure browser. it work for me. and i edit a little bit below is my code.
Note: As i know, 360 secure browser is using IE Core.
protected void Page_Load(object sender, EventArgs e)
{
DownloadStream(StreamFile(#"C:\Users\My\Desktop\test2.xlsx"), "test.xlsx");
}
private byte[] StreamFile(string filename)
{
FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
// Create a byte array of file stream length
byte[] Data = new byte[fs.Length];
//Read block of bytes from stream into the byte array
fs.Read(Data, 0, System.Convert.ToInt32(fs.Length));
//Close the File Stream
fs.Close();
return Data; //return the byte data
}
public static bool DownloadStream(byte[] packageStream, string fileName)
{
var response = HttpContext.Current.Response;
response.ClearContent();
response.ClearHeaders();
response.AppendHeader("Accept-Ranges", "bytes");
response.AppendHeader("Content-Disposition", "inline; filename=" + HttpUtility.UrlEncode(fileName, Encoding.UTF8));
response.AppendHeader("Content-Length", packageStream.Length.ToString());
response.ContentType = "application/xlsx";
response.BinaryWrite(packageStream);
response.Flush();
response.End();
return true;
}

How to get raw request body in ASP.NET?

In the HttpApplication.BeginRequest event, how can I read the entire raw request body? When I try to read it the InputStream is of 0 length, leading me to believe it was probably already read by ASP.NET.
I've tried to read the InputStream like this:
using (StreamReader reader = new StreamReader(context.Request.InputStream))
{
string text = reader.ReadToEnd();
}
But all I get is an empty string. I've reset the position back to 0, but of course once the stream is read it's gone for good, so that didn't work. And finally, checking the length of the stream returns 0.
Edit: This is for POST requests.
The request object is not populated in the BeginRequest event. You need to access this object later in the event life cycle, for example Init, Load, or PreRender. Also, you might want to copy the input stream to a memory stream, so you can use seek:
protected void Page_Load(object sender, EventArgs e)
{
MemoryStream memstream = new MemoryStream();
Request.InputStream.CopyTo(memstream);
memstream.Position = 0;
using (StreamReader reader = new StreamReader(memstream))
{
string text = reader.ReadToEnd();
}
}
Pål's answer is correct, but it can be done much shorter as well:
string req_txt;
using (StreamReader reader = new StreamReader(HttpContext.Current.Request.InputStream))
{
req_txt = reader.ReadToEnd();
}
This is with .NET 4.6.
In ASP.NET Core 2:
using (var reader = new StreamReader(HttpContext.Request.Body)) {
var body = reader.ReadToEnd();
}
I had a similar requirement to get the raw content and struck the same issue. I found that calling Seek(0, SeekOrigin.Begin) solved the problem.
This is not a particularly good approach as it makes assumptions about how the underlying infrastructure operates, but it seems to work.
It is important to reset position of InputStream.
var memstream = new MemoryStream();
Request.InputStream.CopyTo(memstream);
Request.InputStream.Position = 0;
using (StreamReader reader = new StreamReader(memstream)) {
var text = reader.ReadToEnd();
Debug.WriteLine(text);
}
Here's what I ended up doing:
//Save the request content. (Unfortunately it can't be written to a stream directly.)
context.Request.SaveAs(filePath, false);

How to handle errors when using ASP.NET to create a zipfile for download?

I'm working on a functionality in my asp.net web site that enables the user to download some files as a zip file. I'm using the DotNetZip library to generate the zip file.
My code looks like this:
protected void OkbtnZipExport_OnClickEvent(object sender, EventArgs e)
{
var selectedDocumentIds = GetSelectedDocIds();
string archiveName = String.Format("archive-{0}.zip", DateTime.Now.ToString("yyyy-MMM-dd-HHmmss"));
AddResponseDataForZipFile(Response, archiveName);
try
{
string errorMessage = Utils.ExportToZip(selectedDocumentIds, arkivdelSearchControl.GetbraArkivConnection(), Response.OutputStream);
if (!string.IsNullOrEmpty(errorMessage))
{
LiteralExportStatus.Text = errorMessage;
}
else
LiteralExportStatus.Text = "Success";
}
catch (Exception ex)
{
LiteralExportStatus.Text = "Failure " + ex.Message;
}
Response.Flush();
Response.Close();
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
private void AddResponseDataForZipFile(HttpResponse response, string zipName)
{
Response.Clear();
Response.BufferOutput = false;
Response.ContentType = "application/x-zip-compressed";
Response.AddHeader("content-disposition", "attachment; filename=" + zipName);
Response.AddHeader("Expires", "0");
Response.AddHeader("Content-Description", "Zip Arcive");
}
Now, if anything goes wrong, say the Utils.ExportToZip method fails, I want to present an error message to the user and not the download dialog. Do I have to remove some data from the Response object in order to cancel the download operation?
Best regards
OKB
first, Don't call HttpContext.Current.ApplicationInstance.CompleteRequest();
Reference.
At one point, there was some example code that showed CompleteRequest(), but it's wrong.
Second - to do what you describe,
you'll need to insure that the zip file can be created correctly and in its entirety, before sending anything. That means you should do the AddResponseDataForZipFile() only after the zipfile is completely created. That means you need to create an actual zip file on the server, and not simply save out to Response.OutputStream. Once the file is successfully created, then call AddResponseDataForZipFile(), stream the bytes for the temp zip file, call Response.Close(), then delete the temporary zip file.
I can't comment at the moment, so take this answer as one.
How does Utils.ExportToZip work?
If the reason it takes the Response.OutputStream for the constructor is to write the zip-file directly into it, then you need to set Buffering in order to "undo" that in your AddResponseDataForZipFile Method:
Response.BufferOutput = true;

How to Download A file stored in SQL DB in Binary Format

I am simply storing uploaded file into a binary field in SQL Server but I also need to allow users to download it with Asp.NET. How can I do that ?
Thanks in advance.
Here's a Microsoft Knowledge Base article on this.
How to retrieve the file from your database depends on the data access technology you use; I will just assume that you have some Byte array data containing the file (e.g. by filling a DataSet and accessing the field) and some string filename.
Response.Clear()
Response.ContentType = "application/octet-stream"
Response.AddHeader("Content-Disposition", "attachment;filename=""" & filename & """")
Response.BinaryWrite(data)
Response.End()
Put the above code in some download.aspx and link to this file. You probably want to pass some query string information to your download.aspx, so that your code knows which file to get from the database.
Read the data into a filestream object with the appropriate extension tacked on to it, and have the user download the resulting file.
You'll want to use the System.IO BinaryWriter object on the filestream to create the file...something like this:
FileStream fs = new FileStream("thisfile.bin", FileMode.Create);
binWriter= new BinaryWriter(fs);
binWriter.Write(varHoldingSqlRetrievedBinaryData);
Add a Generic Handler (.ashx) page to your web site. The ashx code body below demonstrates how to read an arbitrary stream (in this case a PNG file from disk) and write it out in the response:
using System;
using System.Web;
using System.IO;
namespace ASHXTest
{
public class GetLetter : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
// Get letter parameter from query string.
string fileName = context.Request.MapPath(string.Format("{0}.png",
context.Request.QueryString["letter"]));
// Load file from disk/database/ether.
FileStream stream = new FileStream(fileName, FileMode.Open,
FileAccess.Read);
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
stream.Close();
// Write response headers and content.
context.Response.ContentType = "image/png";
context.Response.OutputStream.Write(buffer, 0, buffer.Length);
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
If desired, you can also set the Content-Disposition header as demonstrated in Heinzi's answer:
context.Response.AddHeader("Content-Disposition",
"attachment;filename=\"letter.png\"");

Resources