Render multiple PDF to browser with Report VIewer - ASP.NET - asp.net

Current situation
I have ASP.NET web application that render PDF for users using MS Report Viewer. The PDF is rendered with this method:
Byte pdfByte = Byte();
pdfByte = ReportViewer.LocalReport.Render("PDF", Nothing, mimeType, encoding, extensions, stream, warning)
And send to browser as an attachment with response object:
Response.Clear()
Response.ContentType = mimeType
Response.AddHeader("content-disposition", "attachment; filename=myfile." + extension)
Response.BinaryWrite(pdfByte)
Response.Flush()
Response.End()
This work great! The user browser will get the PDF as download-able attachment.
What I am trying to achieve
Render multiple PDF and send all of them separately to user's browser. User will get separate PDF documents. It doesn't matter whether they will get them all at once or one by one.
The problem
The problem is after Response.End() the next line of code is not executed. I have tried to store the pdfByte object in session, looping through it and send them to user's browser with Response object but after the first PDF get sent then it stop.
I have also tried removing Response.End() thinking the code will keep running but still it stop after the first PDF get sent.
Please advice any workaround or tips. Thanks!

You cannot send multiple files (as separate entities) in a single HTTP response (the protocol does not support it. However, what you can do is to archive all files together and send the that single zip (or whatever format you want) to the client.
You can use libraries such as DotNetZip/SharpZipLib to combine (and compress) files together. Based on library API, you may need to save PDF files to disk before adding to zip file. Also do not forget to change your content type appropriate while sending the zip file to client.
Yet another alternative is to provide user with a page having multiple links to download files. It may mean that you either have to store your PDFs for some time so that they can served later (via links) or make link point to a handler that will re-run the report again to get the PDF out of it.

Admittedly the method I'm using doesn't feel very elegant, but here's what I'm doing:
create one IFRAME on the page for each document you want to send to the client (maybe create the IFRAMEs dynamically in server-side code if the number of documents is variable);
create a HttpHandler that generates the PDF documents, depending on a parameter you're passing in through the QueryString, just like you're doing above;
set the src on all IFRAMES to the URL of the HttpHandler with the appropriate parameters attached.
Of course the HttpHandler needs to do implement security logic, if required.
This works quite beautifully: If I want to send 3 documents, I create 3 IFRAMEs, set their src, and the user will see 3 "Save As..." dialogs pop up.

Related

Providing PDF specific parameters in an ASP.NET Generic Handler writing a PDF BLOB stream

EDIT: modified Title to be more specific
I've created a generic handler in VS2012 using their basic template as a starting point and modified it to grab a pdf from our sqlserver. The primary code block is this:
buffer = DirectCast(rsp.ScalarValue, Byte())
context.Response.ContentType = "application/pdf"
context.Response.OutputStream.Write(buffer, 0, buffer.Length)
context.Response.Flush()
And this works fine to display the BLOB as a pdf using whichever pdf plugin is installed on any given browser.
My Question: How can I modify the handler to write Adobe PDF specific parameters to the output? Specifically I'm trying to set width='fit' such that the output PDF stream will autofit the document to the width of the popup window.
NB: Writing the BLOB to a pdf file and serving the PDF is not an option.
Thanks in advance for any advice or links
I don't think there's anything that you can do in your handler. According to that document PDF viewers can examine the URL that was used to open the PDF but there are no HTTP headers that you can set. So you'll need to modify the thing that links to your handler to have those parameters in place. Alternatively, you could build a pre-handler that HTTP redirects to your new handler with those parameters in place.
Also, that document was written in 2007 and was intended for Adobe Acrobat and Adobe Reader. Most modern browsers ship with their own internal PDF viewer these days so unless you are only targeting Adobe your efforts might be wasted.

"Save As" popup in response to a POST request?

Is it possible to get the browser to popup its Save As... dialog when doing a POST (rather than GET) request?
Using the Spring framework, I'm trying to build a service that will receive some data (a two-dimensional json array), and produce an Excel file that the user is prompted to download.
By doing a GET request, eg by browsing directly to my URL, I can get the browser to display the popup by setting headers similar to this:
response.setHeader("Content-Disposition", "attachment; filename=" + fileName)
response.setContentType("application/vnd.ms-excel")
I need to allow the client to post the data that will be used to build the Excel file though, which implies a POST request. The same headers are returned, but no popup.
Is there a way to achieve this, or does the popup appear only for GET requests?
I'm thinking I could do a two-step process; 1) allow the client to POST the data, and return some sort of reference key, 2) allow the client to do a GET request and include the key, and return the relevant headers to cause the browser to popup the dialog.
Any other thoughts on how to do this?
Thanks
It's possible. I'm not sure what I did wrong the first time I tried it out.

File upload and read from database

I am using file upload mechanism to upload file for an employee and converting it into byte[] and passing it to varBinary(Max) to store into database.
Now I what I have to do is, if any file is already uploaded for employee, simply read it from table and show file name. I have only one column to store a file and which is of type VarBinary.
Is it possible to get all file information from VarBinary field?
Any other way around, please let me know.
If you're not storing the filename, you can't retrieve it.
(Unless the file itself contains its filename in which case you'd need to parse the blob's contents.)
If the name of the file (and any other data about the file that's not part of the file's byte data) needs to be used later, then you need to save that data as well. I'd recommend adding a column for the file name, perhaps one for its type (mime type or something like that for properly sending it back to the client's browser, etc.) and maybe even one for size so you don't have to calculate that on the fly for each file (useful when displaying a grid of files and not wanting to touch the large blob field in the query that populates the grid).
Try to stay away from using the file name for system-internal identity purposes. It's fine for allowing the users to search for a file by name, select it, etc. But when actually making the request to the server to display the file it's better to use a simple integer primary key from the table to actually identify it. (On a side note, it's probably a good idea to put a unique constraint on the file name column.)
If you also need help displaying the file to the user, you'll probably want to take the approach that's tried and true for displaying images from a database. Basically it involves having a resource (generally an .aspx page, but could just as well be an HttpHandler instead) which accepts the file ID as a query string parameter and outputs the file.
This resource would have no UI (remove everything from the .aspx except the Page directive) and would manually manipulate the response headers (this is where you'd set the content type from the file's type), write the byte stream to the client, and end the response. From the client's perspective, something like ~/MyContent/MyFile.aspx?fileID=123 would be the file. (You can suggest a file name to the browser for saving purposes in the response headers, which you'd probably want to do with the file's stored name.)
There's no shortage of quick tutorials (some several years old, it's been around for a while) on how to do this with images. Just remember that there's essentially no difference from the server's perspective if it's an image or any other kind of file. All the server needs to do is send the type in the response headers and write the file's bytes to the client. How the client handles the file is up to the browser. In the vast majority of cases, the browser will know what to do (display an image, display via a plugin a PDF, save a .doc, etc.).

