formData批量上传的多种实现

前言

  最近项目需要批量上传附件,查了下资料,网上很多但看着一脸懵,只贴部分代码,介绍也不详细,这里记录一下自己的采坑与多种实现,以免以后忘记。

 

  这里先介绍下FormData对象,以下内容摘自:https://developer.mozilla.org/zh-CN/docs/Web/API/FormData

  XMLHttpRequest Level 2添加了一个新的接口FormData.利用FormData对象,我们可以通过JavaScript用一些键值对来模拟一系列表单控件,我们还可以使用XMLHttpRequest的send()方法来异步的提交这个"表单".比起普通的ajax,使用FormData的最大优点就是我们可以异步上传一个二进制文件.

 

  在我的自定义input文件上传样式里就已经实现里单文件上传,并且实现了自定义input样式;如果构造FormData对象是传入表单js对象,formData会自动注入表单里的值;如果是new一个空对象,然后手动append的表单类型为file时要注意:这里append进去的是File对象,而不是FileList对象

   

效果

  先看一下大概效果:

 代码编写

 

  controller有两种方法:三种方式调的都是用一个接口

    /**
     * 批量上传
     */
    @PostMapping("upload")
    public ResultModel<List<AttachmentVo>> upload(HttpServletRequest request, @RequestParam("applyId") String applyId){
        List<MultipartFile> multipartFileList = ((MultipartHttpServletRequest) request).getFiles("attachment");
        System.out.println(multipartFileList.size());
        System.out.println(applyId);
        return null;
    }

    /**
     * 批量上传2 (推荐使用)
     */
    @PostMapping("upload2")
    public ResultModel<List<AttachmentVo>> upload2(MultipartFile[] attachment,@RequestParam("applyId") String applyId){
        System.out.println(attachment.length);
        System.out.println(applyId);
        return null;
    }

 

  自定义样式:(三种方式都是用这个样式),要引入bootstrap, 图标用的是font awesome

  

      .nav-bar {
          border-top: 1px solid #9E9E9E;
          margin: 10px 0 20px;
      }

      .nav-bar-title {
          margin: -13px 0 0 35px;
          background-color: white;
          padding: 0 10px;
          float: left;
          color: #199ED8;
      }
       
       .attachment-remove {
            font-size: 25px;
            color: red;
            margin-left: 5px;
            cursor: pointer;
        }

        .attachment-text-p {
            border: 1px solid #c2cad8;
            padding: 5px 5px;
            margin: 0;
            float: left;
            height: 30px;
            width: 90%;
        }

        .attachment-text-p + i {
            float: left;
            line-height: 30px !important;
        }

        .input-attachment {
            width: 90% !important;
            padding: 4px 12px !important;
        }

 

 

方式1

  点击Add,追加一个input,点击Delete,删除一个input,点击叉号也可以删除对应的input,需要单独为每个input选择文件

  效果

 

  html

<form id="attachments" enctype="multipart/form-data" class="form-horizontal nice-validator n-yellow" novalidate="novalidate">
        <div class='form-body'>
            <div class='form-group'>
                <label class="control-label col-md-1">附件管理:</label>
                <div class="col-md-4">
                    <button id="attachmentAddBtn" type="button" class="btn btn-default">Add Attachment</button>
                    <button id="attachmentDeleteBtn" type="button" class="btn btn-default">Delete Attachment</button>
                    <button id="attachmentUploadBtn" type="button" class="btn btn-default">Upload</button>
                </div>
            </div>
            <div class='form-group'>
                <label class="control-label col-md-1">附件上传:</label>
                <div id="attachmentInputs" class="col-md-3">

                </div>
            </div>
        </div>
    </form>

  js

    //attachment-remove
    $("#attachmentInputs").on("click", ".attachment-remove", function (even) {
        $(this).prev().remove();//删除上一个兄弟节点
        $(this).remove();//删除自己
    });

    //add but
    $("#attachmentAddBtn").click(function (even) {
        //name值一样就可以
        $("#attachmentInputs").append("<input name=\"attachment\" type=\"file\" class=\"form-control input-attachment\"/><i class=\"fa fa-times attachment-remove\"></i>");
    });

    //delete
    $("#attachmentDeleteBtn").click(function (even) {
        var files = $("#attachmentInputs input[type='file']");
        files.each(function (index, element) {
            //从最下面开始删除,至少保留一个
            if (!(index === 0) && index === (files.length - 1)) {
                $(element).next().remove();
                $(element).remove();
            }
        });
    });

    //upload
    $("#attachmentUploadBtn").click(function (even) {
        //1、通过HTML表单创建FormData对象 自动注入
        // var formData = new FormData($("#attachments")[0]);

        //2、从零开始创建FormData对象 手动注入
        var formData = new FormData();
        //注入 name=file
        var files = $("#attachmentInputs input[type='file']");
        for (var i = 0; i < files.length; i++) {
            //注意:这里append进去的是File对象,而不是FileList对象
            formData.append("attachment", files[i].files[0]);
        }
        //注入name=text
        formData.append("applyId", "123456");

        console.log(formData.getAll("attachment"));
        
        //执行上传
        $.ajax({
            url: ctx + "/attachment/upload2",
            type: "post",
            data: formData,
            processData: false,
            contentType: false,
            success: function (data) {
            },
            error: function (e) {
            }
        });
    });

    //add one input
    $("#attachmentAddBtn").click();

 

 

