
我的解決方案如下:在客戶端使用JavaScript控制上傳單元的數量,點擊"Add upload”後觸發Uploading事件,程序在服務器端通過Files屬性(Request.Files)獲取用戶端所有上傳的文件的HttpPostedFile集合。該控件具備如下特性:
該部分主要借助JavaScript中的parentElement, createElement, insertAdjacentElement, appendChild, removeChild等屬性和方法實現,添加的一個文件項的代碼單元為:
<div>
<input type=”file” name=”file”/>
<input type=”button” value=”Delete” onclick=”removeItem()”/>
</div>
把元素放在div裡的目的是為了方便刪除操作(只需要remove掉div就可以),以下是幾個主要的JS函數介紹:
checkFileExist() 文件的重復性檢查,在每次input type="file"的值發生改變時觸發。檢測方式為遍歷容器中所有的input type="file",當檢測到重復文件存在時,會給出重復性提示,同時將相應的input type=file清空.這裡因為intput的value屬性是只讀,為了清除其內部內容,采用的方案為先移除該元素,然後在相同位置添加該元素

function checkFileExist()...{var filename=event.srcElement.value; var duplicate = 0;var elems = get_container().getElementsByTagName("input");for(var i=0;i<elems.length; i++)...{if(elems[i].type == "file" && elems[i].value == filename)...{duplicate++;if(duplicate>1)...{alert("不能重復選擇文件。"); var offset = event.srcElement.nextSibling; var parent = event.srcElement.parentElement;parent.removeChild(event.srcElement);offset.insertAdjacentElement("beforeBegin", get_fileUpload()); return; }} }}
該控件提供如下公共屬性和事件:
控件主要重寫了以下方法:
為了方便維護和使用,JScript代碼被封裝到Resource文件中。
ASPX頁面

<%...@ Register Namespace="MultiFileUpload" Assembly="MultiFileUpload" TagPrefix="ASP" %>
…
<ASP:MultiFileUpload ID="mfu1" runat="server" OnUploading="mfu1_Uploading" MaxFilesCount="3" />
…
.CS文件
protected void mfu1_Uploading(object sender, EventArgs e)
...{
for(int i=0;i<mfu1.Files.Count;i++)
...{
Label1.Text += Request.Files[i].FileName + "<br/>";
}
}
MultiFileUpload.CS
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace MultiFileUpload
...{
[DefaultProperty("Text")]
[ToolboxData("<{0}:MultiFileUpload runat=server></{0}:MultiFileUpload>")]
public class MultiFileUpload : WebControl, INamingContainer
...{
private Button m_btnUpload = null;
protected override void OnInit(EventArgs e)
...{
m_btnUpload = new Button();
m_btnUpload.Text = "Upload";
m_btnUpload.Click += new EventHandler(m_btnUpload_Click);
base.OnInit(e);
}

/**//// <summary>
/// Occures when user clicks the upload button to upload the files.
/// </summary>
public event EventHandler Uploading;
protected override void CreateChildControls()
...{
Controls.Add(m_btnUpload);
Page.Form.Enctype = "multipart/form-data";
base.CreateChildControls();
}
void m_btnUpload_Click(object sender, EventArgs e)
...{
if (null != Uploading)
...{
Uploading(this, e);
}
}

"Properties"#region "PropertIEs"

/**//// <summary>
/// Gets the file collection uploaded by the clIEnt.
/// </summary>
public HttpFileCollection Files
...{
get
...{
return (HttpContext.Current.Request.Files == null) ? null : HttpContext.Current.Request.Files;
}
}

/**//// <summary>
/// Gets or sets the max number of files uploaded by the clIEnt at one time.
/// </summary>
[Category("Appearance")]
public int MaxFilesCount
...{
get
...{
return (null == ViewState["MaxFiles"]) ? 5 : (int)VIEwState["MaxFiles"];
}
set
...{
VIEwState["MaxFiles"] = value;
}
}
#endregion

"Render UI elements"#region "Render UI elements"
protected override void RenderContents(HtmlTextWriter output)
...{
output.WriteBeginTag("div");
output.WriteAttribute("id", "fuPanel");
output.Write(''>'');
output.RenderBeginTag(HtmlTextWriterTag.Div);
WriteInputFile(output);
WriteDeleteButton(output);
output.RenderEndTag();
output.WriteEndTag("div");
output.WriteBeginTag("script");
output.WriteAttribute("type", "text/Javascript");
output.Write(''>'');
output.Write(GetJavaScripts());
output.WriteEndTag("script");
// Render the post back button.
if (null != m_btnUpload)
...{
m_btnUpload.RenderControl(output);
}
}
private void WriteInputFile(HtmlTextWriter output)
...{
output.WriteBeginTag("input");
output.WriteAttribute("type", "file");
output.WriteAttribute("name", "file");
output.WriteAttribute("onchange", "checkFileExist()");
output.Write(''>'');
output.WriteEndTag("input");
}
private void WriteDeleteButton(HtmlTextWriter output)
...{
output.WriteBeginTag("input");
output.WriteAttribute("type", "button");
output.WriteAttribute("id", "add");
output.WriteAttribute("value", "Add upload");
output.WriteAttribute("onclick", "addItem()");
output.Write(''>'');
output.WriteEndTag("input");
}
private string GetJavaScripts()
...{
// Replace the max count of files.
return Resource.JScript.Replace("{$MAX_FILES_COUNT}", MaxFilesCount.ToString());
}
#endregion
}
}
JScript.JS

var maxFiles = ...{$MAX_FILES_COUNT}; 
function get_container()...{return document.getElementById("fuPanel"); } 
function get_addButton()...{return document.getElementById("add");} 
function get_delButton()...{var delButton = document.createElement("<input>"); delButton.type = "button";delButton.name = "delete";delButton.value = "Delete";delButton.onclick = removeItem;return delButton;} 
function get_fileUpload()...{var fileUpload = document.createElement("<input>");fileUpload.type = "file";fileUpload.name = "file"; fileUpload.onchange=checkFileExist; return fileUpload;}
function addItem()...{if(get_container().getElementsByTagName("div").length>=maxFiles)...{alert("超出最大文件限制.");return;}var div = document.createElement("<div>");div.appendChild(get_fileUpload());div.appendChild(get_delButton());get_container().firstChild.insertAdjacentElement("beforeBegin", div);} 
function removeItem()...{get_container().removeChild(event.srcElement.parentElement);}
function checkFileExist()...{var filename=event.srcElement.value; var duplicate = 0;var elems = get_container().getElementsByTagName("input");for(var i=0;i<elems.length; i++)...{if(elems[i].type == "file" && elems[i].value == filename)...{duplicate++;if(duplicate>1)...{alert("不能重復選擇文件。"); var offset = event.srcElement.nextSibling; var parent = event.srcElement.parentElement;parent.removeChild(event.srcElement);offset.insertAdjacentElement("beforeBegin", get_fileUpload()); return; }} }}