Getting a dynamically created PDF from a URL - asp.net

I have a need to aquire a pdf file generated from an aspx site.
Backstory:
I want to get schedueles from a website, I have succeded in getting the url to the scheduele, containing parameters. If you follow the url you will be shown a PDF stream, created by the site using given parameters.
http://www.novasoftware.se/ImgGen/schedulegenerator.aspx?format=pdf&schoolid=57240/sv-se&type=1&id=%7bD8920398-FA90-4960-BD47-69A8EFF7204D%7d&period=&week=38&mode=0&printer=1&colors=2&head=1&clock=1&foot=1&day=0&width=2480&height=3507&count=1&decrypt=0
This URL is what the site uses to get the appropriate scheduele and display it as a pdf.
Since it's not theoretically a PDF file, I can't download it as one using:
Dim Downloader As New WebClient
Downloader.DownloadFile(URL, fileName)
Neither can i download the content as a string using...
Dim Downloader As New WebClient
Dim Result As String = Downloader.DownloadString(URL)
...since it will result in an 500 Server Error due to missing parameters.
The only way of displaying the file/stream/document or whatever it's real name is, is to visit the given url through a browser. Given that, I tried to grab the PDF through a WebBrowser control. But since it "Can't display the XML page" (error displayed when navigating to the scheduele URL) I can't use that approach either.
So my question is, How do I download this data as a PDF or How do I get this data as a Stream that i can read with StreamReader?
Sidenotes:
The page uses no further client-side connections so I cannot backtrack it.
(I used Fiddler2 to track all the connections)
Using Fiddler, I can tell that the url SomethingSomething.com/Something.aspx generates a PDF and displays it. First few lines of the generated document (through Fiddler)
%PDF-1.4
%����
%
%wPDF by WPCubed GmbH V3.54x[0]
%
%
1 0 obj
<>
There are more information I can get from Fiddler about this site, so if you need it either try it yourself or ask me to add the information here.

Use the code below to download the file to memory stream:
Dim webRes As HttpWebResponse = Nothing
Dim memStream As New MemoryStream
Try
Dim webReq As HttpWebRequest = DirectCast(WebRequest.Create(url), HttpWebRequest)
webReq.Credentials = CredentialCache.DefaultCredentials
webRes = webReq.GetResponse
Dim resStream As Stream = webRes.GetResponseStream
Dim bytesRead As Integer
Do
Dim buffer(1023) As Byte
bytesRead = resStream.Read(buffer, 0, buffer.Length)
memStream.Write(buffer, 0, bytesRead)
Loop Until bytesRead = 0
Catch ex As Exception
Finally
If webRes IsNot Nothing Then
webRes.Close()
webRes = Nothing
End If
memStream.Seek(0, SeekOrigin.Begin)
End Try
' optionally save the stream into a file
memStream.WriteTo(New FileStream("d:\filename.pdf", FileMode.Create))

Related

Correct way of streaming a CSV of unknown size from memory

