ASP.NET Downloadable Image Files? - asp.net

I don't have apache support on this website, but I need to be able to allow images to be downloadable in a certain directory only. How can I do this? This site only has ASP.NET support, and it's killing me! Noticed this link: How to download files in mvc3? but not sure where to put that code at, or even if that code there would help me any.
Any help would be greatly appreciated! A Starting point or something...
Is there a way I can just do this in HTML? Like, for example, set the download link to point to an html file, where the HTML file grabs the image file and makes it downloadable, in and of itself?
So far I have the following ASP code in a file called: default.asp
Which begins the download just fine, but it downloads an empty file (download.jpg). How can I point the following code to an actual image file to download?
<%# Language=VBScript %>
<% Option Explicit
Response.ContentType = "application/octet-stream"
Response.AddHeader "Content-Disposition", "attachment; filename=" + "download.jpg"
%>
I have a file named, "download.jpg" even within the same directory, but it never downloads the actual image. It downloads a 90 byte empty image file instead.
I even tried this with no luck:
<%# Language=VBScript %>
<% Option Explicit
Response.ContentType = "application/octet-stream"
Response.AddHeader "Content-Disposition","attachment; filename=07awardee.png"
Response.TransmitFile Server.MapPath("~/images/07awardee.png")
Response.End
%>
And yes, I have the 07awardee.png file in images/07awardee.png on the server root and even in the folder root where default.asp is located. Arggg! What gives here? The file is a bit bigger now at 392 bytes, but it still isn't readable as an image file... I've been searching the internet and this is supposed to work, but doesn't! What could be the problem here?

Your .aspx page with a Request.End throws a ThreadAbortException, and that's bad for server performance (too many of these can even crash a server). So you'll want to avoid that. http://weblogs.asp.net/hajan/archive/2010/09/26/why-not-to-use-httpresponse-close-and-httpresponse-end.aspx
The way I handle this problem is with a HttpHandler (.ashx) and use that to serve downloadable image files. I used some of your code because my implementation was in C# and has much more code (includes image scaling options etc):
//<%# WebHandler Language="VB" Class="DownloadImage" %> // uncomment this line!
Public Class DownloadImage : Implements IHttpHandler
Protected EnabledTypes() As String = New String() {".jpg", ".gif", ".png"}
Public Sub ProcessRequest(ByVal context As HttpContext) _
Implements IHttpHandler.ProcessRequest
Dim request = context.Request
If Not String.IsNullOrEmpty(request.QueryString("file")) Then
Dim path As String = context.Server.MapPath(request.QueryString("file"))
Dim file As System.IO.FileInfo = New System.IO.FileInfo(path)
If file.Exists And EnabledTypes.Contains(file.Extension.ToLower()) Then
context.Response.Clear()
context.Response.AddHeader("Content-Disposition", _
"attachment; filename=" & file.Name)
context.Response.AddHeader("Content-Length", file.Length.ToString())
context.Response.ContentType = "application/octet-stream"
context.Response.WriteFile(file.FullName)
Else
context.Response.ContentType = "plain/text"
context.Response.Write("This file does not exist.")
End If
Else
context.Response.Write("Please provide a file to download.")
End If
End Sub
Public ReadOnly Property IsReusable() As Boolean _
Implements IHttpHandler.IsReusable
Get
Return True
End Get
End Property
End Class
Make sure you implement a check for image files, otherwise you have a potential security problem. (Users could download web.config where database passwords are stored)
The link would become:
Download image

You should clear the header and provide the correct header and content type
Response.Clear()
Response.AppendHeader("Content-Disposition", "attachment; filename=somefilename")
Response.ContentType = "image/jpeg"
Response.TransmitFile(Server.MapPath("/xyz.jpg"));
Response.End();