方式2

  第二种方式只有一个input,用的是multiple="multiple"属性,可以再弹窗里选择多个文件提交,如果再加工一下,也做成第三种一样,展示出文件名,同时可以删除对应的文件

  效果

 

 

  html

<form id="attachments2" enctype="multipart/form-data" class="form-horizontal" novalidate="novalidate">
        <div class='form-body'>
            <div class='form-group'>
                <label class="control-label col-md-1">附件管理:</label>
                <div class="col-md-4">
                    <button id="attachmentUploadBtn2" type="button" class="btn btn-default">Upload</button>
                </div>
            </div>
            <div class='form-group'>
                <label class="control-label col-md-1">附件上传:</label>
                <div id="attachmentInputs2" class="col-md-3">
                    <input name="attachment" type="file" class="form-control input-attachment" multiple="multiple"/>
                </div>
            </div>
        </div>
    </form>

  js

   //upload2
    $("#attachmentUploadBtn2").click(function (even) {
        //1、通过HTML表单创建FormData对象 自动注入
        // var formData = new FormData($("#attachments2")[0]);

        //2、从零开始创建FormData对象 手动注入
        var formData = new FormData();
        //注入 name=file
        var files = $("#attachmentInputs2 input[type='file']");
        for (var i = 0; i < files[0].files.length; i++) {
            formData.append("attachment", files[0].files[i]);
        }
        //注入name=text
        formData.append("applyId", "123456");

        console.log(formData.getAll("attachment"));

        //执行上传
        $.ajax({
            url: ctx + "/attachment/upload2",
            type: "post",
            data: formData,
            processData: false,
            contentType: false,
            success: function (data) {
            },
            error: function (e) {
            }
        });
    });

 

 

方式3

   定义了一个隐藏的input,并将Select File按钮的click与input的click对等,点击按钮相当于点击input,弹出选择文件对话框,监听了input的change事件,将选择的file对象push到全局数组变量attachmentArray中,点击Upload时再遍历注入到formData中

  效果

 

  html

<form id="attachments3" enctype="multipart/form-data" class="form-horizontal" novalidate="novalidate">
        <div class='form-body'>
            <div class='form-group'>
                <label class="control-label col-md-1">附件管理:</label>
                <div class="col-md-4">
                    <button id="selectFile" type="button" class="btn btn-default">Select File</button>
                    <button id="attachmentUploadBtn3" type="button" class="btn btn-default">Upload</button>
                </div>
            </div>
            <div class='form-group'>
                <label class="control-label col-md-1">附件上传:</label>
                <input id="attachmentInputs3" type="file" style="display: none;"/>
                <div id="attachmentText3" class="col-md-3">
                </div>
            </div>
        </div>
    </form>

 

  js

    //存放file对象
    var attachmentArray = [];
    //attachment-remove
    $("#attachmentText3").on("click", ".attachment-remove", function (even) {
        //删除attachmentArray数据
        attachmentArray.splice($(this).data("index"), 1);
        //删除html对象
        $(this).prev().prev().remove();
        $(this).prev().remove();
        $(this).remove();
    });

    //Select File
    $("#selectFile").click(function (even) {
        // 获取input
        $("#attachmentInputs3").click();
    });

    //input change
    $("#attachmentInputs3").change(function (even) {
        // 获取input
        var fileName = $(this).val();
        var file = $(this)[0].files[0];
        //是否选择了文件
        if (fileName) {
            attachmentArray.push(file);
            $("#attachmentText3").append("<div><p class='attachment-text-p'>" + fileName + "</p><i data-index='" + (attachmentArray.length - 1) + "' class=\"fa fa-times attachment-remove\"></i></div>")
        }
    });

    //upload3
    $("#attachmentUploadBtn3").click(function (even) {
        //这里只能手动注入
        var formData = new FormData();
        //遍历数据,手动注入formData
        for (var i = 0; i < attachmentArray.length; i++) {
            formData.append("attachment", attachmentArray[i]);
        }
        formData.append("applyId", "123456");
        console.log(formData.getAll("attachment"));
        //执行上传
        $.ajax({
            url: ctx + "/attachment/upload",
            type: "post",
            data: formData,
            processData: false,
            contentType: false,
            success: function (data) {
            },
            error: function (e) {
            }
        });
    });

 

后记

  最后看一下file数据、请求头、还有振奋人心的后台成功接参图

  file数据

 

请求头

 

成功接参

 

