Get blob from SQL and write to browser for download - asp.net

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 :)

Related

Itextsharp open new tab ISSUE

I need to open pdf in the new tab, it works and file show perfect, but if I open File with notepadd++, after EOF there are some NULL char (see pics).
It happen only I open it in new tab and use memorystream, the string after EOF create some problem the parser of client, whats wrong?.
This is code:
Dim mswithPage As New MemoryStream()
Dim SessValue As String = Request.QueryString("s")
Dim NOrder As String = Request.QueryString("odv")
mswithPage = CType(Session(SessValue), MemoryStream)
Response.Clear()
Response.ContentType = "Application/pdf"
Response.AddHeader("content-disposition", "inline;filename=" & NOrder & ".pdf")
Response.OutputStream.Write(mswithPage.GetBuffer(), 0, mswithPage.GetBuffer().Length)
Response.OutputStream.Flush()
Response.OutputStream.Close()
Response.End()
The issue
An issue is in this line:
Response.OutputStream.Write(mswithPage.GetBuffer(), 0, mswithPage.GetBuffer().Length)
Even more precisely its final argument mswithPage.GetBuffer().Length - you should use the number of actually used bytes in the buffer but you use the size of the complete buffer.
A solution
Thus, use mswithPage.Length instead:
Response.OutputStream.Write(mswithPage.GetBuffer(), 0, mswithPage.Length)
... and if the MemoryStream already is closed
If the MemoryStream already is closed, the solution above doesn't work anymore because its Length property can only be used on open streams.
What does work on closed streams, though, is the ToArray method! Thus, you can instead use
Response.OutputStream.Write(mswithPage.ToArray())
(Actually it is funny that ToArray works on closed streams but Length does not. After all, ToArray essentially returns a copy of the first Length many bytes of the internal buffer...)

Convert excel file to byte array

I'm using ASP.net with VB codebehind.
I need to be able to convert an excel file created in code into a byte array in order to send it to the client. Without saving the file to the server.
this is how i created the excel document
Dim APP As Excel.Application = New Excel.Application
Dim missing As Object = System.Reflection.Missing.Value
Dim Workbook As Excel.Workbook = (APP.Workbooks.Add(missing))
Dim worksheet As Excel.Worksheet = Workbook.ActiveSheet
'..Fill cells in worksheet..
This is how i transmit it:
Response.Clear()
Response.ClearContent()
Response.ClearHeaders()
Response.Cookies.Clear()
Response.Cache.SetCacheability(HttpCacheability.Private)
Response.CacheControl = "private"
Response.Charset = System.Text.UTF8Encoding.UTF8.WebName
Response.ContentEncoding = System.Text.UTF8Encoding.UTF8
Response.AppendHeader("Content-Length", filebytes.Length.ToString())
Response.AppendHeader("Pragma", "cache")
Response.AppendHeader("Expires", 60)
Response.AppendHeader("Conetnt-Disposition", "attachement; filename='MostIssuesByModelReport.xlsx'; size=" & filebytes.Length.ToString() & "; creation-date=" & Date.Now.ToString("R") & "; modification-date=" & Date.Now.ToString("R") & "; read-date=" & Date.Now.ToString("R"))
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
'write to client
Response.BinaryWrite(filebytes)
Response.End()
I just need to get the content of Workbook into filebytes
EDIT:
After looking around a bit more I can now make it download a file, however this file does not use the filename specified in the above code, it uses the name of the asp server page that the user is on, it also has a .aspx extension..however if I force it to open in excel it is correct. Besides popping up saying the source may be corrupted. Why is this happening and how can I fix it?

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.

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.

Export to Excel file as .zip to reduce file size. How to compress xmldata before sending it to the client?

I like to compress the xmldata before the data is provided to the client as an excel file. I am trying to compress and deliver it as a .zip file. its not working Here's my code below. Please help
I tried compressing it, converting it to bytes etc etc. The issue with below code is, the XSL transformation is not happening properly and the output excel file is raw xml with some .net exception at the end. (that's what I see on the .xls file that's downloaded at the end)
Before I started working on compression my below code was working fine that gives properly formatted excel file from the xml input. the excel file is so nice you can't even tell it was from XML.
pLEASE HELP with compression
Dim attachment As String = "attachment; filename=DataDownload.xls"
Response.ClearContent()
Response.AddHeader("content-disposition", attachment)
Response.ContentType = "application/vnd.ms-excel"
'Response.ContentType = "text/csv"
Response.Charset = ""
Dim ds As New DataSet()
Dim objXMLReader As XmlReader
objXMLReader = Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteXmlReader(ConnectionString, CommandType.StoredProcedure, "SPName")
ds.ReadXml(objXMLReader)
Dim xdd As New XmlDataDocument(ds)
Dim xt As New XslCompiledTransform()
'xt.Transform(xdd, Nothing, Response.OutputStream)
Dim bytearr() As Byte
bytearr = System.Text.Encoding.Default.GetBytes(xdd.OuterXml)
Dim objMemStream As New MemoryStream()
objMemStream.Write(bytearr, 0, bytearr.Length)
objMemStream.WriteTo(Response.OutputStream)
xt.Transform(xdd, Nothing, Response.OutputStream)
Response.End()
Why you don't use XLSX-Format instead of XLS? It is already XML-Data compressed in a ZIP file. You can rename a XLSX file to ZIP to verify this.
Updated: By the way, ContentType in the case should be "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" instead of "application/vnd.ms-excel".

Resources