I have a database that has a table with just over 2 million records of about 20 columns. The user is able to query the database and limit the number of records returned so the recordset may be from 1 to 2 million.
As it is tabular information I want to send the data as a CSV. I'm using a StreamWriter to write the data to memory and once the file is complete I'm sending it as an HttpResponseMessage. My code is below, and it works fine as long as I don't run out of memory. Is there a way for me to stream the file as it's being processed so that the memory used is minimal?
<HttpGet, Route("api/records/export")>
Public Function ExportRecords() As HttpResponseMessage
Dim stream As New MemoryStream
Dim writer As New StreamWriter(stream)
writer.WriteLine("")
' Processing of data here
writer.WriteLine("""Write Data to MemoryStream"")
writer.Flush()
stream.Position = 0
Dim result As New HttpResponseMessage(HttpStatusCode.OK)
result.Content = New StreamContent(stream)
result.Content.Headers.ContentType = New Headers.MediaTypeHeaderValue("text/csv")
result.Content.Headers.ContentDisposition = New Headers.ContentDispositionHeaderValue("attachment") _
With {.FileName = "I" & Format(Date.Now, "yyMMdd") & ".csv"}
Return result
End Function
I've read on StackOverflow answers to questions such as Returning binary file from controller in ASP.NET Web API but these all deal with streaming a web response from a file stored on disk and not from memory.
As the comment on my question suggested I used PushStreamContent to stream the contents of my database to the browser in CSV format. I also made it asynchronous to get even more performance out of the export.
There is an important limitation with using PushStreamContent to stream content to the client from the server. Since the 200 OK header is sent first before anything is streamed, the response doesn't know ahead of time if there's going to be an error in the results as they're returned. If something goes wrong while the results are being sent, the client will just see a generic network error on its end. It's up to the server to log any error so that you can check server logs to find the specific error.
Here is the code for PushStreamContent that I used (the error checking is removed for brevity).
Dim result As HttpResponseMessage = New HttpResponseMessage(HttpStatusCode.OK) With {
.Content = New PushStreamContent(Async Function(outStream, httpContent, context)
Dim writer As StreamWriter = New StreamWriter(outStream)
Await writer.WriteLineAsync("""Header 1"",""Header 2"",""Header 3""")
For Each item In returnItems
Await writer.WriteLineAsync("""" & item.Col1.ToString & """,=""" & item.Col2.ToString & """,=""" & item.Col3.ToString & """")
Await writer.FlushAsync()
Next
outputStream.Close()
End Function)}
result.Content.Headers.ContentType = New Headers.MediaTypeHeaderValue("text/csv")
result.Content.Headers.ContentDisposition = New _
Headers.ContentDispositionHeaderValue("attachment") With {.FileName = "MyCSV.csv"}
Return result

VB.NET FTP Picture Upload Error [duplicate]

This question already has answers here:
Zip file is getting corrupted after downloading from server in C#
(3 answers)
Closed 4 years ago.
I am trying to allow authenticated users to upload pictures to the server through FTP. The code works for the most part. The part that doesn't is that there is an issue in uploading the file. I have tried to upload a few different pictures and all of them are larger on the server and therefore, not properly constructed.
One picture I tried is 4.56MB on my computer and 8.24MB on the server. When I load the picture in Photo, it states "We can't open this file." The page location is at http://troop7bhac.com/pages/slideshowedit.aspx. The following is the VB.NET code behind the upload:
Sub uploadFile_Click(sender As Object, e As EventArgs)
lblUploadErrors.InnerHtml = ""
If (lstSlideshowChoose.SelectedValue = "") Then
lblUploadErrors.InnerHtml = "<p>A slideshow must be selected.</p>"
Else
If (FileUpload1.HasFile) Then
Dim nameList() As String
Dim successList() As String
Dim i As Integer = 0
For Each file As HttpPostedFile In FileUpload1.PostedFiles
Dim fileBytes() As Byte = Nothing
Dim fileName As String = Path.GetFileName(file.FileName)
Dim photoRE As New Regex("^[A-z0-9 _]{1,}\.jpg|JPG|jpeg|JPEG|png|PNG+$")
Dim photoSuccess As Boolean = photoRE.Match(fileName).Success
ReDim Preserve nameList(i)
ReDim Preserve successList(i)
If (photoSuccess = True) Then
Using fileStream As New StreamReader(file.InputStream)
fileBytes = Encoding.UTF8.GetBytes(fileStream.ReadToEnd())
fileStream.Close()
End Using
Try
Dim request As FtpWebRequest = DirectCast(WebRequest.Create(ftpPath & lstSlideshowChoose.SelectedValue & "/" & fileName), FtpWebRequest)
request.Method = WebRequestMethods.Ftp.UploadFile
request.Credentials = New NetworkCredential(ftpUser, ftpPass)
Using uploadStream As Stream = request.GetRequestStream()
uploadStream.Write(fileBytes, 0, fileBytes.Length)
uploadStream.Close()
End Using
Dim response As FtpWebResponse = DirectCast(request.GetResponse(), FtpWebResponse)
response.Close()
successList(i) = "Success "
Catch ex As Exception
successList(i) = "Failed "
End Try
Else
successList(i) = "Failed "
End If
nameList(i) = fileName
i += 1
Next
For x As Integer = 0 To nameList.Count - 1
lblUploadErrors.InnerHtml += "<p>" & successList(x) & nameList(x) & "</p>"
Next
Else
lblUploadErrors.InnerHtml = "<p>You have not selected a picture to upload.</p>"
End If
End If
End Sub
The files are obtained through an ASP.NET FileUpload control. The control has been set to allow multiple files at once.
Any help to figure out why the pictures are not uploading properly would be greatly appreciated.
EDIT: I tried Martin Prikryl's possible duplicate solution. Had to change it from C# to VB.NET. It failed. I tried David Sdot's solution and it also failed. Both solutions returned the same errors.
If the page was ran on my local machine, it returned "C:\Program Files (x86)\IIS Express\PictureName.JPG." If the page was ran on the server, it returned "C:\Windows\SysWOW64\inetsrv\PictureName.JPG." Both errors are of the System.IO.FileNotFoundException class.
Your Problem is here:
Using fileStream As New StreamReader(file.InputStream)
fileBytes = Encoding.UTF8.GetBytes(fileStream.ReadToEnd())
fileStream.Close()
Using
Your image is read as text. From this text you get the bytes UTF8 byte values, thats why your image is nearly twice the size when uplaoded. You need the bytes from the image, without converting them to something else.
fileBytes = File.ReadAllBytes(file.FileName)

