本文转自:http://www.ironspeed.com/articles/Maintain%20File%20Upload%20Control/Article.aspx
Introduction
|
For security reasons, the File Upload control does not save the posted file name in its View State, so the file is lost on postback. This is not a bug; it was designed this way so the file cannot be hijacked. Even Iron Speed’s technical support application has this behavior (compare Fig. 1 and Fig. 2). For most programmers, the issue is not a big problem. People with less programming background, however, may become annoyed if their input data gets lost again and again. During the development of one of my projects, I received so many complaints about the issue that I had to find a solution.
Figure 1. File Upload control with a posted file.
Figure 2. The posted file is lost on postback after "Add Attachment" is clicked.
|
Solution
|
I borrowed my idea for a solution from Google Mail (Gmail). Gmail allows an email to have multiple files attached. When you attach more than one file, the previously posted files are shown as text links (Fig. 3). This can also be accomplished in Iron Speed Designer.
Although the File Upload control cannot save the posted file in its View State, the file can be saved in a session variable for later use. Its name can be displayed in a Literal control. Here is the design.
- Hide a Literal control behind the File Upload control.
- On postback, if the File Upload control contains a file, set the file name to the Literal control. Show the Literal control, and hide the File Upload control.
- Save the posted file to a session variable, since it will be needed when the record is saved.
Figure 3. Gmail's work-around for multiple attachments.
|
Implementation
|
Step 1: Add a Literal control to TableControlRow Drag and drop a Literal control (non data-bound) alongside the File Upload control (Fig. 4). Name it "FilePath". Note the hidden control "ATCH_FILENM" stores the file name to the database, and the FileDeleteButton. The FileDeleteButton removes the posted file and cancels its upload.
Figure 4. Add a Literal control alongside the File Upload control.
Step 2: Add a PreRender event handler to switch the visibilities of the controls In the MyTableControlRow class (in file: ...<App Name>App_CodeMyFolderMyPage.Controls.cs or .vb), insert the following code.
C#:
public MyTableControlRow(){ PreRender += new EventHandler(MyTableControlRow_PreRender); }
private void MyTableControlRow_PreRender(object sender, EventArgs e){ if(ATCH_FILE.PostedFile != null && ATCH_FILE.PostedFile.FileName.Length > 0){ // Save PostedFile to a session variable Page.Session[FilePath.ClientID] = ATCH_FILE.PostedFile;
// Also set filename to ATCH_FILENM string fullPath = ATCH_FILE.PostedFile.FileName; int LastIndex = fullPath.LastIndexOf("\"); ATCH_FILENM.Text = fullPath.Substring((LastIndex + 1)); }
// Show Literal if PostedFile is in the session variable. if(Page.Session[FilePath.ClientID] != null) { FilePath.Text = ((HttpPostedFile)Page.Session[FilePath.ClientID]).FileName; FilePath.Visible = true; ATCH_FILE.Visible = false; }else{ // Otherwise, show the FileUpload FilePath.Visible = false; ATCH_FILE.Visible = true; } }
|
Visual Basic .NET:
Public Sub New() AddHandler PreRender, AddressOf MyTableControlRow_PreRender End Sub
Private Sub MyTableControlRow_PreRender(ByVal sender As Object, _ ByVal e As EventArgs) If Not ATCH_FILE.PostedFile Is Not Nothing AndAlso _ ATCH_FILE.PostedFile.FileName.Length > 0 Then ‘ Save PostedFile to a session variable Page.Session(FilePath.ClientID) = ATCH_FILE.PostedFile
‘ Also set filename to ATCH_FILENM Dim fullPath As String = ATCH_FILE.PostedFile.FileName Dim LastIndex As Integer = fullPath.LastIndexOf("\") ATCH_FILENM.Text = fullPath.Substring((LastIndex + 1)) End If
‘ Show Literal if PostedFile is in the session variable. If Not Page.Session(FilePath.ClientID) Is Not Nothing then FilePath.Text = _ (DirectCast(Page.Session(FilePath.ClientID), HttpPostedFile)).FileName FilePath.Visible = True ATCH_FILE.Visible = False Else ‘ Otherwise, show the FileUpload FilePath.Visible = False ATCH_FILE.Visible = True End If End Sub
|
Step 3: Override FileDeleteButton_Click event handler While still in the MyTableControlRow class, add the following code to remove the posted file from the session if the user discards the file.
C#:
public override void FileDeleteButton_Click(object sender, ImageClickEventArgs args){ base.FileDeleteButton_Click(sender, args); this.Visible = false; this.Page.Session.Remove(FilePath.ClientID); } |
Visual Basic .NET:
Public Overloads Overrides Sub FileDeleteButton_Click(ByVal sender As Object, ByVal args As ImageClickEventArgs) MyBase.FileDeleteButton_Click(sender, args) Me.Visible = False Me.Page.Session.Remove(FilePath.ClientID) End Sub |
Step 4: Override GetUIData() for uploading the file Again in the MyTableControlRow class, insert the following code.
C#:
public override void GetUIData(){ HttpPostedFile file = null; if (ATCH_FILE.PostedFile != null && ATCH_FILE.PostedFile.FileName.Length > 0 && ATCH_FILE.PostedFile.ContentLength > 0) file = ATCH_FILE.PostedFile; else file = Page.Session[FilePath.ClientID] as HttpPostedFile;
if(file != null){ DataSource.Parse(MiscUtils.GetFileContent(file), MyTable.ATCH_FILE); DataSource.Parse(HttpUtility.HtmlDecode(ATCH_FILENM.Text), MyTable.ATCH_FILENM); } }
|
Visual Basic .NET:
Public Overloads Overrides Sub GetUIData() Dim file As HttpPostedFile = Nothing If Not ATCH_FILE.PostedFile Is Not Nothing AndAlso _ ATCH_FILE.PostedFile.FileName.Length > 0 AndAlso _ ATCH_FILE.PostedFile.ContentLength > 0 Then file = ATCH_FILE.PostedFile Else file = TryCast(Page.Session(FilePath.ClientID), HttpPostedFile) End If
If Not file is Not Nothing Then DataSource.Parse(MiscUtils.GetFileContent(file), MyTable.ATCH_FILE) DataSource.Parse(HttpUtility.HtmlDecode(ATCH_FILENM.Text), _ MyTable.ATCH_FILENM) End If End Sub
|
Step 5: Rebuild and run Here is what it looks like (Fig. 5).
Figure 5. Upload multiple files in Iron Speed Designer generated app.
|
Conclusion
|
Although the File Upload control cannot save the posted file in its View State, the file can be saved in a session variable for later use. Its name can be displayed in a Literal control. |
About the Author
|
Jing Ding has a PhD in Computer Engineering, Bioinformatics and Computational Biology, and an M.S. in Toxicology from Iowa State University. He received his B.S. in biophysics from Fundan University in Shanghai, China. He is a self-taught programmer who "played" with assembly, C and C++ in the 1990s. He took a break from programming from 1997 to 2000. When he picked it up again in 2001, he worked with Java. Jing began working with C# and .NET in 2006. |
|
|