How can I return a pdf from a web request in ASP.NET?

Simply put, I'd like someone to be able to click a link, and get a one-time-use pdf. We have the library to create PDF files, so that's not an issue.
We could generate a link to an aspx page, have that page generate the pdf, save the pdf to the filesystem, and then Response.Redirect to the saved pdf. Then we'd somehow have to keep track of and clean up the PDF file.
Since we don't ever need to keep this data, what I'd like to do instead, if possible, is to have the aspx page generate the pdf, and serve it directly back as a response to the original request. Is this possible?
(In our case, we're using C#, and we want to serve a pdf back, but it seems like any solution would probably work for various .NET languages and returned filetypes.)
Assuming you can get a byte[] representing your PDF:
Response.Clear();
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition",
"attachment;filename=\"FileName.pdf\"");
Response.BinaryWrite(yourPdfAsByteArray);
Response.Flush();
Response.End();
Look at how HTTP works. The client (=browser) doesn't rely on extensions, it only wants the server to return some metadata along with the document.
Metadata can be added with Response.AddHeader, and one 'metadata line' consists of Name and Value.
Content-Type is the property you are interested in, and the value is MIME type of the data (study: RFC1945 for HTTP headers, google for MIME type).
For ordinal aspx pages (html, ....) the property is 'text/html' (not so trivial, but for this example it is enough.). If you return JPG image, it can have name 'image.gif', but as long as you send 'image/jpeg' in Content-Type, it is processed as JPG image.
Content-type for pdf is 'application/pdf'.
The browser will act according to default behaviour, for example, with Adobe plugin, it will display the PDF in it's window, if you don't have any plugin for PDF, it should download the file, etc..
Content-Disposition header says, what you should do with the data. If you want explicitly the client to 'download' some HTML/PDF/whatever, and not display it by default, value 'attachment' is what you want. It should have another parameter, (as suggested by Justin Niessner), which is used in case of something like:
http://server/download.aspx?file=11 -> Content-Disposition: attachment;filename=file.jpg says, how the file should be by default named.