download server generated file (.vcf)

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.

how to call page which creates pdf

I have a page called OrderExport.aspx which creates a pdf file in a folder on the server.
In my page OrderSend.aspx I have a function which sends an e-mail with the pdf file attached, like this:
Dim atc As Net.Mail.Attachment = New Attachment(stm, fileNameOrder)
mail.Attachments.Add(atc)
How can I call OrderExport.aspx from this function before sending the e-mail without showing it for the user?
I need to make sure that the pdf file exists when sending the e-mail.
You probably want to create a class rather than an aspx page to handle the PDF creation. One way to do this is to use the PDFSharp library which can handle creating the PDF document and then saving it to a stream which can be attach to an email message. You could also save the stream to a file on the server at the same time.
' Create a new PDF document
Dim document As PdfDocument = New PdfDocument
document.Info.Title = "TITLE"
Dim securitySettings As Security.PdfSecuritySettings = document.SecuritySettings
' Restrict some rights.
securitySettings.PermitAccessibilityExtractContent = False
securitySettings.PermitAnnotations = False
securitySettings.PermitAssembleDocument = False
securitySettings.PermitExtractContent = False
securitySettings.PermitFullQualityPrint = True
securitySettings.PermitModifyDocument = False
securitySettings.PermitPrint = True
' Create an empty page
Dim page As PdfPage = document.AddPage
' Get an XGraphics object for drawing
Dim gfx As XGraphics = XGraphics.FromPdfPage(page)
Dim img As XImage = XImage.FromGdiPlusImage(imgPermit)
gfx.DrawImage(img, 20, 20)
Dim stream As New MemoryStream()
document.Save(stream, False)
Dim xStream As System.IO.Stream = stream
SendEmail(sEmail, xStream)
in your mail message it would be constructed similar to this
Dim xStream As New Attachment(pdf, "FILE" + Date.Now.ToString("MM.dd.yyyy") + ".pdf")
mm.Attachments.Add(xStream)
You need to separate the GUI from the actual logic.
You presumably have code in OrderExport.aspx that creates the PDF and saves it to disk. You should put that code into a separate class, so that you can call it from OrderExport.aspx, and from the page with the "Send e-mail" button. Similarly, extract the code for sending the email from OrderSend.aspx and call it from there, and from the "Send e-mail" button.
Separation of Concerns is the best solution, but if you need a quick solution -not a good one- make the class and the function public and call it like any other class

Need to read a URL and save the response as a file on the same server in ASP.NET

I need to have a page on my website that when opened will read another URL address and capture the response from it (it's actually XML data responding) and then save that data on that same webservr to a file called DATA.XML
It needs to overwrite any file that is already there.
I only have ASP.NET available to me as a progrmming language.
Can someone show me how to do this in a ASP page.
so something like?
Get the response from the other URL in to strResult.
WebResponse objResponse;
WebRequest objRequest = HttpWebRequest.Create(strURL);
objResponse = objRequest.GetResponse();
using (StreamReader sr = new StreamReader(objResponse.GetResponseStream()))
{
strResult = sr.ReadToEnd();
sr.Close();
}
return strResult;
Use System.IO to save the strResult to a file (you can look that up as its pretty common code)

Resources