download server generated file (.vcf) - asp.net

i have an interactive aspx dialog with some address data (like name, email, address,...). Now i want the user to be able by clicking a button to download the address data as vcf file.
Now, generating the vcf compatible string isn't the problem. But saving it to the client is.
While it returns the vcf string just fine, it does not open a "Save AS"-dialog. Below i attached my logic for the file download.
What am i doing wrong?
(Maybe it's worth mentioning that the code-behind function calls come from java script,...)
Thanks for any helpfull answers in advance.
Public Sub SaveText(ByVal Text As String)
Dim FileName As String = System.IO.Path.GetRandomFileName()
Using sw As New System.IO.StreamWriter(Server.MapPath(FileName + ".txt"))
sw.WriteLine(Text)
sw.Close()
End Using
Dim fs As System.IO.FileStream = Nothing
fs = System.IO.File.Open(Server.MapPath(FileName + ".txt"), System.IO.FileMode.Open)
Dim btFile(fs.Length) As Byte
fs.Read(btFile, 0, fs.Length)
fs.Close()
With HttpContext.Current.Response
.Clear()
.Buffer = True
.Expires = 0
.AddHeader("Content-disposition", "attachment;filename=" + FileName)
.AddHeader("Content-Length", btFile.Length.ToString)
.ContentType = "application/octet-stream"
.BinaryWrite(btFile)
'.OutputStream.Write(btFile, 0, btFile.Length())
.Flush()
.End()
End With
End Sub

Ok, the problem was not the above mentioned logic itself. The way i handeled the response on the client side was just wrong. The calling java script function expected something else.
I would elaborate in more detail, but this stuff here is so home grown and proprietary, it wouldn't make any sense.
Cheers.

Related

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.

Get blob from SQL and write to browser for download

ASP / .NET 3.5 & SQL Server 2005
I'm trying to download a blobbed PDF from our SQL database, then without physically writing the file anywhere, output the file and download to user with the "Save / Open".
The code processes through without throwing an Exception, but no download occurs.
To be honest I'm not certain how close this is to "working"... I've tried piecing together several different samples from various posts, being that nothing seems to match my particular situation.
Response.ClearContent()
Response.Clear()
Response.ContentType = "application/pdf"
Response.AddHeader("Content-Disposition", "attachment; filename=MY_DOWNLOAD.pdf")
Dim bufferSize As Integer = 100
Dim outByte(bufferSize - 1) As Byte
Dim strSql As String = "SELECT BlobData, BlobData FROM Attachment WHERE RecordID = " & CInt(Request.QueryString("pid").ToString)
Using connection As New SqlConnection(ConfigurationManager.ConnectionStrings("SqlServer").ConnectionString)
connection.Open()
Dim scSql As SqlCommand = New SqlCommand(strSql, connection)
Dim sdrSql As SqlDataReader = scSql.ExecuteReader(CommandBehavior.SequentialAccess)
Do While sdrSql.Read
Dim startIndex As Long = 0
Dim retVal As Long = sdrSql.GetBytes(1, startIndex, outByte, 0, bufferSize)
Do While retVal = bufferSize
Response.BinaryWrite(outByte)
Response.Flush()
startIndex += bufferSize
retVal = sdrSql.GetBytes(1, startIndex, outByte, 0, bufferSize)
Loop
Response.BinaryWrite(outByte)
Response.Flush()
Response.End()
Loop
sdrSql.Close()
End Using
I would strongly recommend saving the files to a temporary location on disk, then letting Response.TransmitFile send the file for you.
The problem with the approach that you are using is that you will have two copies of each file in memory. If your files are 100MB each and you have 100 simultaneous users, this will result in 20GB of memory being used by your app.
Also, your current code will send at most one file to the user since you end the response within the SQL loop.
Finally, Response.End is no longer recommended since it causes problems in non-IE browsers. Instead, use Context.ApplicationInstance.CompleteRequest().
As pointed out in the comments, you should wrap the creation of the temp file and TransmitFile operation in a try/finally statement and delete the temp file in the finally portion. This will handle various exceptions (such as the end user getting disconnected part way through the transmission process) and ensure that you don't have a bunch of temp files stranded on your disk.
While I agree with competent_tech's answer above, if you still feel like you don't want to write the file to disk and want to fetch it, then convert it to byte array and stream it as a "Save As", then you could try the following (sorry it's C#, but you can convert it to VB.NET pretty easily):
byte[] byteArray = GetBytesFromSource();
Response.AddHeader("Content-Disposition", "attachment; filename=Test.pdf");
Response.AddHeader("Content-Length", byteArray.Length.ToString(CultureInfo.CurrentCulture));
Response.ContentType = "application/octet-stream";
Response.OutputStream.Write(byteArray, 0, byteArray.Length);
Response.Flush();
I just tested this and it works like a charm :)

Saving Files to user Computer in VB.Net

I have look for days, with no luck.
Can anyone tell me how to save files in VB.Net to any computer? There are lost of articles, but those only tell you about saving to your own computer by giving folder access, not a random user's computer.
You can see my example here http://hanontest.com/POShellCreator.aspx (You have to enter text into task code, project id and notes field, then click create. Then click export, you will see the error.)
I can go to a local Pizza shop website and download a menu pdf, I know its possible.
In my example it saves when you click the button, I would like a save as dialog if anyone knows how to do that as well.
Here is the save string:
Dim regDate As Date = Date.Now()
Dim strDate As String = regDate.ToString(".yyyy\.MM\.dd")
TextBox5.Text = "c:\temp\" & Vendor & "&" & Vendor2 & "&" & Vendor3 & "&" & Vendor4 & TaskEmpty & strDate & ".csv"
Here is how I am saving:
Public Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
'Define Path to save file.
Dim path As String = TextBox5.Text
' Create or overwrite the file.
Dim fs As FileStream = File.Create(path)
' Add text to the file.
Dim info As Byte() = New UTF8Encoding(True).GetBytes(TextBox3.Text)
fs.Write(info, 0, info.Length)
fs.Close()
End Sub
The error you're seeing there is because you're attempting to save the text file to the server's
file system, which you don't have write access to.
To return a file to user from the server you need to do a little more work, and it's not going to simple on a postback from a button.
You need to send the data down to the client in the Response.OutputStream, instead of your page, and also tell the browser to treat it as a file download:
Response.ContentType = "text/plain";
Response.AppendHeader("Content-Disposition", "attachment; filename=textFile.csv");

I have path Manipulation issue. The following code is placed in Page_load method of ASPx page + VB

If Request.QueryString("ID") = "" Then
folderDirectory = Global.FileUpload.GetFolderDirectory(Request.QueryString("TFID"))
If Not File.Exists(folderDirectory + fileName) Then
If Not Directory.Exists(folderDirectory) Then
Directory.CreateDirectory(folderDirectory)
End If
Dim bufferSize As Integer = Me.fileUpload.PostedFile.ContentLength
Dim buffer As Byte() = New Byte(bufferSize) {}
' write the byte to disk
Using fs As New FileStream(Path.Combine(folderDirectory, fileName), FileMode.Create)
Dim bytes As Integer = Me.fileUpload.PostedFile.InputStream.Read(buffer, 0, bufferSize)
' write the bytes to the file stream
fs.Write(buffer, 0, bytes)
End Using
Else
CallOnComplete("error", "", "Error uploading '" & fileName & "'. File has been exists!")
Exit Sub
End If
But Fortify scan report for the above sample code shows Path Manipulation issue as high. I Need help to modify above code so that it can pass fortify scan
It is showing me error at folderDirectory
Usually, when your code works inside a web application you don't have the liberty to use the full file system as you do on your local PC. Any kind of 'Path Manipulation' is suspicious.
You should try to recode your works using Server.MapPath method.
Pay particular attention to this warning
For security reasons, the AspEnableParentPaths property has a default value set to FALSE.
Scripts will not have access to the physical directory structure unless AspEnableParentPaths
is set to TRUE.

ASP.NET Filename encoding while sending file

I am sending a file from ASP.NET Page to the browser. To properly send a filename I am adding a header:
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", "attachment; filename=" + filename);
The problem is that when file contains white spaces (e.g. "abc def") browser receives only "abc" part of the filename. I have tried with: Server.HtmlEncode but it didn't help.
Do you have any idea how to solve this problem?
PK
Put the file name in quotes:-
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
Don't UrlEncode. This is not the right way to escape a value for use in an HTTP structured header parameter. It only works in IE due to that browser's buggy handling, and even then not reliably.
For a space you can use a quoted-string as suggested by Anthony (+1). But the dirty truth of Content-Disposition is that there is no reliable, supported escaping scheme that can be used to put arbitrary characters such as ;, " or Unicode characters in the filename parameter. The only approach that works reliably cross-browser is to drop the filename parameter completely and put the desired filename in the URI as a trailing, UTF-8+URL-encoded path part.
See this answer for some background.
Filename with special symbols(e.g: space; # # ! $ ) or Non-Unicode characters either cannot be supported by some browsers or cause incorrect filename in client machine.
Here is an article by a Chinese called chanext, he gave a perfect way to solve this problem:
this article gave a sample code(written with c#) to show how to get perfect solution to this problem in the all four popular browsers (IE; Opera; Firefox and Chrome)
the filename "Microsoft.Asp.Net.doc" and "F ile;;!#%#^&y.doc" can both be output correctly using the way the author provided in this article.
http://ciznx.com/post/aspnetstreamdownloaddisplaynonunicodespacechar.aspx
Based on the code referenced by #chanext I cleaned it up and put it into a single extension method. Hope this can help someone.
Partial Class Uploader
Inherits Page
Private Sub UploadFile()
Dim sFileName As String
Dim oPdf As MigraDoc.Rendering.PdfDocumentRenderer
sFileName = "File Name With Spaces #22.pdf"
With Me.Request.Browser
If .Browser = "InternetExplorer" OrElse .Browser = "IE" Then
sFileName = sFileName.EncodeForIE
Else
sFileName = String.Format("""{0}""", sFileName)
End If
End With
oPdf = New MigraDoc.Rendering.PdfDocumentRenderer
oPdf.Document = FileFactory.CreatePdf()
oPdf.RenderDocument()
Using oStream As New MemoryStream
oPdf.Save(oStream, False)
Me.Response.Clear()
Me.Response.ContentType = "application/pdf"
Me.Response.AddHeader("content-disposition", String.Format("attachment; filename={0}", sFileName))
Me.Response.AddHeader("content-length", oStream.Length)
Me.Response.BinaryWrite(oStream.ToArray)
End Using
Me.Response.Flush()
Me.Response.End()
End Sub
End Class
Public Module StringExtensions
<Extension()>
Public Function EncodeForIE(Url As String) As String
Dim _
sReservedChars,
sEncodedString As String
sReservedChars = "$-_.+!*'(),#=&"
With New StringBuilder
Url.ToList.ForEach(Sub(C)
If Char.IsLetterOrDigit(C) OrElse sReservedChars.Contains(C) Then
.Append(C)
Else
With New StringBuilder
C.ToBytes.ToList.ForEach(Sub(B)
.AppendFormat("%{0}", Convert.ToString(B, 16))
End Sub)
sEncodedString = .ToString
End With
.Append(sEncodedString)
End If
End Sub)
Return .ToString
End With
End Function
<Extension()>
Public Function ToBytes(Chr As Char) As Byte()
Return Encoding.UTF8.GetBytes(Chr.ToString)
End Function
End Module

Resources