How to make a fileupload interface in ASP.NET - asp.net

I am trying to make a file upload interface in ASP.NET webforms and am looking for some advice on how to proceed.
The file upload interface is part of a website I am making on which users can post adverts. The interface is part of the "create a new advert" and will enable the user to upload up to 6 images. I am using only the asp.net FileUpload server control as I am trying to make a control which will work when users have javascript disabled. That's the background.
The upload for all 6 files occurs on button click. This stores the files in a temp folder (/UserUploads/temp) until the user submits the form in which case the files are moved to the /UserUploads folder and the references in the database or until the user hits the cancel button or navigates away in which case the files are deleted.
First question is: Is storing the files in a temp directory the right way to go about this? Or is there some better way of keeping the temp files on the server until the parent form is submitted? The only alternative I can think about is saving the files to the session, but that seems like a recipe for killing the server...
Second question: I am unclear what to do when the user just closes the browser window. I want to avoid ending up with a mess of orphaned files in the temp directory. Is there some way to make sure that all the files will get cleared out if the user doesn't go through with the form submission? Or do I just have to perform a cleanup of the temp directory every so often?
Third question: Am I doing this completely wrong and there is in fact a much better approach to uploading multiple files?

1) If you are using SQL Server, I personally prefer to store uploaded files in a varbinary(max) field and work with them by their unique ID. Then you don't have to worry about name collisions or de-sync of your DB to your filesystem. This also allows your upload process to be independent of the insertion of the parent form.
The examples below show how to grab the file stream (and metadata) from a FileUpload control in a FormView and supply it as a parameter to a SQL stored procedure. Then, a class implementing IHTTPHandler is used to retrieve files from the DB.
2) As far as clearing out temp files, I would associate each uploaded file with a temp master record so they are tied together. When the real master is confirmed, delete the temp master (and reference files from the real master). Then run a SQL Agent job on a regular interval to delete temp masters and associated files that are older than X amount of time.
Saving:
Protected Sub DetailsView1_ItemInserting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DetailsViewInsertEventArgs) Handles DetailsView1.ItemInserting
Dim objUploader As FileUpload = DetailsView1.FindControl("fuFile")
If objUploader.HasFile Then
Dim strFileName As String = objUploader.PostedFile.FileName
strFileName = strFileName.Substring(strFileName.LastIndexOf("\") + 1)
Dim objFileStream As System.IO.Stream = objUploader.PostedFile.InputStream
Dim arrFileImageByteArray(objFileStream.Length) As Byte
objFileStream.Read(arrFileImageByteArray, 0, objFileStream.Length)
e.Values.Insert(0, "FileImage", arrFileImageByteArray)
e.Values.Insert(1, "FileName", strFileName)
e.Values.Insert(3, "PostingDate", Now)
e.Values.Insert(5, "Application", "CorpForms")
Else
e.Cancel = True
objMessages.Add(New StatusMessage(MessageType.Warning, "File Upload canceled. No file was selected."))
End If
End Sub
Retrieving:
Public Class FileServiceHandler : Implements IHttpHandler
Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
Dim idFileID As Guid
If context.Request.QueryString("FileID") IsNot Nothing Then
Dim strFileID As String = context.Request.QueryString("FileID")
Try
idFileID = Guid.Parse(strFileID)
Catch ex As Exception
Throw New Exception("Unable to parse File ID")
End Try
End If
Dim objConnection As New SqlConnection(ConfigurationManager.ConnectionStrings("PublicWebConnectionString").ConnectionString)
Dim objCommand As SqlCommand = objConnection.CreateCommand
Dim objReader As SqlDataReader
objCommand.CommandType = Data.CommandType.StoredProcedure
objCommand.CommandText = "spGetUploadedFile"
objCommand.Parameters.AddWithValue("FileID", idFileID.ToString)
Dim arrFileImage() As Byte = Nothing
Dim strFileName As String = String.Empty
Try
objConnection.Open()
objReader = objCommand.ExecuteReader
While objReader.Read
If Not IsDBNull(objReader("FileImage")) Then
arrFileImage = objReader("FileImage")
End If
If Not IsDBNull(objReader("FileName")) Then
strFileName = objReader("FileName")
End If
End While
Catch ex As Exception
Throw New Exception("There was a problem retreiving the file: " & ex.Message)
End Try
If objConnection.State <> Data.ConnectionState.Closed Then
objConnection.Close()
End If
If arrFileImage IsNot Nothing Then
context.Response.Clear()
context.Response.AddHeader("content-disposition", "attachment;filename=" & strFileName)
context.Response.BinaryWrite(arrFileImage)
context.Response.End()
Else
context.Response.ContentType = "text/plain"
context.Response.Write("Unable to retrieve file ID# " & idFileID.ToString)
End If
End Sub
Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return True
End Get
End Property
End Class
Web.Config in file retrieval path:
<configuration>
<system.web>
<httpHandlers>
<add verb="GET" path="*" type="MyNamespace.FileServiceHandler" />
</httpHandlers>
</system.web>
<system.webServer>
<handlers>
<add name="MyNamespace.FileServiceHandler" path="*" verb="*" type="MyNamespace.FileServiceHandler" resourceType="Unspecified" preCondition="integratedMode" />
</handlers>
</system.webServer>
</configuration>

File upload is always annoying. I recently found a great component that does what I belive all upload componentes should do.
See at:
http://aquantum-demo.appspot.com/file-upload
And a sample in C# at:
https://github.com/BoSchatzberg/jfu-CSharp-Example
And, you should store your files in a temporary folder before creating the database row. To avoid a mess of files left useless, you can use a windows temporary folder to delagate to the windows when delete or not those files.
System.IO.Path.GetTempPath()

I would recommend storing the files in the database, rather than a temporary folder.
Don't store them in Session Db - too much information, but include the SessionId in the Files Database record.
So you'd have a Files database with a table along the lines of
Id (int identity field)
Data (varbinary(max))
MimeType (varchar(50))
SessionId (varchar(50))
UserId ??
Then you'd simply need to write a scheduled SQL task to clear images where the session had expired.

Related

SessionID not retaining same value through a session

I have a strnge problem which I dont know how to resolve.
The sessionID keeps changing every time I open a page in the application.
But when I debug the program, the sessionID remains constant and does not change
The application is installed on Windows server 2008 R2 (iis 7.5 and dotnet framework 4.0)
<sessionState cookieless="UseCookies" mode="InProc" sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes" stateConnectionString="tcpip=127.0.0.1:42424" timeout="60" />
What is the matter?
Note: This works fine on my local development machine.
EDIT
This is the code in Global.asax.vb for Session_Start`
Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
Session("mySessions") = "junk"
AddMySessions()
If User.Identity.IsAuthenticated Then
Dim cmd As New SqlCommand
Dim sql As String
sql = String.Format("SELECT EmpID FROM Intranet_Employees WHERE Username='{0}'", sUserName)
cmd.CommandText = sql
' Production ...
Session("EmpID") = CType(Dao.ExecuteScalar(cmd, ConfigurationSettings.AppSettings("cSqlTemplateDB")), String)
Dim oEmpInfo As New TOrders.Data.Objects.Employee(Convert.ToInt64(Session("EmpID")))
Session("EmpInfo") = oEmpInfo
If Application("SessionCount") Is Nothing Then
Application.Lock()
Application("SessionCount") = 0
Application.UnLock()
End If
Application.Lock()
Application("SessionCount") += 1
Application.UnLock()
Else
Response.Redirect("http://intranet/tsystem")
End If
End Sub
Private Sub AddMySessions()
Dim sMsg As String = Session.SessionID & ";" & Now.ToString & ";" & Request.ServerVariables.Get("AUTH_USER").ToString
If IsNothing(Application("mySessions")) Then
Dim arrSessions As New ArrayList
arrSessions.Add(sMsg)
Application.UnLock()
Application("mySessions") = arrSessions
Application.Lock()
arrSessions = Nothing
Else
Dim arrTemp As ArrayList = CType(Application("mySessions"), ArrayList)
arrTemp.Add(sMsg)
Application.UnLock()
Application("mySessions") = arrTemp
Application.Lock()
arrTemp = Nothing
End If
sMsg = Nothing
End Sub
`
From MSDN:
When using cookie-based session state, ASP.NET does not allocate
storage for session data until the Session object is used. As a
result, a new session ID is generated for each page request until the
session object is accessed. If your application requires a static
session ID for the entire session, you can either implement the
Session_Start method in the application's Global.asax file and store
data in the Session object to fix the session ID, or you can use code
in another part of your application to explicitly store data in the
Session object.
If your application uses cookieless session state, the session ID is
generated on the first page view and is maintained for the entire
session.
After more than week of frustration, I finally figured it out. The issue was not with the code or web.config - but with the naming of the Server. The server name contained the underscore '_' which was blocking cookies. The server name was changed and everything worked fine.
#fnostro - Thanks for your patience and suggestions.
Here is the link from microsoft.
http://support.microsoft.com/default.aspx?scid=kb;EN-US;q316112

Can't share isolated storage file between applications in different app pools

I've got various web apps (containing WCF services) in IIS under the default website. As long as they are all running in the same app pool they can access a shared isolated storage file no problem.
However, once I move them to different app pools I get "System.IO.IsolatedStorage.IsolatedStorageException: Unable to create mutex" when one tries to access a file created by another. They are all running under NetworkService user. I tried GetUserStoreForAssembly and GetMachineStoreForAssembly all with the same result. Any ideas why they couldn't use a shared file?
I made sure to close the stream and even dispose it in case one was holding onto it, but I am running a simple test where one service writes it, then another tries to read from it later, and it always fails.
Also, I am accessing the isolated store from a signed assembly.
Does anybody have any ideas?
Here is the code:
Private Sub LoadData()
Dim filename = FullFilePath(_fileName)
Dim isoStorage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForAssembly()
' Tried GetMachineStoreForAssembly, same failure
isoStorage.CreateDirectory(ROOT_DIRECTORY)
If (isoStorage.GetFileNames(filename).Length = 0) Then
Return
End If
Dim stream As Stream = New IsolatedStorageFileStream(filename, FileMode.OpenOrCreate, isoStorage)
If stream IsNot Nothing Then
Try
Dim formatter As IFormatter = New BinaryFormatter()
Dim appData As Hashtable = DirectCast(formatter.Deserialize(stream), Hashtable)
Dim enumerator As IDictionaryEnumerator = appData.GetEnumerator()
While enumerator.MoveNext()
Me(enumerator.Key) = enumerator.Value
End While
Finally
stream.Close()
stream.Dispose()
stream = Nothing
End Try
End If
End Sub
Public Sub Save()
Dim filename = FullFilePath(_fileName)
' Open the stream from the IsolatedStorage.
Dim isoFile As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForAssembly()
' Tried GetMachineStoreForAssembly, same failure
Dim stream As Stream = New IsolatedStorageFileStream(filename, FileMode.Create, isoFile)
If stream IsNot Nothing Then
Try
Dim formatter As IFormatter = New BinaryFormatter()
formatter.Serialize(stream, DirectCast(Me, Hashtable))
Finally
stream.Close()
stream.Dispose()
stream = Nothing
End Try
End If
End Sub
Looks like it was a trust issue.
After adding the assembly accessing the isolated storage file to the gac it magically worked as everything in the gac has full trust set automatically.
This works for me, but it might not always be an option to do this for other solutions. Check out the .NET Framework caspol utility if this is the case.
Hope this helps somebody! It was a huge pitafor me.

displaying htm contents in browser via Response.Writefile in ASP.Net

I'm working on a site that I've inherited that's built with ASP.Net, which I'm only slightly familiar with. One of the pages allows for a link to a document (word or pdf) that, when clicked on, prompts the user to save or open the file, but does not reveal the true path of the file. This way it prevents users from pasting a url to a file - the link is to an aspx file that checks for a valid session, and then retrieves the file.
Anyway, because there's a lot of legacy code, I need to do this with a bunch of static htm files as well, however these files need to be displayed rather than prompting the user to save them, which is what happens now. I tried changing the content type to application/text, application/html, text/html, etc. and that didn't work, then tried adding a response header of content-disposition inline. When I do that, build, and try linking to the file, I get a couple of runtime exceptions:
[FormatException: Input string was not in a correct format.]
Microsoft.VisualBasic.CompilerServices.Conversions.ParseDecimal(String Value, NumberFormatInfo NumberFormat) +206
Microsoft.VisualBasic.CompilerServices.Conversions.ToLong(String Value) +110
[InvalidCastException: Conversion from string "inline; filename=\" + myFile + " to type 'Long' is not valid.]
Microsoft.VisualBasic.CompilerServices.Conversions.ToLong(String Value) +428
cmswebasp.CMSModdoclinks.DownloadFile(String file) +1704
cmswebasp.CMSModdoclinks.Page_Load(Object sender, EventArgs e) +625
System.Web.UI.Control.OnLoad(EventArgs e) +99
System.Web.UI.Control.LoadRecursive() +50
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +627
Here's a snippet of code from the page:
Response.AddHeader("Content-Disposition", "inline; filename=" & file)
Dim fi As New FileInfo(myFile)
Response.AddHeader("Content-Length", fi.Length)
Dim contentType As String = ""
Dim fileExt As String = file.Split(".")(1)
Select Case fileExt
Case "htm"
contentType = "application/text"
Case Else
contentType = "application/octet-stream"
End Select
Response.ContentType = contentType
Response.WriteFile(myFile)
Do I have to do something with an htmlwriter object or something? Can't I just have it open a new browser window with the file displaying or does it have to prompt the user if used in this way??
Scrap the full page (.aspx) approach in place of using a generic handler (.ashx). An .aspx page is going to do a lot of built-in loading to initialize all the state that would normally be used in an ASP.NET web page, while the generic handler only initializes the bare minimum to send output back out to the client.
You will need to also implement System.Web.SessionState.IRequiresSessionState to get your session state when using a generic handler, as it does not load the session state by default.
An example:
Public Class FileWrapper
Implements System.Web.IHttpHandler, System.Web.SessionState.IRequiresSessionState
Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
' Validate session...
' Set output content type based on extension of requested file
Dim fileName As String = context.Request.QueryString("file")
Dim fileExt As String = fileName.Split("."c)(1).ToLowerInvariant()
Select Case fileExt
Case "htm", "html"
context.Response.ContentType = System.Net.Mime.MediaTypeNames.Text.Html
Case Else
context.Response.AddHeader("Content-Disposition", "attachment; filename=" & fileName)
context.Response.ContentType = System.Net.Mime.MediaTypeNames.Application.Octet
End Select
context.Response.WriteFile(fileName)
End Sub
ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
Note: If hosting on IIS 7, you will need to remove any <httpHandler> registrations from <system.web> and register them instead as <handler> registrations in <system.webServer>.
Such as (correct as necessary to use your namespace):
<system.webServer>
<!-- For IIS 7 -->
<handlers>
<add name="FileWrapperHandler" path="FileWrapper.ashx" verb="*" type="MyNamespace.FileWrapper"/>
</handlers>
</system.webServer>
Another Note: If you are developing for a IIS 7 host using Integrated pipeline (default for IIS 7), then I would suggest using (and installing if necessary) the IIS Express option for your web project. This would be found going into Properties for your web project, the Web tab from left, then in the Servers section, select Use Local IIS Web Server radio button, and check Use IIS Express below.
This will put your development environment more in sync with how your production environment will behave.
After some discussion, it appears that you may be best served using a lobotomized Page after all, instead of a generic handler, due to the possibility of users coming to the site via links within Word documents or other sources outside the browser.
We've found that the aspx page is able to recover session in this instance, whereas the ashx does not. Thus, I am providing code for an aspx solution:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
' Validate session...
' Set output content type based on extension of requested file
Dim fileName As String = Request.QueryString("file")
Dim fileExt As String = fileName.Split("."c)(1).ToLowerInvariant()
Select Case fileExt
Case "htm", "html"
Response.ContentType = System.Net.Mime.MediaTypeNames.Text.Html
Case Else
Response.ClearHeaders()
Response.AppendHeader("Content-Disposition", "attachment; filename=" & fileName)
Response.ContentType = System.Net.Mime.MediaTypeNames.Application.Octet
End Select
Response.WriteFile(fileName)
Response.Flush()
Response.Close()
End Sub
It doesn't matter whatever is placed in the aspx markup itself, since none of that will get rendered anyway.
Just a note that this may cause some log entries on the web host about trying to "access a closed stream" since we have closed the response stream, but the page will continue processing as usual. That's one of the prices to pay for essentially hijacking the normal page flow.
The server might be throwing out your manually-set content-type header when you call Response.WriteFile(). Find out what the actual content-type header is when the client receives it. Firebug will tell you if you look under its Net tab.
If this is the case, try setting Response.ContentType after calling Response.WriteFile(). Alternatively, you could try reading the file into a string and use Response.Write() instead of WriteFile.

FileUpload within a Wizard Control, Processed at the End

This is related to my previous question. An unforeseen issue arose with the Wizard control.
I now know how to upload to FTP, however when using the FileUpload control inside a Wizard control, when you move to the next step, the File you selected gets cleared because of the postback. I need to be able to rename the file according to the results from the Wizard before uploading. So...
I finish my wizard
It uploads some stuff to a database
Renames the file according to those results
Uploads the renamed file to the FTP server
I suspect I will need to follow a procedure something like this, having an upload button next to FileUpload
On "Upload" button click stream the file to the Web Server.
Complete the Wizard.
If the wizard completes successfully, rename file and stream to FTP server.
If the wizard fails, what? Delete the file from the web server? How?
I think I understand the process, so I would like help on how to split my FTP Upload function into two parts with the proper error handling for when the wizard fails.
It would be a great help if you please use the following code as a base. Thanks as always :)
Protected Sub UploadFile(ByVal NewFilename As String)
Dim myFtpWebRequest As FtpWebRequest
Dim myFtpWebResponse As FtpWebResponse
'Function one? - Problem, "NewFilename" depends on the output of the Wizard,
' but obviously it has not been called yet.
myFtpWebRequest = CType(WebRequest.Create(ftpServer + ftpPath + NewFilename), FtpWebRequest)
myFtpWebRequest.Method = WebRequestMethods.Ftp.UploadFile
myFtpWebRequest.UseBinary = True
Dim myFileStream As Stream = FileUpload1.FileContent
myFtpWebRequest.ContentLength = myFileStream.Length
'Function two?
Dim requestStream As Stream = myFtpWebRequest.GetRequestStream()
myFileStream.CopyTo(requestStream)
requestStream.Close()
myFtpWebResponse = CType(myFtpWebRequest.GetResponse(), FtpWebResponse)
myFtpWebResponse.Close()
End Sub
-- ANSWER ---
Here's my final implementation based on input from Icarus :)
For brevity I have excluded the error catching.
'This function is what kicks things off...
Protected Sub UploadFileToWebServer() Handles btnUploadFile.Click
Dim TempDir As String = "C:\TEMP", FileName As String = "uploadedfile.tmp", FilePath As String
If Not Directory.Exists(TempDir) Then
Directory.CreateDirectory(TempDir).Attributes = FileAttributes.Directory
End If
FilePath = TempDir + "\" + FileName
Session.Add("FileName", File1.FileName) 'Keep track of uploaded file name
File1.SaveAs(FilePath)
Session.Add("File", FilePath)
End Sub
After the file is uploaded to the web server, we can continue through the wizard, and when the "Finish" button is clicked, the wizard data gets submitted to the database. The filename is based on the inserted record ID. The following function gets called by the "Final" button click after the record is inserted, and the file finally gets uploaded to the FTP server with the filename changed accordingly.
Protected Sub UploadFileToFtpServer(ByVal FileLinkStr As String)
Dim myFtpWebRequest As FtpWebRequest
Dim myFtpWebResponse As FtpWebResponse
'Defines the filename, path, and upload method, and connection credentials
myFtpWebRequest = CType(WebRequest.Create(ftpServer + ftpPath + FileLinkStr), FtpWebRequest)
'Be sure to authenticate prior to uploading or nothing will upload and no error
myFtpWebRequest.Credentials = New NetworkCredential(ftpUsername, ftpPassword)
myFtpWebRequest.Method = WebRequestMethods.Ftp.UploadFile
myFtpWebRequest.UseBinary = True
'Streams the file to the FTP server
'Retrieves File temporarily uploaded to the Web Server during Wizard Processing
Dim iStream As New FileInfo(Session.Item("File"))
Dim myFileStream As Stream = iStream.OpenRead
myFtpWebRequest.ContentLength = myFileStream.Length
Dim requestStream As Stream = myFtpWebRequest.GetRequestStream()
myFileStream.CopyTo(requestStream)
requestStream.Close()
myFtpWebResponse = CType(myFtpWebRequest.GetResponse(), FtpWebResponse)
myFtpWebResponse.Close()
End Sub
Your understanding is correct. Once you upload the file to the web server (you'd need to place it in a temp directory somewhere and keep track of the file name you gave it) and the wizard completes successfully, you grab that file, rename it accordingly and upload it to the ftp server. If fails, simply call:
File.Delete(Path_to_file_uploaded_on_temp_directory);
You can keep track of the file name given originally, by storing it in Session, for example. When you upload the file to the server initially, do something like Session["FileName"]=Path_to_temp_directory+fileName;
On the final step of the Wizard, get the file name from Session and either rename it and upload it to the FTP Server or delete it.
Of course you need to account for possible name conflicts, etc. You can use a Guid to generate a random name for the file, for example.
I hope I explained this clearly.
EDIT
To make sure I understand correctly...
You need your user to go through all the steps of a Wizard kind of thing
During the process, you ask your user to upload a file.
Because the user has to select a file before the last step of the wizard, you are forced to upload the file immediately the user clicks on the "Next" button to go to the next step of the wizard.
At the very last step of the Wizard, you need to determine whether the file the user has selected should be uploaded to an ftp server (presumably, another box different from your web server) or should be discarded completely.
If the file needs to be uploaded to the FTP server, it needs to be renamed with a special name.
Based on the above, my suggestion is:
When the user clicks "Next" on the step where he selects the file from his computer, you need to save the file immediately to a temporary location on your web server. You save the file to this temporary folder on your web server by doing something like:
if(FileUpload1.HasFile) //user selected a file
{
try
{
//D:\temp is a temp directory on the Web Server
FileUpload1.PostedFile.SaveAs(#"D:\temp\"+FileUpload1.FileName);
//Store the FULL PATH TO the file just uploaded on Session
Session["FileName"]="D:\temp\"+FileUpload1.FileName;
}
catch (Exception ex)
{
//Handle it.
}
}
On the last step of the wizard, assuming everything was successful, do this
Dim myFtpWebRequest As FtpWebRequest
Dim myFtpWebResponse As FtpWebResponse
' You know the NewFileName because it's the output of the wizard
myFtpWebRequest = CType(WebRequest.Create(ftpServer + ftpPath + NewFilename), FtpWebRequest)
myFtpWebRequest.Method = WebRequestMethods.Ftp.UploadFile
myFtpWebRequest.UseBinary = True
'Here you need to read the Original File
Dim myFileStream As Stream = new FileStream(Session["FileName"]),FileMode.Open,FileAccess.Read,FileShare.ReadWrite)
myFtpWebRequest.ContentLength = myFileStream.Length
Dim requestStream As Stream = myFtpWebRequest.GetRequestStream()
myFileStream.CopyTo(requestStream)
requestStream.Close()
myFtpWebResponse = CType(myFtpWebRequest.GetResponse(), FtpWebResponse)
myFtpWebResponse.Close()
If you decide that you should delete the original file uploaded by the user because he did not complete the wizard successfully, you can simply do:
try
{
File.Delete (Session["FileName"]);
}
catch(Exception ex)
{
//Handle it.
}

restrict access to doc files asp.net

i have a folder in my asp.net app conatining doc files that can be accessed only (dowwload) by a certain type of connected users(admin account or permitted other accounts)
how can i do that?
any ideas?
thanks in advance.
The App_Data folder in .NET is protected, and therefore ideal for this very purpose. I normally put sensitive files in here then have a page "ViewDoc.aspx" that performs the security checks and then sends the file to the user (using Response.Write).
Put sensitive files outside of web site root, so they can not be accessed by URL.
After that, use this HttpHandler (written in VB.NET) to serve files:
Public NotInheritable Class FileHandler
Implements IHttpHandler
Public ReadOnly Property IsReusable() As Boolean Implements System.Web.IHttpHandler.IsReusable
Get
Return False
End Get
End Property
Public Sub ProcessRequest(ByVal context As System.Web.HttpContext) Implements System.Web.IHttpHandler.ProcessRequest
If Not String.IsNullOrEmpty(context.Request.QueryString("FileName")) Then
Dim fileName As String = context.Request.QueryString("FileName")
Try
Dim filesPath As String = "D:\TheFiles\"
Dim fileInfo As New IO.FileInfo(filesPath & fileName)
If fileInfo.Exists Then
Dim fileExt As String = fileInfo.Extension.Remove(0, 1).ToUpperInvariant
If fileExt = "JPG" Then
context.Response.ContentType = "image/jpeg"
Else
context.Response.ContentType = "image/" & fileExt
End If
context.Response.TransmitFile(fileInfo.FullName)
End If
Catch ex As Exception
End Try
End If
End Sub
End Class
and register this handler in your web.config like this:
<httpHandlers>
<add verb="*" path="secfile.axd" type="MyApp.FileHandler, MyApp" validate="false"/>
</httpHandlers>
use like this:
<a href="secfile.axd?pic=sample.jpg" />
Remember adding your file types to handler and change response.contenttype by type of your file.
Using a handler is not the only way, you can use context.Response.TransmitFile(fileInfo.FullName) in your aspx file.
A simple way to do this is to NOT put these documents inside a folder of your ASP.NET app and instead, put it somewhere else in the file system that can't be accessed directly from the browser. Then programmatically, you can serve the file to the user if s/he's authorized to do so.

Resources