Working with big files in classic ASP

I was wondering what's the best practise for serving a generated big file in classic asp.
We have an application with "export to excel" function that produces 10MB files. The excels are created by just calling a .asp page that has the Response.ContentType set to excel and has an HTML table for the data.
This gives as problem that it takes 4 minutes before the user sees the "Save as..." dialog.
My current solution is to call an .asp page that creates the excel on the server with AJAX and lets the page return the URL of the generated document. Then I can use javascript to display the on the original page.
Is this easy to do with classic asp (creating files on server with some kind of stream) while keeping security in mind? (URL should make people be able to guess the location of other files)
How would I go about handling deleted the generated files overtime? They have to be deleted periodicly as the data changes in realtime.
Thanks.
edit: I realized now that creating the file on the server will probably also take 4 minutes...
I think you are selecting a complex route, when the solution is simple enough (Though I may be missing some requirements)
If you to generate an excel, just call an asp page that do the following:
Response.clear
Response.AddHeader "content-disposition", "attachment; filename=myexcel.xls"
Response.ContentType = "application/excel"
'//write the content of the file
Response.write "...."
Response.end
This will a start a download process in the browser without needing to generate a extra call, javascript or anything
See this question for more info on the format you will choose to generate the excel.
Edit
Since Thomas update the question and the real problem is that the file take 4 minutes to generate, the solution could be:
Offer the user the send the file by email (if this is a workable solution in you server or hosting).
Generate the file async, and let the user know when the file generation is done (with an ajax call, like SO does when other user have added an answer)
To generate the file on the server
'//You should change for a random name or something that makes sense
FileName = "C:\temp\myexcel.xls"
FileNumber = FreeFile
Open FileName For Append As #FileNumber
'//generate the content
TheRow = "...."
Print #FileNumber, TheRow
Close #FileNumber
To delete the temp files generated
I use Empty Temp Folders a freeware app that I run daily on the server to take care of temp files generated. (Again, it depends on you server or hosting)
About security
Generate the files using random numbers or GUIds for a light protection. If the data is sensitive, you will need to download the file from a ASP page, but I think that you will be in the same problem again...(waiting 4 minutes to download)
Read file using FSO.
Set headers for Excel file-type, name according to file read and for download (attachment)
Flush response after headers are set. The client should display "save as" dialogue.
Output FSO to response. Client will download file and see progress bar.
How do you plan to generate the Excel? I hope you don't plan to call Excel to do that, as it is unsupported, and generally won't work well.
You should check to see if there are COM components to generate Excel that you can call from Classic ASP. Alternatively, add one ASP.NET page for the purpose. I know for a fact that there are compoonents that can be called from ASP.NET pages to do this. Worse come to worst, there's an Excel exporter component from Infragistics that works with their UltraWebGrid control to export. The grid need not be visible in order to accomplish this, but styles in the grid translate to styles in the spreadsheet. They also allow you to manipulate the spreadsheet programmatically.

Resources