Output XML as file in ASP.NET - asp.net

I'm having difficulty sending an XML stream to the client browser. I've researched this considerably and everything looks right - moreover, similar code works in a previous version of this app. Any ideas what I could be doing wrong?
The following code throws no errors, but does not download a file on the client machine.
Public Shared Sub Export(ByVal source As DataTable)
Try
With Current.Response
Dim xml As String = CreateExcelXMLFromDataTable(source.DefaultView)
.Clear()
.Buffer = True
.ContentType = "application/vnd.ms-excel"
.AddHeader("Content-Disposition", String.Concat("attachment;filename=", "export.xlsx", ";"))
.AddHeader("Content-Length", xml.Length.ToString)
.Charset = ""
.Write(xml)
.Flush()
.Close()
End With
Catch ex As Exception
Console.WriteLine(ex.Message.ToString)
End Try
End Sub
Thanks!

If you are calling this function in an Ajax request it will not work. You have to cause a full postback to send the file to the client.
You can send the file without causing a full postback in the current page using an popup window (window.open) or an iframe with the URL of the download asp.net page, which will call the Export function.

Related

Server.MapPath changes file path when passed outside of Class

I abbreviated my code here and hope convey enough data to express the problem I am having. I am more than happy to elaborate as needed.
Background:
I have an asp site that has about 70 pages that open files from various locations.
In one scenario I do some file manipulation, like copy, rename, convert to PDF etc.
This is done my moving the file into the project and then eventually serving the file from a project folder.
Originally I created a class with a few functions.
I call the function from the web page and the class manipulates and then opens the file.
Dim ReturnValue As String = OpenMyFile.OpenQCBD(Doc_Id)
The function would manipulate the file and the open it (note the creation of the file path)
OpenTempFile(HttpContext.Current.Server.MapPath(fpath & "\") & FileName.ToLower, FileName)
Then opens it (contained in the class)
Public Sub OpenTempFile(strURL As String, FileName As String)
Dim req As WebClient = New WebClient()
Dim response As HttpResponse = HttpContext.Current.Response
response.Clear()
response.ClearContent()
response.ClearHeaders()
response.Buffer = True
response.AppendHeader("Content-Disposition", "attachment; filename=""" & FileName & """")
response.WriteFile(strURL)
response.Flush()
response.SuppressContent = True
HttpContext.Current.ApplicationInstance.CompleteRequest()
This all worked great and passed the proper file path and opened the file (e.g. \\MyServer\Folder...) This was tested both locally and in production and worked as expected.
I had to make a change and pass the file path back to the asp page and then call the procedure to open the file from there.
Class the function from asp page (same)
Dim ReturnValue As String = OpenMyFile.OpenQCBD(Doc_Id)
Instead of opening the file return the file path
Result = HttpContext.Current.Server.MapPath(fpath & "\") & FileName.ToLower
And then open the file (call from asp page)
OpenMyFile.OpenTempFile(FilePath, Path.GetFileName(FilePath))
This works great running locally on my machine.
However when I run it from the production server the class function returns C:Folder/.. instead //server/folder/... like it did before.
Construction of the file path is the same in both scenarios.
OpenTempFile(HttpContext.Current.Server.MapPath(fpath & "\") & FileName.ToLower, FileName)
vs
Result = HttpContext.Current.Server.MapPath(fpath & "\") & FileName.ToLower
The only difference is passing it back to the asp page, this is where I receive the wrong path.
Again - works fine on my local machine
Any help or direction would be super helpful, thanks in advance.

ASP.NET how to return pdf file as response without changing response header

I want to return a pdf file as response to some button click.
I succeeded to send the pdf file, but when i try to save it via the browser, it won't let me save it as a .pdf file (but as .aspx file)
here's the code:
Dim myWebClient As WebClient = New WebClient()
Dim myDataBuffer As Byte() = myWebClient.DownloadData(LocalImageURL) ' LocalImageURL is some path to a pdf file
Response.ContentType = "application/pdf"
Response.BinaryWrite(myDataBuffer)
Response.Flush()
Response.End()
if I am adding also the following line before writing the byte array:
Response.AddHeader("content-disposition", "attachment;filename=report.pdf")
it does the trick, but the problem is that the page remains stuck (looks like it still waits for server response to come)
This has been asked and answered before. Details are at the link below:
asp.net VB.net file download handler not work properly

Display Xml document in Browser vb.net

I need to render a xml document in either an new asp.net page or a pop up page.
I am following the article
I am getting the following error "Thread was being aborted." but the xml document is rendered in the page.
is there any better example
Try
If Not String.IsNullOrEmpty(tempFilePath) Then
Response.Clear()
Response.Buffer = True
Response.Charset = ""
Response.Cache.SetCacheability(HttpCacheability.NoCache)
Response.ContentType = "application/xml"
'Response.WriteFile(Server.MapPath(tempFilePath)) ' Todo Change path to temp location and add th
Response.WriteFile("c:\Test.xml")
Response.Flush()
Response.[End]()
End If
Catch ex As Exception
'TODO
Throw ex
End Try
Response.End explicitly throws that exception as result of aborting the current thread (Is Response.End() considered harmful?) - so if you are logging all exceptions or just watching in debugger you'll see ThreadAbortException.
To stop sending extra content you can follow recommendation in linked question (C#):
Response.Flush();
Response.SuppressContent = True
HttpContext.Current.ApplicationInstance.CompleteRequest()
Based on your comment you are trying to render XML as part of the same response as main HTML of the page and it is not really possible (at least end result will not be "downloadable file"). It is generally easier to serve files from separate handler that does not have any HTML rendering associated.

DotNetZip download works in one site, not another

EDIT - RESOLVED: the difference was that in the "main" case the download was initiated via a callback cycle, and in the "test" case it was initiated through a server side button click function. My guess is that the download request and the callback cycle interfered with each other, both stopping the download and causing the page to become inactive (as described below). When I rewired the download on the main page to start with a submit instead of a callback, it did initiate the download.
This is in VS2013 Ultimate, Win7Pro, VB.Net, websites (not projects),IISExpress.
I built a test site to develop functionality for creating OpenXML PPTX and XLSX memorystreams and zipping and downloading them using DotNetZip. Got it to work fine. I then merged all that code into my "main" site. Both sites are on the same machine; I can run the test site and the main site at the same time. The main site processing is somewhat more complicated, but only in terms of accessing and downloading more files.
However, the Zip and Download function (below) works fine in the test site, but the exact same code doesn't work in the main site (with or without the test site up and running).
There's an error trap (see below) around the Zip.Save function where the download occurs but no error shows up.
Same overall behavior in Chrome, Firefox and IE11.
One peculiarity that might be a clue is that when the main site download fails, the server side functionality "goes dead". Local JS functions work, but the app doesn't respond to callbacks. When I do an F5 on the browser it works again.
I did a refresh on the DotNetZip package in the main site. The Zip object appears to be working properly, because it generates an error on duplicate file names.
I thought it might be the download function as written, however, it works in the test site. Also, another piece of the main site does a non-zipped download of a memory stream (included as the second code block below) and that works fine.
I thought it might be the data. So I kludged the main site to access, convert to memorystream and download the same file that the is accessed and downloaded in the test site. Still the main site download doesn't work.
When I compare the watch values on the Zip object in the two sites, they look identical. The length of the wrkFS.ContentStream is identical in both cases. The file names are different, however, they are:
Test_2EFVG1THK5.xlsx (main)
6-18_12-46-28_0.xlsx (test)
which are both legal file names.
EDIT: I saved the zip file to disk from the main program, instead of trying to download it, using this:
wrkFilePath = "D:\filepath\test.zip"
wrkZip.Save(wrkFilePath)
And it worked fine. So that possibly isolates the problem to this statement
wrkZip.Save(context.Response.OutputStream)
EDIT: Base on help I received here:
Convert DotNetZip ZipFile to byte array
I used this construct:
Dim ms as New MemoryStream
wrkZip.Save(ms)
wrkBytes = ms.ToArray()
context.Response.BinaryWrite(wrkByteAr)
to get around the ZipFile.Save(to context), and that didn't work either; no download, no error message, and page goes dead. However, at least I can now assume it's not a problem with the ZipFile.Save.
At this point I'm out of ways to diagnose the problem.
Any suggestions would be appreciated.
Here is the code that works in the test site but not in the main site.
Public Sub ZipAndDownloadMemoryStreams(ByVal context As HttpContext) _
Implements IHttpHandler.ProcessRequest
Dim rtn As String = ""
Try
Dim wrkAr As ArrayList
wrkAr = SC.ContentArrayForDownLoad
If wrkAr.Count = 0 Then
Dim wrkStop As Integer = 0
Exit Sub
End If
Dim wrkFS As ZipDownloadContentPair
Using wrkZip As New ZipFile
'----- create zip, add memory stream----------
For n As Integer = 0 To wrkAr.Count - 1
wrkFS = wrkAr(n)
wrkZip.AddEntry(wrkFS.FileName, wrkFS.ContentStream)
Next
context.Response.Clear()
context.Response.ContentType = "application/force-download"
context.Response.AddHeader( _
"content-disposition", _
"attachment; filename=" & "_XYZ_Export.zip")
'---- save context (initiate download)-----
wrkZip.Save(context.Response.OutputStream)
wrkZip.Dispose()
End Using
Catch ex As Exception
Dim exmsg As String = ex.Message
Dim wrkStop As String = ""
End Try
End Sub
Below is the non-zip download function that works fine in the main site.
It might be possible to convert the Zip content to a byte array and try the download that way, however, I'm not sure how that would work.
(SEE EDIT NOTE ABOVE --- I implemented a version of the below, i.e. try to download byte array instead of directly ZipFile.Save(), however, it didn't help; still doesn't download, and still doesn't give any error message)
Public Sub DownloadEncryptedMemoryStream(ByVal context As HttpContext) _
Implements IHttpHandler.ProcessRequest
Dim wrkMemoryStream As New System.IO.MemoryStream()
wrkMemoryStream = SC.ContentForDownload
Dim wrkFileName As String = SC.ExportEncryptedFileName
wrkMemoryStream.Position = 0
Dim wrkBytesInStream As Byte() = New Byte(wrkMemoryStream.Length - 1) {}
wrkMemoryStream.Read(wrkBytesInStream, 0, CInt(wrkMemoryStream.Length))
Dim wrkStr As String = ""
wrkStr = Encoding.UTF8.GetString(wrkMemoryStream.ToArray())
wrkMemoryStream.Close()
context.Response.Clear()
context.Response.ContentType = "application/force-download"
context.Response.AddHeader("content-disposition", "attachment; filename=" & wrkFileName)
context.Response.BinaryWrite(wrkBytesInStream)
wrkBytesInStream = Nothing
context.Response.End()
(Per the note now at the top of the question): The difference was that in the "main" case the download was initiated via a callback cycle, and in the "test" case it was initiated through a server side button click function. My guess is that the download request and the callback cycle interfered with each other, both stopping the download and causing the page to become inactive (as described below). When I rewired the download on the main page to start with a submit instead of a callback, it did initiate the download.

PDF Binary instead of Viewer

In an application we have here we create an advice booklet pdf's for users on a web app. These are quite hefty documents and take upto 30 seconds to generate. We decided the best method for this was to generate the pdf in the background of the application before the user requests it.
If the user requests the PDF a page is opened that checks if the booklet is complete and when it is displays it to the user.
This works great in every case unless adobe has been set to NOT 'Display PDF in browser' and the user is using IE8 or IE7.
In this case we get a warning message about a download file and the file is displayed as Binary text.
We pull the pdf when the application triggers an ajax call that generates that pushes the content using the following code.
To loop until the booklet is ready.
If pdfState = PdfBooklet.Status.Complete Then
If Me.SupportsScript Then
' Send signal back to Ajax call saying we're ready
Response.ClearContent()
Response.Write("complete")
Response.End()
Else
' Non-JS can just have it delivered straight away
pdf = CType(HttpContext.Current.Session(PdfBooklet.sSessionBookletKey), Byte())
ServerPDFBooklet(pdf)
End If
Else
' Not finished yet
Response.ClearContent()
If Me.SupportsScript Then
If isAjaxRequest Then
' Signal back to Ajax to wait a bit long
Response.Write("waiting")
Response.End()
End If
Else
' Non-JS => page refresh
Response.AddHeader("refresh", "3")
End If
End If
And to write the pdf
With Response
.Clear()
.ClearHeaders()
.AddHeader("Content-Disposition", "inline; filename=" & pdfFilename & ".pdf")
.ContentType = "application/pdf"
Try
.BinaryWrite(binaryPDF)
.Flush()
.End()
Catch Ex As HttpException When Ex.ErrorCode = &H80072746
'Client disconnected
End Try
End With
The easy way for us to fix this is to just response.redirect to a new page to serve the booklet when its complete but we would rather not do this.
Any ideas...?

Resources