以前写过一篇《单页面多类型的多附件上传》的文章,但是在实际项目中,这样的并不多见,相比之下,多附件上传却经常用到。
而每次使用都要复制粘贴相关的代码,虽然不麻烦,但用起来却不太方便,一旦忘记某段代码没复制过来,页面就会报错。
于是,就想把现在用的这些代码,变成一个用户自定义控件,这样再次使用的时候就方便多了。
话不多说,先看下界面吧。
设计时
运行后
先介绍下控件界面,上下一共有两个repeater,分别用于编辑和查看时使用。上面的repeater,带有删除按钮,用于编辑时可以删除不需要的附件;下面的repeater,用于查看时使用,如果没有上传附件,则后台代码会给控件Literal赋值空格符( ),用于解决浏览器兼容问题(因为有些浏览器在表格行没有数据时,边框线不显示)。
中间的上传控件,不用多说,用于上传附件;后面的“添加”按钮,点击后,会调用JS代码动态创建上传控件,以便能够上传多个附件;下面的隐藏控件,前期用于记录创建的上传控件个数,点击“提交”按钮后用于保存上传的附件路径,以便数据保存失败时删除附件。
下面我们来看下前台的html代码。
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="AttaUpload.ascx.cs" Inherits="WEB.webcontrols.AttaUpload" %> <script src="../js/AttaUpload.js" type="text/javascript"></script> <div id="tabEdit" runat="server"> <asp:Repeater ID="rpFileE" runat="server" OnItemCommand="rpFile_ItemCommand"> <ItemTemplate> <div> <asp:LinkButton ID="lbn_load" CommandArgument='<%#Eval("ID") %>' CommandName="load" runat="server" Style="line-height: 25px"><%# Eval("FileName").ToString() + Eval("FileType").ToString()%></asp:LinkButton>    <asp:LinkButton ID="lbtn_del" Style="color: Blue" CommandArgument='<%#Eval("ID") %>' CommandName="del" runat="server" OnClientClick='<%#"return DelMsg("「"+Eval("FileName").ToString() + Eval("FileType").ToString()+"」")" %>'>删除</asp:LinkButton> </div> </ItemTemplate> </asp:Repeater> <div id="atta" runat="server"> <asp:FileUpload ID="fufile" runat="server" onchange="if(this.value)JudgeFile(this.value,this);" /> <asp:ImageButton ID="imgbtn_add" runat="server" src="../images/add.gif" Style="margin-bottom: -2px;" /> </div> <asp:HiddenField ID="hffile" runat="server" Value="1" /> </div> <div id="tabView" runat="server" visible="false"> <asp:Repeater ID="rpFileV" runat="server" OnItemCommand="rpFile_ItemCommand"> <ItemTemplate> <div> <asp:LinkButton ID="lbn_load" CommandArgument='<%#Eval("ID") %>' CommandName="load" runat="server" Style="line-height: 25px"><%# Eval("FileName").ToString() + Eval("FileType").ToString()%></asp:LinkButton> </div> </ItemTemplate> </asp:Repeater> <asp:Literal ID="ltlfile" runat="server"></asp:Literal> </div>
其中引用的AttaUpload.js文件,就是该控件用到的所有JS代码所在,里面包含有动态创建上传控件,删除附件提示,删除动态创建的上传控件,判断附件类型等等相关功能的JS编码。
/**********动态添加上传附件**********/ function AddAtta(id, hfid) { //var hfid = jQuery("#" + id).next().attr('id'); //document.getElementById(id).nextSibling.id;(IE10不兼容) var d = document.createElement("div"); var f = document.createElement("input"); f.setAttribute("type", "file"); f.setAttribute("name", "upfile"); f.setAttribute("style", "margin-top:5px;"); f.onchange = function () { if (this.value) JudgeFile(this.value, this); } d.appendChild(f); //添加删除按钮 var im = document.createElement("img"); im.setAttribute("src", "../images/close.gif"); im.style.cssText = "margin-left:3px;margin-bottom: -2px;cursor:pointer"; im.onclick = function () { document.getElementById(hfid).value = $val(hfid) - 1; return DelFile(this, "DIV"); } d.appendChild(im); document.getElementById(id).appendChild(d); //添加计数 document.getElementById(hfid).value = parseInt($val(hfid)) + 1; return false; } /**********删除**********/ function DelFile(f, name) { while (f.tagName != name) f = f.parentNode; f.parentNode.removeChild(f); return false; } /**********判断文件类型和大小**********/ function JudgeFile(file, node) { var typelist = ["txt", "doc", "xls", "ppt", "docx", "xlsx", "pptx", "pdf", "jpeg", "jpg", "png", "bmp", "gif"]; if (file) { var match = 0; var suffix = file.split("."); var num = suffix.length - 1; var name = suffix[num].toLowerCase(); for (var i = 0; i < typelist.length; i++) { if (name == typelist[i]) { match = 1; break; } } if (match != 1) { alert("暂不支持上传该类型的文件,请重新选择!"); node.outerHTML = node.outerHTML; } } getFileSize(node); } //判断文件大小 function getFileSize(obj) { var size = 0; if (navigator.userAgent.indexOf("MSIE") > 0) { try { var fso = new ActiveXObject('Scripting.FileSystemObject'); //获取上传文件的对象 var file = fso.GetFile(obj.value); size = file.Size; } catch (err) { size = 0; } } else { size = obj.files[0].size; } if ((size / 1048576) > 40) { alert("上传文件大于40M,无法上传!"); obj.outerHTML = obj.outerHTML; } } /**********删除信息提示框**********/ function DelMsg(mess) { return confirm("系统提示:您确认删除" + mess + "吗?"); }
现在我们来看下控件的后台编码:
1、参数:
/// <summary> /// 项目ID /// </summary> public string ProID { get { if (ViewState["AttaProID"] == null) { return ""; } else { return (string)ViewState["AttaProID"]; } } set { ViewState["AttaProID"] = value; } } /// <summary> /// 控件ID /// </summary> public string WucID { get { if (ViewState["AttaWucID"] == null) { return "AttaUp"; } else { return (string)ViewState["AttaWucID"]; } } set { ViewState["AttaWucID"] = value; } } /// <summary> /// 查看或编辑(0:编辑;1:查看) /// </summary> public string Flag { get { if (ViewState["AttaFlag"] == null) { return "0"; } else { return (string)ViewState["AttaFlag"]; } } set { ViewState["AttaFlag"] = value; } }
2、页面加载:
/// <summary> /// 加载 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { imgbtn_add.Attributes.Add("onclick", "return AddAtta('" + WucID + "_atta','" + WucID + "_hffile')"); //页面附件绑定 if (ProID != "") { //读取现有数据 InitData(); } } }
关于在后台给“添加”按钮绑定JS方法,是为了要把控件的ID传到前台,否则“动态创建上传控件”和“保存累计个数”的方法就不能使用了。
而如果id="atta"的div不加上runat="server"的话,那任何页面就只能使用一次了。
3、绑定数据:
/// <summary> /// 读取数据 /// </summary> private void InitData() { PrjFileEntity model = new PrjFileEntity(); model.ProID = ProID; model.FileFlag = -2; DataView dv = PrjFileBLL.GetContact(model).DefaultView; if (Flag == "0") { // 编辑数据 rpFileE.DataSource = dv; rpFileE.DataBind(); } else { // 查看数据 rpFileV.DataSource = dv; rpFileV.DataBind(); tabEdit.Visible = false; tabView.Visible = true; if (dv == null || dv.Count <= 0) { ltlfile.Text = " "; } } }
4、保存附件:
/// <summary> /// 保存附件 /// </summary> /// <param name="proid"></param> public PrjFileEntity Save(int start, string path, string ucid) { int one = Convert.ToInt32(hffile.Value); PrjFileEntity filemodel = CommonFunction.FileUpLoad(start, one + start, hffile, path, ucid, (int)CommonEnum.FileFlag.上传); if (filemodel.ID == "-2") { CommonFunction.delfile(hffile.Value.ToString()); hffile.Value = "1"; ShowMessages(filemodel.FileName); return null; } else { return filemodel; } }
/// <summary> /// 项目文档上传文件 /// </summary> /// <param name="start">起始值</param> /// <param name="end">终止值</param> /// <param name="hfcount">隐藏控件</param> /// <param name="url">路径(例:project/123)</param> /// <param name="ucid">用户控件ID(UserContronlID)</param> /// <param name="fileflag">附件标识(CommonEnum.FileFlag)</param> /// <returns></returns> public static PrjFileEntity FileUpLoad(int start, int end, HiddenField hfcount, string url, string ucid, int fileflag) { int upsize = 40000000; try { upsize = Convert.ToInt32(ConfigurationManager.AppSettings["upsize"].ToString()); } catch (Exception) { upsize = 40000000; } //清空隐藏控件的值,用于存放路径 if (hfcount != null) { hfcount.Value = ""; } //设置上传路径 string path = System.Web.HttpContext.Current.Server.MapPath("~/UpFile/" + url + "/"); //判断上传文件夹是否存在,若不存在,则创建 if (!Directory.Exists(path)) { //创建文件夹 Directory.CreateDirectory(path); } string attaname = ""; string attaurl = ""; string attatype = ""; PrjFileEntity accessinfo = null; HttpFileCollection files = HttpContext.Current.Request.Files; for (int i = start; i < end; i++) { HttpPostedFile postedFile = files[i]; if (postedFile.FileName != "") { if (postedFile.ContentLength < upsize) { string[] name = postedFile.FileName.ToString().Split('\'); //获取扩展名 string extname = System.IO.Path.GetExtension(postedFile.FileName); //获取上传文件的名称 string oglname = name[name.Length - 1].ToString().Replace(extname, ""); //存储上传的文件名 attaname += oglname + ","; attatype += extname + ","; //为上传的文件设置新的名称,防止重名 string newname = System.DateTime.Now.ToString("yyyyMMddHHmmssffff") + i.ToString(); newname = newname + extname; //设置完整的文件上传路径 string filepath = path + newname; postedFile.SaveAs(filepath); if (hfcount != null) { hfcount.Value += filepath + "$"; } int j = filepath.IndexOf("UpFile"); string str = filepath.Substring(j - 1); attaurl += "~" + str + ","; } else { accessinfo = new PrjFileEntity(); accessinfo.ID = "-2"; accessinfo.FileName = "上传失败,上传文件不能大于" + upsize / 1000000 + "M!"; return accessinfo; } } } attatype = attatype.TrimEnd(','); attaname = attaname.TrimEnd(','); attaurl = attaurl.TrimEnd(','); if (attaname != "" && attaurl != "" && attatype != "") { accessinfo = new PrjFileEntity(attatype, attaname, attaurl, ucid, fileflag); } else { accessinfo = new PrjFileEntity(); } return accessinfo; }
5、删除附件:
/// <summary> /// 删除附件 /// </summary> public void Delete() { CommonFunction.delfile(hffile.Value.ToString()); hffile.Value = "1"; }
6、附件下载及删除:
/// <summary> /// 附件下载及删除 /// </summary> /// <param name="source"></param> /// <param name="e"></param> protected void rpFile_ItemCommand(object source, RepeaterCommandEventArgs e) { string id = e.CommandArgument.ToString().Trim(); PrjFileEntity fileinfo = PrjFileBLL.GetUniqueObj(id); string path = fileinfo.FilePath; if (e.CommandName.ToString() == "del") { path = HttpContext.Current.Server.MapPath(path); bool istrue = PrjFileBLL.Delete(id); if (istrue) { //物理路径的文件删除 if (System.IO.File.Exists(path)) { System.IO.File.Delete(path); } ShowMessages("删除成功"); //InitData(); //表单附件绑定 if (WorkTaskInsID != "") { FormData(); } //页面附件绑定 else if (ProID != "") { //读取现有数据 InitData(); } } else { ShowMessages("删除失败"); } } else { ShowScript("AttaView("" + id + "",1)"); } }
7、消息提醒:
/// <summary> /// 消息提醒 /// </summary> /// <param name="sMessage"></param> private void ShowMessages(string sMessage) { Page.ClientScript.RegisterStartupScript(this.GetType(), "Message", "<script>alert('系统提示:" + sMessage + "!');</script>"); return; } /// <summary> /// 运行纯脚本 /// </summary> /// <param name="sMessage"></param> public void ShowScript(string script) { Page.ClientScript.RegisterStartupScript(GetType(), "", script, true); }
8、获取隐藏控件的计数:
/// <summary> /// 获取隐藏控件的数值 /// </summary> /// <returns></returns> public int GetHiddenCount() { return Convert.ToInt32(hffile.Value); }
此方法在页面调用两个或两个以上此控件的时候使用。
最后看下调用:
1、在webconfig中注册:
<add src="~/webcontrols/AttaUpload.ascx" tagName="AttaUp" tagPrefix="wuc"/>
2、页面前台调用:
<tr> <th> 附件 </th> <td colspan="3"> <wuc:AttaUp ID="AttaUp" runat="server" /> </td> </tr>
3、页面加载时传递参数:
//附件 AttaUp.ProID = CID; //查看时才传,编辑时不需要 AttaUp.Flag = "1";
至于控件的WucID参数,再没有修改控件ID的情况下不需要传递,如果你把控件的ID重命名了,例如改为“FileUpControl”时,则一定要传递,否则会报错!!
4、提交页面时调用:
PrjFileEntity filemodel = AttaUp.Save(0, CommonFunction.GetProjectPath(hfproid.Value), "");
第一参数“0”,表示是从第一个上传控件开始查找附件;如果有两个上传控件,那么第一个控件保存附件时,传“0”;第二个则要用第一控件的GetHiddenCount()方法,获取第一个控件的上传控件个数;如果有第三个控件,那就要“累加”第一个控件和第二个控件的GetHiddenCount()方法的返回值。以此类推!!
注意:该方法只是返回了附件的实体类,多附件的每个属性值都以逗号(,)隔开,详情请仔细阅读FileUpLoad()方法。
如果你需要返回List<PrjFileEntity>,那么请自行修改FileUpLoad()方法。
5、页面数据保存失败时调用:
AttaUp.Delete();//删除上传的附件
因为我是通过点击“提交”按钮,来保存表单数据。而保存失败时会刷新页面,导致附件控件被重置,那么在保存数据前被上传的附件,就无法看到。
所以我需要删除之前上传的附件,让客户重新选择附件上传,虽然这样有点反人类,呵呵。如果你有好的解决办法,还请不吝赐教!
至此,整个多附件上传控件就介绍完毕了。