How to make a fileupload interface in ASP.NET

2020-03-26 08:23发布

问题:

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:

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>


回答2:

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


回答3:

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.