jQuery-File-Upload通过点击外部按钮上传附件,并美化Html文件上传控件

先说一些废话。这两天在为ASP.NET MVC程序添加上传附件功能。HTML默认的input file控件不但难看,而且需要结合form实现提交(虽然可以使用H5的FormData对象来异步提交,但IE不太支持)。花了两天时间研究这个功能,期间找到了uploadifyPlupload以及zui的上传附件插件,uploadify和Plupload配置有些麻烦,而zui虽然样式很好,而且配置简单,但由于基于bootstrap做了定制,我的程序也用到了bootstrap,引用zui的上传控件后导致css冲突,页面变乱,因此不得不放弃(自己的程序基本上完成,不愿再调整页面布局)。
本来打算就使用form异步提交吧,但还有些不甘心,就重新组织关键词进行百度,发现一个新的插件jQuery-File-Upload,但可惜的是,虽然官方文档很详细,插件功能很强大,但示例基本上都是自动上传的,而通过外部按钮上传的示例在大量的文档内容中被我给忽略了,导致多花费了半天时间。

经过不断摸索,和网友的文章,终于找到一个合适的方法实现所需功能:点击外部按钮实现异步上传文件至服务器。其实操作很简单,在fileupload()函数的add选项中为外部按钮绑定点击事件并调用data.submit()函数,具体请见下方js代码$(’#fileUpload’).fileupload()部分

使用这个插件前先按照网友的文章对文件上传按钮进行了美化,这里一并写出来做个记录。
对上传文件控件进行美化其实也很简单,就是将控件本身变透明,然后用其他标签将其包围,并改为自己喜欢的样式。

显示效果。
在这里插入图片描述
附上代码。
上传文件的html页面组织(使用bootstrap和网友提供的样式),超链接a标签包围input[file]控件,并添加删除按钮,在file控件选择文件后调用updateFileMessage函数更新页面提示,点击执行导入按钮进行文件上传:

@* 上传文件开始 *@
<a href="javascript:;" class="a-upload form-control" id="fileTip" title="尚未选择任何文件">
    <input type="file" id="fileUpload" name="files[]" multiple  data-url="@Url.Action("ImportData")"
           onchange="updateFileMessage('fileUpload', 'fileCount', 'fileTip', '尚未选择任何文件');"
                    accept="application/msexcel,application/msword,application/pdf,image/x-png,text/plain,
                    application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet">
            <span>选择要导入的数据文件</span>[<span id="fileCount">0</span>]
            <span style="width:auto;height:auto;" title="点击清除所选文件" onclick="clearFile('fileUpload','fileCount', 'fileTip', '尚未选择任何文件');"
                    class="close">x</span>
</a>
<input type="button" class="btn btn-sm btn-primary" id="btnImport" value="执行导入" />
@* 上传文件结束 *@
@* 以下为upload插件所需的基本js文件,我的jquery引用在布局页,这里没有显示,单独使用时需要引用jquery文件 *@
 <script src="~/Scripts/jquery.ui.widget.js"></script>
 <script src="~/Scripts/jquery.iframe-transport.js"></script>
 <script src="~/Scripts/jquery.fileupload.js"></script>

自定义的js代码,用于选择文件后现实提示内容,包括显示所选文件数量,并在title中提示文件名称(因为chrome和ie等浏览器显示input[file]的样式不一样,只好统一一下):

//felem为上传文件控件id, fcount为显示文件数量的标签id,ftip为显示title提示的标签id, defaultTip为默认的提示信息
function updateFileMessage(felem, fcount, ftip, defaultTip) {
	//使用$("#id")方式找不到files属性(选择的文件列表),使用getElementById可以,后来发现使用jquery可以实现,这里暂时不改了
    var files = document.getElementById(felem).files;
    $("#" + fcount).html(files.length);
    var info = "";
    for (var i = 0; i < files.length; i++) {
        info += files[i].name + '[大小:' + calSize(files[i].size) + "]" + "\n";
    }
    if (info == "") {
        info = defaultTip == null ? "未选择任何文件" : defaultTip;
    }
    $("#" + ftip).attr("title", info);
    //将文件大小由字节换算为KB或MB显示
    function calSize(size) {
        size /= 1024;
        if (size < 1024) {
            return size.toFixed("1") + "KB";
        }
        return (size / 1024).toFixed("1") + "MB";
    }
}
//清除所选文件并更新提示,我的清理方法可能有问题,还需要确认文件是否被清除
function clearFile(felem, fcount, ftip, defaultTip) {
    document.getElementById(felem).files = null;
    $("#" + fcount).html(0);
    $("#" + ftip).attr("title", defaultTip);
}
//使用jquery-fileupload控件控制上传文件
$('#fileUpload').fileupload(
            {
                dataType: "json",
                add: function (e, data) {
                	//敲黑板!官网是动态添加按钮,这里使用已定义的按钮来提交。
                	//这里还有待确认,按照官网介绍,add选项是每添加一个文件调用一下回调函数,所以对于一次选择多个文件会不会重复执行,我还没有确认。
                    $("#btnImport").click(function () {
                        data.submit();
                    });
                },
                done: function (e, data) {
                    alert(data);
                },
                fail: function (e, data) {
                    alert(data);
                }
            });

上传文件样式(引用自网友haorooms的文章):

/*a  upload */
.a-upload {
    padding: 4px 10px;
    /*height: 20px;*/
    line-height: 25px;
    position: relative;
    cursor: pointer;
    color: #888;
    background: #fafafa;
    border: 1px solid #ddd;
    border-radius: 4px;
    overflow: hidden;
    display: inline-block;
    *display: inline;
    *zoom: 1
}

    .a-upload input {
        position: absolute;
        font-size: 100px;
        right: 0;
        top: 0;
        opacity: 0;
        filter: alpha(opacity=0);
        cursor: pointer
    }

    .a-upload:hover {
        color: #444;
        background: #eee;
        border-color: #ccc;
        text-decoration: none
    }

好了,就写这些吧,因为记性有些差,趁着找到解决方法赶紧记录下来,以免后面给忘记。写的内容有些凌乱,而且有部分内容我还没有确认是否真的可行,但脚印需要一步一步踩出来,慢慢探索,多思考终究会找到解决方法。

后续补充:
如果需要一次上传多个文件,并且希望只发送一次请求,那么需要将singleFileUploads选项设置为false。
如果需要限制一次上传文件的数量,可以通过设置limitMultiFileUploads选项的数字来实现,例如设置为5,但请注意,这个选项只是限制一次请求发送文件的数量,如果所选文件数量超过设置值,那么会分多次发送,例如选择了7个文件,第一次会发送5个,第二次发送2个。真正实现文件限制数量需要设置send选项,在send选项的函数中进行检验,如果超过数量则返回false以终止发送。需要注意的是,send返回false后插件会调用fail选型绑定的函数,可以在这个函数里做一些处理。还需要注意的是:如果设置了limitMultiFileUploads,那么会因为分批发送文件导致每次send的数量不会超过你在send函数中检验的数量,所以如果要在send中检查,最好不要设置limitMultiFileUploads。

因为我的上传控件美化是在使用这个插件之前做的(当时未发现这个插件),所以修改控件的样式函数绑定的位置是自己研究的,用起来比较乱。而使用这个插件时,插件提供了需要绑定函数的选项,例如change, add等,我们可以按需要将样式修改函数绑定到这些事件上,这样不但思路清楚,而且还好维护。

另外,在ASP.NET MVC Action中接收文件时,我发现使用foreach(HttpPostedFileBase file in Request.Files)无法获取到文件,需要通过普通的循环使用HttpPostedFileBase file = Request.Files[i]来获取,这个问题还没有详细研究,有时间再说。

补充代码。
调整后的fileupload js代码:

$('#fileUpload').fileupload(
            {
                dataType: "json",
                singleFileUploads: false,
                //limitMultiFileUploads:5,
                add: function (e, data) {
                    $("#btnImport").click(function () {
                        data.submit();
                    });
                },
                send: function (e, data) {
                    if (data.files.length > 5) {
                        alert("文件数量不能超过5个!");
                        return false;
                    }
                },
                done: function (e, data) {
                    // data.result
                    // data.textStatus;
                    // data.jqXHR;
                    alert(data.result.message);
                },
                fail: function (e, data) {
                    // data.errorThrown
                    // data.textStatus;
                    // data.jqXHR;
                    if (data.errorThrown != undefined) {
                        alert(data.errorThrown);
                    }
                }
            });

ASP.NET MVC Action中的代码:

 [HttpPost]
        public JsonResult ImportData()
        {
            if (Request.Files.Count == 0 || Request.Files.Count > 5)
            {
                return Json(new { success = "false", message = "导入数据失败!文件数量不能0,也不能超过5个。" });
            }
            var dir = new DirectoryInfo(Path.Combine(Server.MapPath("~"), "Attachment", "ImportData"));
            try
            {
                if (!dir.Exists)
                    dir.Create();
                //fileupload默认每次只提交一个文件,同时选择多个会分多次提交,这里需要考虑怎样处理
                //需要修改singleFileUpload选项为false实现一次请求发送多个文件
                //使用foreach无法实现遍历文件,尝试改用for循环
                for (int i=0;i<Request.Files.Count;i++)
                {
                    var file = Request.Files[i];
                    var fileSavePath = Path.Combine(dir.FullName, $"{file.FileName.Substring(0, file.FileName.LastIndexOf('.'))}_{DateTime.Now.ToString("yyyyMMddHHmmss")}{Path.GetExtension(file.FileName)}");
                    file.SaveAs(fileSavePath);
                }

                return Json(new { success = "true", message = "已导入!" });
            }
            catch (Exception ex)
            {
                LogService.Current.Error("导入数据失败!", UserContext.GetCurrentUserAccount(), ex);
                return Json(new { success = "false", message = $"导入数据失败!错误:{ex.Message}" });
            }
        }

参考文章:
jQuery-File-Upload 使用,jQuery-File-Upload上传插件
ASP.NET MVC中使用Jquery File Upload插件上传文件
插件官网介绍:How to start uploads with a button click(官网页面最下方)
[css inputtype=file] 样式美化,input上传按钮美化

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章