PDFHandler.ash error when there is no file - asp.net

I am having an issue with my ASHX handler that is generating PDF.
When the user hits a "View PDF" button, it will look in the database for the PDF file and display it, but if there isn't a PDF file there it should display a blank page saying "no PDF available", but instead I get a "null reference" error on this line of code:
ms.WriteTo(context.Response.OutputStream)
Below is the code for the handler:
Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
'This class takes the uniqueidentifier of an image stored in the SQL DB and sends it to the output stream
'This saves storing copies of image files on the web server as well as in the DB
context.Response.Clear()
If context.Request.QueryString("fileSurveyID") IsNot Nothing Then
Dim filesID As String = context.Request.QueryString("fileSurveyID")
Dim fileName = String.Empty
Dim ms As MemoryStream = GetPDFFile(filesID)
context.Response.ContentType = "application/pdf"
context.Response.AddHeader("Content-Disposition", "attachment;filename=" & fileName)
context.Response.Buffer = True
ms.WriteTo(context.Response.OutputStream)
context.Response.End()
Else
context.Response.Write("<p>No pdf file</p>")
End If
End Sub
Can anyone tell me how to get rid of this error?

Simple If..Then should do the trick:
Dim ms As MemoryStream = GetPDFFile(filesID)
If ms IsNot Nothing Then
context.Response.ContentType = "application/pdf"
context.Response.AddHeader("Content-Disposition", "attachment;filename=" & fileName)
context.Response.Buffer = True
ms.WriteTo(context.Response.OutputStream)
context.Response.End()
End If

You probably want to move the following out of the if:
context.Response.End()
so it execute each time regardless.
However you say the following line executes when no PDF available:
ms.WriteTo(context.Response.OutputStream)
which would suggest something wrong with your if condition

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.

Error trying to download an XML file

I'm trying to download an XML file, the file already exists in the specified path, I am not familiar with VB and probably this code is not right, I need help just in it to be able to download an existing xml file, here's the code:
Protected Sub DownloadFile(ByVal sPath As String)
Dim TargetFile As New System.IO.FileInfo(sPath)
Response.Clear()
Response.AddHeader("Content-Disposition", "attachment; filename=" +
TargetFile.Name)
Response.AddHeader("Content-Length", TargetFile.Length.ToString())
Response.ContentType = "text/xml"
Response.WriteFile(TargetFile.FullName)
Response.End()
End Sub
The error returned in the console:
Uncaught Error: Sys.WebForms.PageRequestManagerParserErrorException: The message received from the server could not be parsed. Common causes for this error are when the response is modified by calls to Response.Write(), response filters, HttpModules, or server trace is enabled.
Contextualizing the problem:
I have serialized an object and created an XML file, then I would simply like to download this file, my difficulty is to download the file.
Dim oObj1 As New System.Xml.Serialization.XmlSerializer(GetType(eSocial.Eventos.evtTabHorTur.eSocial))
Dim sFileName = Date.Now.ToString("yyyyMMddHHmmss") & ".xml"
Dim sPath = Constantes.Ambiente.CaminhoSite & "temp\" & sFileName
Dim oFile As New System.IO.StreamWriter(sPath)
oObj1.Serialize(oFile, eSocialCamposXml)
oFile.Close()
You are saying you are having difficulty downloading but there is nothing in the code except showing writing a file and then serializing a file. You would be using a 'StreamReader' or similar manner to READ a file. Here is a simple example. Say I have an xml structure on a file location with the schema like:
<root>
<test>Data</test>
</root>
I could write this in VB.NET to get it:
Sub Main()
Dim xmlFile As XDocument
Dim fileLocation = "D:\\Test Code\\Test.xml"
Using sr = New StreamReader(fileLocation)
xmlFile = XDocument.Parse(sr.ReadToEnd())
End Using
Console.WriteLine(xmlFile.Root.Element("test").Value.ToString)
Console.ReadLine()
End Sub

Print SSRS Report from Code (asp.net)

