1.情景展示
文件上传,在开发过程中,经常会用到;
本文将以springboot为例,如何接收前端的文件请求;
上传文件使用插件更方便,这里我使用webuploader
2.前端代码
第一步:准备webuploader插件放到项目当中;
由于,springboot在项目启动的时候,会自动将static目录下的静态资源(前端代码)加载到项目当中;
所以,我这里为了省事儿,就不创建web目录了;
而且springboot还会自动将resources/static目录下的index.html当做项目的欢迎页(我这里为了省事,就没有配置请求与页面想对照的映射关系)。
现在,我们来对前端代码进行整体认知;
一个要存放插件的位置:webuploader;
一个要展示插件的页面:index.html;
一个要处理文件上传的js:upload.js;
index.html需要引入:
webuploader.css和webuploader.js;
必须在webuploader.js引入之前引入jQuery.js(因为它依赖jQuery);
include.js包含的内容如下:
// 获取请求路径
var pathName = window.document.location.pathname;
// 通过截取得到项目名称
var baseUrl = pathName.substring(0, pathName.substr(1).indexOf('/') + 1);
用途:获取该项目的名称。
html的核心代码为:
<div id="filePicker" style="line-height:15px;" class="webuploader-container">
<div class="webuploader-pick">选择文件</div>
</div>
webuploader插件会通过init()方法,对filePicker这个div里面追加内容,例如:
js核心代码:
查看代码
/**
* 该插件批量上传的本质:
* 每个文件单独发送一个上传请求,所以,其本质还是单文件上传,而不是真正含义上的:一次请求包含多个文件。
*/
this.init = function () {
var state = 'pending';
var upUrl = baseUrl + '/file/upload.do';
var uploader = WebUploader.create({
auto: true, // 选择文件后自动上传,默认不自动上传需要触发
swf: baseUrl + '/webuploader/Uploader.swf', // swf文件路径
server: upUrl, // 上传文件的接口(替换成你们后端给的接口路径)
// 选择文件的按钮。可选。
// 内部根据当前运行是创建,可能是input元素,也可能是flash.
pick: '#filePicker',
accept: {
extensions: 'xls,xlsx', // 允许的文件后缀,不带点,多个用逗号分割,这里支持老版的Excel和新版的
mimeTypes: 'application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
},
resize: false, // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!
/* duplicate :true */ //可重复上传
});
/**
* 文件被加入队列之前触发
* @explain 主要用来传其它参数
*/
uploader.on('beforeFileQueued', function (file) {
var obj = new Object();
obj.THEMEID = $('#THEMEID').val();
uploader.options.formData = obj;
});
/**
* 上传成功
*/
uploader.on('uploadSuccess', function( file, result) {
alert(JSON.stringify(result));
});
/**
* 上传失败
*/
uploader.on('uploadError', function( file, result) {
// alert('文件上传失败!');
alert(JSON.stringify(result));
});
/**
* 上传完成
*/
uploader.on( 'uploadComplete', function( file ) {
});
uploader.on( 'all', function( type ) {
if ( type === 'startUpload' ) {
state = 'uploading';
} else if ( type === 'stopUpload' ) {
state = 'paused';
} else if ( type === 'uploadFinished' ) {
state = 'done';
}
});
}
HTML完整代码:
查看代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
<link rel="stylesheet" type="text/css" href="webuploader/webuploader.css">
<script type="text/javascript" src="common/js/jquery-3.6.0.min.js"></script>
<script type="text/javascript" src="webuploader/webuploader.js"></script>
<script type="text/javascript" src="common/js/include.js"></script>
<script type="text/javascript" src="upload.js"></script>
</head>
<body>
<h1>欢迎来到Marydon的博客园</h1>
<table border="0" cellpadding="0" cellspacing="0" style="width: 100%; height: 90%">
<tr>
<td style="text-align: center">
<div id="filePicker" style="line-height:15px;" class="webuploader-container">
<div class="webuploader-pick">选择文件</div>
</div>
</td>
<td>
<span style="color: #898989">备注:扫描文件格式为xls</span>
</td>
</tr>
</table>
</body>
</html>
注意:关键点在于,引入文件的路径问题,不要搞错了。
JAVASCRIPT完整代码:
查看代码
var upload = new Upload();
window.onload = function(){
// 不要忘了调用WebUploader的初始化函数
upload.init();
}
function Upload() {
var object = this;
/**
* 该插件批量上传的本质:
* 每个文件单独发送一个上传请求,所以,其本质还是单文件上传,而不是真正含义上的:一次请求包含多个文件。
*/
this.init = function () {
var uploadUrl = baseUrl + '/file/upload.do';
var uploader = WebUploader.create({
auto: true, // 选择文件后自动上传,默认不自动上传需要触发
swf: baseUrl + '/webuploader/Uploader.swf', // swf文件路径
server: uploadUrl, // 上传文件的接口(替换成你们后端给的接口路径)
// 选择文件的按钮。可选。
// 内部根据当前运行是创建,可能是input元素,也可能是flash.
pick: '#filePicker',
accept: {
extensions: 'xls,xlsx', // 允许的文件后缀,不带点,多个用逗号分割,这里支持老版的Excel和新版的
mimeTypes: 'application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
},
resize: false, // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!
/* duplicate :true */ //可重复上传
});
/**
* 文件被加入队列之前触发
* @description 主要用来传其它参数
* @explain 我没有在这里演示,没有传其它参数
*/
uploader.on('beforeFileQueued', function (file) {
var obj = new Object();
obj.THEMEID = $('#THEMEID').val();
uploader.options.formData = obj;
});
/**
* 上传成功
*/
uploader.on('uploadSuccess', function( file, result) {
alert(JSON.stringify(result));
});
/**
* 上传失败
*/
uploader.on('uploadError', function( file, result) {
// alert('文件上传失败!');
alert(JSON.stringify(result));
});
};
}
注意:关键点在于,需要对插件WebUploader进行初始化。
3.后端代码
难点在于:如何接收前端发过来的请求?
其实,很简单:参数file的值就是MultipartFile类型的文件。
文件上传,上传的参数至少会有以下6个参数:
为了方便对于参数的使用,我用实体类来接收这些参数,这样,在后续用到的时候就会很方便。
查看代码
import lombok.Getter;
import lombok.Setter;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 文件上传请求参数
* @description:
* @author: Marydon
* @date: 2022-01-10 15:38
* @version: 1.0
* @email: marydon20170307@163.com
*/
@Getter
@Setter
public class UploadParamsDto {
@NotBlank(message = "id:不能为空")
private String id;
@NotBlank(message = "name:不能为空")
private String name;
@NotBlank(message = "type:不能为空")
private String type;
@NotBlank(message = "lastModifiedDate:不能为空")
private String lastModifiedDate;
@NotBlank(message = "size:不能为空")
private String size;
@NotNull(message = "file:不能为空")
private MultipartFile file;
}
如果不需要对参数进行非空校验,删掉这些属性对应的注解即可。
JAVA控制层完成代码:
查看代码
import com.example.upload.web.dto.UploadParamsDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* 文件上传控制层
* @description:
* @author: Marydon
* @date: 2022-01-10 10:16
* @version: 1.0
* @email: marydon20170307@163.com
*/
@Slf4j
@RestController
@RequestMapping("/file")
public class UploadController {
// 文件存放路径:项目路径/files
private static final String UPLOADED_FOLDER = System.getProperty("user.dir") + File.separator + "files";
/*
* 文件上传
* @attention: 不支持批量上传
* @description: 请求类型-multipart/form-data
* @date: 2022/1/11 10:23
* @param: paramsDto
* @return: java.util.Map
*/
@PostMapping("/upload.do")
public Map<String, Object> upload(@Validated UploadParamsDto paramsDto) {
// 即将返回的数据
Map<String, Object> resultMap = new HashMap<>(2);
resultMap.put("isSuccess", true);
resultMap.put("error", "");
Map<String, String> errorMap = new HashMap<>();
// 获取上传的文件
MultipartFile file = paramsDto.getFile();
// 新文件的文件名
String fileName = System.currentTimeMillis() + "_" + paramsDto.getName();
// 新文件的生成
File copyFile = new File(UPLOADED_FOLDER + File.separator + fileName);
// 目录不存在就自动创建
copyFile.mkdirs();
try {
// 将multipartfile文件转移到file对象所代表的文件中
// 该方法可以将文件夹直接变为文件
// 当copyFile代表的是一个目录时,正常情况文件是无法输入的,而transferTo()方法会将该目录直接变为文件并输入数据
file.transferTo(copyFile);
} catch (IOException e) {
e.printStackTrace();
log.error(e.getMessage());
// 如果报错,意味着该文件上传失败,将原文件名和报错信息返回
errorMap.put(paramsDto.getName(), e.getMessage());
}
if (!errorMap.isEmpty()) {
resultMap.put("error", errorMap);
}
return resultMap;
}
}
难点还有:如何获取保存文件的目录;
新文件名的命名规则;
文件的快速复制。
当然,如果只是根据上传的文件进行解析处理,而不保存的话,不会遇到以上3个问题。
说明:
请求格式:multipart/form-data;
返回格式:application/json。
另外,如果,需要对实体类UploadParamsDto进行参数校验,除了需要在它前面加注解@Validated外,还要引入对应的注解jar依赖。
<!--spring对参数进行校验:hibernate validator-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.18.Final</version>
</dependency>
更多细节,我在之前的文章讲过。
4.效果展示
直接来到欢迎页
点击“选择文件”,选择要上传的文件,支持多选。
最终上传的文件存放位置
写在最后
哪位大佬如若发现文章存在纰漏之处或需要补充更多内容,欢迎留言!!!