基于FormData的JS批量文件上传实现指南

作者:小括号

时间:2026-10-22

浏览:596

本文详细解析如何利用JavaScript中的FormData对象实现前端批量文件上传功能。文章对比了三种常见实现方式:动态添加输入框、使用multiple属性多选以及隐藏Input配合数组管理。通过具体的HTML与JS代码示例,展示了从构造FormData到Ajax提交的全过程,并附带后端接收参数的关键代码,帮助开发者解决批量附件上传的技术难点。

文章目录

近期项目中遇到了批量上传附件的需求,查阅相关资料后发现,网络上的教程往往只展示部分代码且解释不够详尽。为了记录实践过程中的经验教训及多种实现方案,避免日后遗忘,特此整理本文。

FormData对象基础

首先对FormData对象进行简要介绍,相关定义如下:

XMLHttpRequest Level 2规范引入了FormData接口。借助该接口,开发者可以使用JavaScript模拟一系列键值对来构建表单数据,并通过XMLHttpRequest的send()方法异步提交这些数据。相较于传统的Ajax请求,使用FormData的最大优势在于支持异步上传二进制文件。

在之前的自定义Input文件上传样式中,已实现了单文件上传及自定义样式的功能。需要注意的是,若通过传入表单JS对象来构造FormData,系统会自动注入表单内的值;若新建一个空对象并手动调用append方法添加类型为file的文件,务必确保传入的是File对象,而非FileList对象。

以下是大致的运行效果展示:

FormData对象基础

后端接口定义

后端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;
 }

方式一:动态添加Input

第一种实现方式

点击Add按钮可追加一个input元素,点击Delete按钮或叉号可删除对应的input。这种方式要求用户为每个独立的input单独选择文件。

界面效果如下:

FormData对象基础

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>

JavaScript逻辑代码


//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();

方式二:multiple属性多选

第二种实现方式

此方式仅包含一个input元素,利用multiple="multiple"属性允许用户在弹窗中一次性选择多个文件进行提交。若需进一步优化,可参考第三种方式,展示文件名并支持单独删除已选文件。

界面效果如下:

FormData对象基础

FormData对象基础

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>

JavaScript逻辑代码


//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) {
 }
 });
 });

方式三:隐藏Input与数组管理

第三种实现方式

定义一个隐藏的input,并将Select File按钮的click事件与input的click事件绑定,点击按钮即触发文件选择对话框。同时监听input的change事件,将选中的File对象push到全局数组变量attachmentArray中。点击Upload时,再遍历该数组并将文件注入到FormData对象中。

界面效果如下:

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>

JavaScript逻辑代码


//存放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数据详情

FormData对象基础

请求头信息

FormData对象基础

后端成功接收参数截图

FormData对象基础

以上内容为本文的全部总结,希望能为读者的学习提供有益帮助,也欢迎大家继续支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章