I have found good information on how to automatically download a file to the client advertised as a solution of how to print using code
(https://forums.asp.net/t/1233841.aspx?How+do+I+print+from+Reporting+Services+AUTOMATICALLY+in+VB+Net+web+app+)
but what I need to do is have the code print the document without the user interacting.
From what I have found it appears this can not be done as one might casually think. ReportViewer for example does not have a 'print' method.
The only two solutions appears to be to use ProcessStart (which then means saving the file to the file system before printing which I dont want to do)
or maybe (will be researching this today) create a subscription using code and then delete it later.
You are not able to print a report directly from your asp.net page. The reason for this is security. If it allowed you to send a file through the network and onto the clients computer, and then search the computer for a printer, this could cause major security issues. The report viewer does have a print icon, but this disappears when you deploy the project and run the page remotely. I have faced the same issue in the past and found it best to just export the report to say PDF and allow the user to Download it. I have used the below code to accomplish this task in the past:
Private Sub CreatePDFMatrix(fileName As String)
' ReportViewer1.LocalReport.DataSources.Clear()
Dim adapter As New ReportDataSetTableAdapters.vwPrintTagsTableAdapter
Dim table As New ReportDataSet.vwPrintTagsDataTable
Dim month = MonthName(Date.Today.Month)
Dim year = Date.Today.Year
Dim p(1) As ReportParameter
Dim warnings() As Warning
Dim streamIds As String()
Dim mimeType As String = String.Empty
Dim encoding As String = String.Empty
Dim extension As String = String.Empty
Dim adpt2 As New ReportDataSetTableAdapters.vwPrintTagsTableAdapter
adapter.FillForMainReport(table, DropDownList1.SelectedValue, g_Company, g_Division)
Me.ReportViewer1.LocalReport.DataSources.Add(New ReportDataSource("DataSet1", CType(table, DataTable))) 'Add(New ReportDataSource("ReportingData", CType(table, DataTable)))
Me.ReportViewer1.DataBind()
Dim viewer = ReportViewer1
viewer.ProcessingMode = ProcessingMode.Local
viewer.LocalReport.ReportPath = "Report1.rdlc"
p(0) = New ReportParameter("MonthYear", month & "-" & year)
Dim check = DropDownList1.SelectedValue
ReportViewer1.LocalReport.SetParameters(p(0))
p(1) = New ReportParameter("Location", DropDownList1.SelectedValue)
ReportViewer1.LocalReport.SetParameters(p(1))
Try
Dim bytes As Byte() = viewer.LocalReport.Render("PDF", Nothing, mimeType, encoding, ".pdf", streamIds, warnings)
Response.Buffer = True
Response.Clear()
Response.ContentType = mimeType
Response.AddHeader("content-disposition", Convert.ToString((Convert.ToString("attachment; filename=") & fileName) + ".") & extension)
Response.BinaryWrite(bytes)
' create the file
Response.Flush()
Catch ex As Exception
Debug.Print(ex.ToString)
End Try
End Sub

VB.NET get bytes from SQL Table

It's been a while since I've been here. I've been working on an ASP project where the attachment is stored to DB. I'm trying to get the bytes stored in the table to a file.
I was able to make the file, however, I couldn't get the bytes from the table.
This is the code that enters the file to the table:
'Saves the upload file data to database for the using Request_ID after the request has been created
strFileName = FileUploadReqDtl.FileName
strFileExt = strFileName.Substring(strFileName.IndexOf(".") + 1)
intFileSize = FileUploadReqDtl.PostedFile.ContentLength()
Dim theDataSize As Byte() = New Byte(FileUploadReqDtl.PostedFile.ContentLength - 1) {}
Dim uploadedFile As HttpPostedFile = FileUploadReqDtl.PostedFile
Dim theData = uploadedFile.InputStream.Read(theDataSize, 0, CInt(FileUploadReqDtl.PostedFile.ContentLength))
I was able to create a link on a page that goes to another ASP page and tries to make the file downloadable. I know that the SQL is working properly to get the records from the table, however, I'm not able to get the bytes properly:
If (dr.HasRows = True) Then
While dr.Read()
' Casting the value returned by the datareader to the byte[] data type.
result = CType(dr.GetValue(0), Byte())
strFileName = dr.GetString(1)
Debug.Write("strFileName: " & strFileName & vbCrLf)
End While
End If
dr.Close()
cnToDb.Close()
ShowFile(strFileName, result, MimeType)
Catch ex As Exception
MsgBox("Uh oh, I couldn't get the attachment!")
Debug.Write("Uh oh, I couldn't get the attachment!")
Debug.Write(ex.Message & " - " & ex.StackTrace & vbCrLf & vbCrLf)
result = Nothing
End Try
This is the ShowFile function which finally "creates the file" with the bytes from the database:
Dim fileData() As Byte
fileData = byteFileData
Response.Clear()
Response.AddHeader("Content-Disposition", "attachment; filename=" + strFileName)
Response.ContentType = MimeType
Response.BinaryWrite(fileData)
Response.End()
Response.Flush()
The exception I'm hitting that I'm having issues with is:
SQL: SELECT Data, FileName FROM tblFileUploadData WHERE Report_ID =
116
strFileName: TheAwesomeAttachment.txt A first chance exception of type
'System.Threading.ThreadAbortException' occurred in mscorlib.dll Uh
oh, I couldn't get the attachment!Thread was being aborted. - at
System.Threading.Thread.AbortInternal() at
System.Threading.Thread.Abort(Object stateInfo) at
System.Web.HttpResponse.End() at
WebApplication3.WebForm2.ShowFile(String strFileName, Byte[]
byteFileData, String MimeType) in C:\Users\Mike\documents\visual
studio
2010\Projects\WebApplication3\WebApplication3\Attachment.aspx.vb:line
81 at WebApplication3.WebForm2.getAttachment(Int32 RequestID,
String MimeType) in C:\Users\Mike\documents\visual studio
2010\Projects\WebApplication3\WebApplication3\Attachment.aspx.vb:line
56
A first chance exception of type
'System.Threading.ThreadAbortException' occurred in
WebApplication3.DLL An exception of type
'System.Threading.ThreadAbortException' occurred in
WebApplication3.DLL but was not handled in user code The thread '<No
Name>' (0x728) has exited with code 0 (0x0). The program '[13512]
WebDev.WebServer40.EXE: Managed (v4.0.30319)' has exited with code 0
(0x0). The program '[13512] WebDev.WebServer40.EXE: Program Trace' has
exited with code 0 (0x0).
Take a look at:
http://support.microsoft.com/kb/312629
"For Response.End, call the HttpContext.Current.ApplicationInstance.CompleteRequest method instead of Response.End to bypass the code execution to the Application_EndRequest event"
I like to create an .ashx file for this type of situation. You can link to it and it will download the file without showing a page.
Public Class DownloadFile
Implements System.Web.IHttpHandler
Public Sub ProcessRequest(context As HttpContext) Implements IHttpHandler.ProcessRequest
Try
Dim id As String = context.Request.QueryString("id")
Dim filename As String = ""
Dim output As Byte()
filename = ServiceHelper.GetFilenameByID(id)
output = ServiceHelper.GetFileBytesByID(id)
Dim ext As String = System.IO.Path.GetExtension(filename)
Dim contenttype As String
'clear the current output content from the buffer
context.Response.Clear()
'add the header that specifies the default filename for the
'Download/SaveAs dialog
context.Response.AddHeader("Content-Disposition", "attachment; filename=" & filename)
'add the header that specifies the file size, so that the browser
'can show the download progress
context.Response.AddHeader("Content-Length", output.Length.ToString())
'specify that the response is a stream that cannot be read by the
'client and must be downloaded
'Response.ContentType = "application/octet-stream"
context.Response.Buffer = True
Select Case ext
Case ".doc"
contenttype = "msword"
Case ".pdf"
contenttype = "pdf"
Case ".xls"
contenttype = "x-excel"
Case ".ppt"
contenttype = "ms-powerpoint"
Case Else
contenttype = "octet-stream"
End Select
context.Response.ContentType = "application/" & contenttype
context.Response.BinaryWrite(output)
'http://support.microsoft.com/kb/312629
'context.Response.End()
context.ApplicationInstance.CompleteRequest()
Catch
End Try
End Sub
Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class

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

Resources