OMG, I rock. Here's the way this is done. Create a file called, download.aspx, and input the following code into it:
<%# Page language="vb" runat="server" explicit="true" strict="true" %>
<script language="vb" runat="server">
Sub Page_Load(Sender As Object, E As EventArgs)
Dim strRequest As String = Request.QueryString("file")
If strRequest <> "" AND strRequest.EndsWith(".jpg") OR strRequest.EndsWith(".jpeg") OR strRequest.EndsWith(".png") OR strRequest.EndsWith(".gif") OR strRequest.EndsWith(".pdf") OR strRequest.EndsWith(".doc") OR strRequest.EndsWith(".docx") OR strRequest.EndsWith(".bmp") Then
Dim path As String = Server.MapPath(strRequest)
Dim file As System.IO.FileInfo = New System.IO.FileInfo(path)
If file.Exists Then
Response.Clear()
Response.AddHeader("Content-Disposition", "attachment; filename=" & file.Name)
Response.AddHeader("Content-Length", file.Length.ToString())
Response.ContentType = "application/octet-stream"
Response.WriteFile(file.FullName)
Response.End
Else
Response.Write("This file does not exist.")
End If
Else
Response.Write("You do not have permission to download this file type!")
End If
End Sub
</script>
Now, when you want to get a file to download (ANY FILE), just link it like so:
Download the logo image
And that's all she wrote!

Related

How to send multiple files with Response. ASP.NET