新需求

  项目需要支持同一张单上面有多个上传组件,按照我们之前的三种方式并不满足,第一种使用了id的方式去绑定,当多个组件在同一个html的时候就不行了,第三种我们采用一个全局数组变量来存选中的file,但之前一个组件有引一次js,当多个的时候就会重复引入,后面引入的变量、方法就会覆盖前面,同时,应该用的是id,当我们调用upload方式时不知道applyId工单号对应的form是哪一个,无法绑定附件的工单号,这里改进一下,将第一种跟第三种整合一下。

 

  上传组件html

  使用的是thymeleaf,th:text="#{attachment.title}"是国际化,<script th:replace="common/head::static"></script>引入的是公用的js、css,上传组件的js、css写在common里面,所有的页面都会引入它们,而且只引入一次。这里给每个form表单绑定一个applyId属性,对应具体的工单号,这样我们调用upload的时候就可以找到对应的form表单

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title th:text="#{attachment.title}"></title>
    <script th:replace="common/head::static"></script>
</head>
<body>
<!--
  使用方法:在任意工单页面添加此DIV
  <div th:replace="attachment/attachment::attachmentPage(${applyId})"></div>

  调用上传方法:Attachment.upload(${applyId});
-->
<div th:fragment="attachmentPage(applyId)">
    <div class="nav-bar"><span class="nav-bar-title" th:text="#{attachment.title}"></span></div>
    <form th:applyId="${applyId}" class="form-horizontal attachments-form" enctype="multipart/form-data">
        <div class='form-body'>
            <div class='form-group'>
                <label class="control-label col-md-1">附件管理:</label>
                <div class="col-md-4">
                    <button type="button" class="btn btn-default" onclick="Attachment.appendAttachmentInput(this)">
                        Select File
                    </button>
                </div>
            </div>
            <div class='form-group'>
                <label class="control-label col-md-1">附件列表:</label>
                <div class="col-md-10 attachments-list"></div>
            </div>
        </div>
    </form>
</div>
</body>
</html>

 

  其他任意html调用

  thymeleaf的传值方式之一,与组件html的 th:fragment="attachmentPage(applyId)" 配合使用,后面就可以这样使用 th:applyId="${applyId}"

        <div th:replace="attachment/attachment::attachmentPage(123456)"></div>
        <div th:replace="attachment/attachment::attachmentPage(111111)"></div>

 

 

  common.js  上传组件部分

  removeAttachmentInputListener,监听×号的点击事件,要在common.js执行一次。

/**
 * 三、附件上传的方法
 */
var Attachment = {
    //上传附件
    upload: function (applyId) {
        //终止上传
        if (!applyId) {
            layer.msg(i18n('attachment.applyid.is.null'));
            return;
        }

        //添加附件
        var formData = new FormData();
        $("form[applyId='"+applyId+"']").find("input[name='attachment']").each(function (index, element) {
            //过滤操作:input框有值,才append到formData
            if ($(element).val()) {
                formData.append("attachment",element.files[0]);
            }
        });

        //追加applyId到formData
        formData.append("applyId", applyId);

        //执行上传
        $.ajax({
            url: ctx + "/attachment/upload",
            type: "post",
            data: formData,
            processData: false,
            contentType: false,
            success: function (data) {
                if (checkResult(data)) {
                    console.log('附件上传成功:', data);
                } else {
                    throw e;
                }
            },
            error: function (e) {
                console.log('附件上传失败');
                throw e;
            }
        });
    },
    //添加附件
    appendAttachmentInput: function (btn) {
        //先追加html
        $(btn).parents('.attachments-form').find(".attachments-list").append("<div><input type=\"file\" name=\"attachment\" class=\"hidden\"/></div>");

        //最新追加的input
        var attachments = $(btn).parents('.attachments-form').find(".attachments-list").find("input[name='attachment']");

        //绑定input的change事件,注意:当我们点击取消或×号时并不触发,但是无所谓,我们在upload方法进行过滤空的input就可以了
        attachments[attachments.length - 1].onchange = function(){
            var fileName = $(this).val();
            if (fileName) {
                $(this).parent("div").append("<p class='attachment-text-p'>" + fileName + "</p><i class=\"fa fa-times attachment-remove\"></i>");
            }else{
                $(this).parent("div").remove();
            }
        };

        //触发最新的input的click
        attachments[attachments.length - 1].click();
    },
    //删除附件
    removeAttachmentInputListener: function () {
        $(".attachments-form").on("click", ".attachment-remove", function (even) {
            $(this).parent().remove();
        });
    }
};

 

  common.css 上传组件部分

    .attachment-remove {
        font-size: 25px;
        color: red;
        margin-left: 5px;
        cursor: pointer;
    }
    
    .attachment-text-p {
        border: 1px solid #c2cad8;
        padding: 5px 5px;
        margin: 0;
        float: left;
        height: 30px;
        width: 90%;
        margin-top: 5px;
    }
    
    .attachment-text-p + i {
        float: left;
        line-height: 30px !important;
        margin-top: 5px;
    }

 

新需求效果

 

 

报错记录:org.apache.tomcat.util.http.fileupload.FileUploadBase$FileSizeLimitExceededException: The field images exceeds its maximum permitted size of 1048576 bytes.

解决:调大http的最大上传大小

  http:
    multipart:
      max-file-size: 5Mb #单个文件大小
      max-request-size: 50Mb #总大小

 

转载于:https://www.cnblogs.com/huanzi-qch/p/9853067.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值