I'm trying to call below code in a loop hundreds of times:
Sub ExportReport(ByVal en As MyReport)
Dim warnings As Warning() = Nothing
Dim streamids As String() = Nothing
Dim mimeType As String = Nothing
Dim encoding As String = Nothing
Dim extension As String = Nothing
Dim bytes As Byte()
bytes = aReport.ServerReport.Render("WORD", Nothing, mimeType, encoding, extension, streamids, warnings)
Response.Buffer = True
Response.Clear()
Response.ContentType = mimeType
Response.AddHeader("content-disposition", "attachment; filename=" & en.ToString() & "." + extension)
Response.BinaryWrite(bytes)
Response.Flush()
Response.End()
End Sub
And I'm getting this error :
Server cannot append header after HTTP headers have been sent.
How can I change the code so that I can loop this piece of code? Thanks.
EDIT :
I added this line after Response.End()
Response.Redirect(Request.Url.AbsoluteUri)
And I get this error :
Cannot redirect after HTTP headers have been sent.
WWW works on a request / response mechanism. For every request there is only 1 response. You cannot change that basic mechanism. When browser sends a request it is expecting one and only one response. So if it receives more than 1 response, it either issues a warning to the user to block this behaviour or may choose to ignore the extra responses by itself. Thus these extra responses may be lost.
Having said that you have 2 options with you:
Zip all the files that you want to download and download as a single file.
You can use Popular framework Ionic.Zip.
First, keep all your files in a local directory on the server.
Then use this library to zip the entire folder.
Pseudo code:
Imports (var zip = New Ionic.Zip.ZipFile())
{
zip.AddDirectory("DirectoryOnDisk", "rootInZipFile")
Response.Clear()
Response.AddHeader("Content-Disposition", "attachment; filename=DownloadedFile.zip")
Response.ContentType = "application/zip"
zip.Save(Response.OutputStream)
Response.End()
}
Add a mechanism to issue multiple request using Javascript to get multiple responses, so browser still treats this behaviour as normal.
A normal web page will have a load of (headers) stuff set up for you already, but you don't want any of that: you want complete control over what is sent to the browser. If you cause a redirect to something which sends the headers shown in code later here, the browser will (normally) download the data.
In the code-behind you can have something like
Protected Sub btn_click(ByVal sender As Object, ByVal e As EventArgs) Handles btn.Click
Response.Redirect("~/sendfile.ashx?ref=" & enReference, False)
Context.ApplicationInstance.CompleteRequest()
End Sub
You will also need to add a generic handler (right-click on the project in Solution Explorer, Add->New Item... -> Visual Basic--Web--General choose "Generic Handler"; give it a name like sendfile.ashx) which is somewhat like
Imports System.IO
Public Class sendfile
Implements System.Web.IHttpHandler, IReadOnlySessionState
Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
Dim enReference = context.Request.QueryString("ref")
' do whatever is needed to get the report from enReference '
Dim bytes As Byte() = aReport.ServerReport.Render("WORD", Nothing, mimeType, encoding, extension, streamids, warnings)
Dim downloadName = yourfilename & "." & yourextension
context.Response.ContentType = "application/octet-stream"
context.Response.AddHeader("content-disposition", "attachment; filename=""" & downloadName & """" )
context.Response.BinaryWrite(bytes)
End Sub
ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
and you will need to work out the code to create the data to be sent.
If you don't need to use session state then you can remove the , IReadOnlySessionState part on the Implements line.
You might need to add context.Response.Flush(). If you find that the response does not have a Content-Length header, then you ought to add one so that the browser can show a meaningful download progress.

Download PDF using Response on ASPX Page only working in Page_Load

I've seen several questions relating to downloading a PDF from a Web browser using Response, but none seem to fit the mysterious issue I'm having.
I am working on a project that requires the user to be able to click a button (btnPDF) to instantly download a PDF of a Telerik report with a specific "ID" string to the Downloads folder. This process was originally located in an ASPX Page on an IIS separate from where the button is located. When btnPDF was clicked, I used Response.Redirect to download the PDF through that page. The code to download the PDF looked like this:
Response.Clear()
Response.ContentType = result.MimeType 'this is always "application/pdf"
Response.Cache.SetCacheability(HttpCacheability.Private)
Response.Expires = -1
Response.Buffer = True
Response.AddHeader("Content-Disposition", String.Format("{0};FileName={1}", "attachment", fileName))
Response.BinaryWrite(result.DocumentBytes)
Response.End()
Note that result.DocumentBytes is a byte array containing correct bytes for the PDF.
This code worked fine. Now, instead of having the process on a separate Page in a separate project, I need to merge the process onto the same page where btnPDFis located, so that when you click btnPDF, a subroutine is called that performs the same task. I thought this would be very easy, pretty much a copy and paste. With the same code added in a new subroutine, this is what my click event handler "ButtonPDF_Click" now looks like:
Protected Sub ButtonPDF_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnPDF.Click
DownloadReportPDF(Me.RadGrid1.SelectedValue.ToString())
Dim strMessage As String = "alert('Printed PDF Sheet.');"
ScriptManager.RegisterStartupScript(Me, Me.GetType, "MyScript", strMessage, True)
End Sub
Protected Sub DownloadReportPDF(ByVal releaseMasterId As String)
'Service call to generate report source
Dim service As New TelerikReportLibrary.ReportServices.PPSReportService
Dim source As Telerik.Reporting.TypeReportSource = service.GetReportSource(releaseMasterId)
'Render PDF and download
Dim reportProcessor As New ReportProcessor()
Dim result As RenderingResult = reportProcessor.RenderReport("PDF", source, Nothing)
Dim fileName As String = result.DocumentName + "_" + releaseMasterId + "." + result.Extension
Response.Clear()
Response.ContentType = result.MimeType 'this is always "application/pdf"
Response.Cache.SetCacheability(HttpCacheability.Private)
Response.Expires = -1
Response.Buffer = True
Response.AddHeader("Content-Disposition", String.Format("{0};FileName={1}", "attachment", fileName))
Response.BinaryWrite(result.DocumentBytes)
Response.End()
End Sub
But the PDF no longer downloads. An accurate byte array is still created, but the Response portion does not result in the PDF being downloaded from the browser. I've found that putting a call to DownloadReportPDF in the Page_Load handler on the same Page successfully generates and downloads a PDF as it did before.
I can't see any reason why this isn't working, but I'm new to ASP, and I'm not great in VB. I've tried using Response.OutputStream, Response.WriteFile, and making use of a MemoryStream, among several other things that I've lost track of. I'm hoping there's something simple, maybe some sort of property of the Page or btnPDF I could be missing. Here is the markup for btnPDF, just in case:
<asp:linkButton ID="btnPDF" CssClass="btn btn-default" runat="server" Width="115px">
<i class="fa fa-file-text" title="Edit"></i> PDF
</asp:linkButton>
What could be causing such a problem? Where should I look at this point?
Let me know if more information is needed.
Thanks,
Shane
EDIT:
I experimented with setting a session variable on btnPDF_Click, and handling the PDF download on postback. Again, a valid byte array was generated, but the HttpResponse did not cause the PDF to download from the browser.
EDIT:
Building on the last edit, this tells me that calling DownloadReportPDF from Page_Load works only when IsPostBack is false. I just tested this thought, and it holds true. In the above code, if I check IsPostBack at the moment I'm trying to download the PDF, it is true. Investigating further.
Alright, I finally found a solution I'm satisfied with (though I still don't understand why I can't download the PDF using Response while IsPostBack is true).
Inspired by this thread, I put the previously posted code in an HttpHandler called PDFDownloadHandler, then used Response.Redirect in the btnPDF_Click event handler to utilize PDFDownloadHandler. This article helped me a lot on that process, as it is something I have not done before.
In case anyone else runs into this problem, here is the new PDFDownloadHandler:
Imports Microsoft.VisualBasic
Imports System.Web
Imports Telerik.Reporting
Imports Telerik.Reporting.Processing
Public Class PDFDownloadHandler
Implements IHttpHandler
Public Sub ProcessRequest(ByVal context As _
System.Web.HttpContext) Implements _
System.Web.IHttpHandler.ProcessRequest
Dim request As HttpRequest = context.Request
Dim response As HttpResponse = context.Response
Dim path As String = request.Path
If path.Contains("pps.pdfdownload") Then
Dim releaseMasterId As String = request.QueryString("ID")
If releaseMasterId IsNot Nothing Then
'Service call to generate report source
Dim service As New TelerikReportLibrary.ReportServices.PPSReportService
Dim source As Telerik.Reporting.TypeReportSource = service.GetReportSource(releaseMasterId)
'Render PDF and save
Dim reportProcessor As New ReportProcessor()
Dim result As RenderingResult = reportProcessor.RenderReport("PDF", source, Nothing)
Dim fileName As String = result.DocumentName + "_" + releaseMasterId + "." + result.Extension
response.Clear()
response.ContentType = result.MimeType
response.Cache.SetCacheability(HttpCacheability.Private)
response.Expires = -1
response.Buffer = True
response.AddHeader("Content-Disposition", String.Format("{0};FileName={1}", "attachment", fileName))
response.BinaryWrite(result.DocumentBytes)
End If
End If
response.End()
End Sub
Public ReadOnly Property IsReusable() As Boolean _
Implements System.Web.IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
Any further insight on why the original technique did not work is greatly appreciated.

Download file works on firefox, not on other browsers

in staging my download function is working fine, when I deploy in production (nas server with IIS 7.5) the same functions works only on Firefox.
With IE, Edge and Chrome the download start but, it don't arrive to the end, and after minute, get an error.
I know that there is a lot of documentation online but I just tried a lot of solutions without result. Please help me.
#Page Language="VB" ContentType="text/html"
#Import Namespace="System.Data"
#Import Namespace="System.Data.SqlClient"
#Import Namespace="System.IO"
Private SUB SDataBind()
'Path construction
Dim VarFileName as string = "mytest.zip"
'Path construction
Dim VarPath as string = "/public/mydownload" & VarFileName
'Absolute path
Dim VarCompletPath as string = Server.MapPath(VarPath)
VarCompletPath = replace(VarCompletPath,"/","\")
'Filename construction
Dim VarLast4Char as string = right(VarFileName,4)
Dim VarNewFilename as string = "download" & VarLast4Char
'Headers cleaner
Response.Clear
Response.ClearHeaders
Response.ClearContent
'Send the file to the browser
Response.AddHeader("Content-Disposition", ("attachment;filename=" & VarNewFilename))
Response.ContentType = ReturnFiletype( right(VarFileName,4) )
Response.WriteFile( VarCompletPath )
Response.Flush()
Response.Close()
Response.End
end sub
'----------------------------
'Get content file type
Private function ReturnFiletype(fileExtension as string) as string
Select Case fileExtension
case ".zip"
return "application/zip"
case ".pdf"
return "application/pdf"
case else
return "application/octet-stream"
end select
end function
I resolved with a more classic function
'Clean the headers
Response.Clear
Response.ClearHeaders
Response.ClearContent
'Send file to browser
Response.AddHeader("Content-Disposition", ("attachment;filename=" & VarFileName))
Response.WriteFile(Server.MapPath(VarPath))
Response.End
I removed the lines
Response.Flush()
Response.Close()

Asp.Net hide url of file

I hav an Asp.NET site that displays a PDF/Word/Excel file but I would like to hide the location of the file i.e. if the user requests a file via a link instead of displaying the path just open the filename, I've seen some other posts on other sites but since they are old they do not wok.
Any help is appreciated.
Thanks
Use ASP.NET ASHX Handler
Some ASP.NET files are dynamically generated. They are generated with C# code or disk resources. These files do not require web forms. Instead, an ASHX generic handler is ideal. It can dynamically return an image from a query string, write XML, or any other data.
One way to get around this is to write a custom .ashx handler, which implements IHttpHandler.
Implement the ProcessRequest(..) method, and pipe the file out in the response (here's an example from an application I wrote a while back:
Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
Dim inline As Boolean = Boolean.Parse(context.Request.QueryString("Inline"))
Dim fileName As String = context.Request.QueryString("fileName")
If (fileName.Contains("\")) Then Throw New Exception(String.Format("Invalid filename {0}. Looks like a path was attempted", fileName))
Dim filePath = ConfigurationManager.AppSettings("FileDirectory") + "\" + fileName
With context.Response
.Buffer = True
.Clear()
If inline Then
.AddHeader("content-disposition", "inline; ; filename=" & IO.Path.GetFileName(filePath))
Else
.AddHeader("content-disposition", "attachment; ; filename=" & IO.Path.GetFileName(filePath))
End If
.WriteFile(filePath)
If fileName.ToUpper.EndsWith(".PDF") Then
.ContentType = "application/pdf"
ElseIf fileName.EndsWith(".htm") Or fileName.EndsWith(".html") Then
.ContentType = "text/html"
ElseIf fileName.EndsWith(".tif") Then
.ContentType = "image/tiff"
ElseIf fileName.EndsWith(".jpeg") Or fileName.EndsWith(".jpg") Then
.ContentType = "image/jpeg"
End If
.End()
End With

When downloading a file from ASP .Net, the text file gets appended with HTML content

I have made a page that allows users to upload files to the server using a FileUpload Control and handling its event with this code
Sub SaveAttachment()
Dim root As String = "C:\temp\"
Dim filename As String = FileUpload1.FileName
Dim SaveName As String = root & filename
FileUpload1.SaveAs(SaveName)
End Sub
That worked fine, I was able to see files getting uploaded, and the content of the files is intact (exactly a duplicate copy of the file that the user's upload).
Now for downloading the files back to the user (at a later time) I have written another page that reads the file name from a Request.Parameter ("file") and fetches that file to be downloaded to the user. I have written the Download.aspx page to handle downloading in the ASP part (no code behind was used):
<%# Import Namespace="System.IO"%>
<script language="VB" runat="server">
Sub Page_Load(sender As Object, e As EventArgs)
Dim root As String = "C:\temp\"
Dim filepath As String = Request.Params("file")
If Not filepath Is Nothing Then
filepath = root & filepath
If File.Exists(filepath) And filepath.StartsWith(root) Then
Dim filename As String = Path.GetFileName(filepath)
Response.Clear()
Response.ContentType = "application/octet-stream"
Response.AddHeader("Content-Disposition", _
"attachment; filename=""" & filename & """")
Response.Flush()
Response.WriteFile(filepath)
End If
End If
End Sub
</script>
<form id="form1" runat="server">
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
</form>
I tried uploading images files and then downloading them again, and it worked fine. However, only when I upload text files that I get the content of that file appended with some HTML content.
Here is a sample file that I have uploaded
Here is my sample text file
It consists of 3 lines only
And here is the file when I downloaded it back
Here is my sample text file
It consists of 3 lines only
<form name="form1" method="post" action="FileDownload.aspx?file=sample_file.txt" id="form1">
<div>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTE5NTU5ODQyNTBkZNCYbOVZJDRUAOnIppQYkwHUSlb0" />
</div>
<span id="Label1">Label</span>
</form>
I went to the file on the server and opened it to make sure that the additional HTML content was there, but as I said, the file was uploaded perfectly. Only when it is downloaded does it contain appended HTML stuff.
What is it that I am doing wrong? What can I do to make this additional HTML code go away?
Why does this problem only affect Text file, not images, EXE, XLS, DOC ,, etc?
Make sure to end your Response by calling Response.End() after Response.WriteFile(filepath) or else your form will be appended to the stream and sent to the client.
It will affect other files as well though most likely only be seen as garbage at the end of the file and ignored by their respective applications.
Response.redirect threw an exception. Shoaib's answer got me closer but it suppressed everything including the valid file content. What worked for me was reordering the statements as follows:
Response.ContentType = ContentType
Response.AppendHeader("Content-Disposition", "attachment; filename=" & originalFileName)
Response.WriteFile(filePath)
Response.Flush()
Response.SuppressContent = True
HttpContext.Current.ApplicationInstance.CompleteRequest()
Is is not safe to use Response.End(), It will always return a System.Threading.ThreadAbortException:.. if debugged and seen in the catch block...
Alternative..
Use the following instead...
Response.Flush();
Response.SuppressContent = true;
Response.WriteFile(filepath);
HttpContext.Current.ApplicationInstance.CompleteRequest();

Resources