一、业务场景
项目中有个功能模块是商品管理,其中需要使用到图片上传,而且是多文件上传,这是比较常规的操作,就自己来写这个功能。
商品管理这个功能,除去图片操作之外,常规的增、删、改、查很快完成,然后在把图片相关操作添加进来。
二、需求分析
自己以前开发的项目中,涉及文件上传的功能倒是不少,可是涉及多文件上传的功能还真没有接触过。可是这不影响自己来做
这个功能,正好可以借此机会学习学习如何实现多文件上传的功能。要点有两个,一个是前端代码怎么添加多个文件,一个是后台
如何来接收多个文件。搞清楚这两个问题之后,剩下的事情会简单得多。
三、解决方案
自己首先完成多文件上传的前端代码,核心代码是创建一个FormData对象,然后获取页面中选择的文件数组之后,
循环遍历添加多个文件,并且多个文件是添加到同一个字段上,代码如下
var formData = new FormData();
//循环获取上传个文件
for (var i = 0, len = $("#upload_file")[0].files.length; i < len; i++) {
var dataTemp = $("#upload_file")[0].files[i];
//console.log("dataTemp--->", dataTemp);
var fileName = dataTemp.name;
var fivarype = fileName.substring(fileName.lastIndexOf('.')).toLowerCase();
if(fivarype !== '.jpg' && fivarype !== '.png'){
return layer.msg('错误提示:只能上传jpg或者png格式的图片!');
}
var fileSize = dataTemp.size;
var limitSize = 2 * 1024 * 1024;
//console.log("fileSize--->", fileSize);
if(fileSize > limitSize){
return layer.msg('错误提示:文件大小不能超过2M!');
}
formData.append("files", dataTemp);
}
这样写有一个好处,可以单独判断每一个选中的文件是否符合特定的格式,大小是否符合要求,在前端可以做一个
简单的过滤。将所有的文件对象都添加到files这个字段之上,多单文件上传的时候自己以为只能添加一次,可是做
好这个功能之后,发现它可以添加多次。有的网友是直接添加formData.append("files",$("#upload_file")[0]); 这样也没问题,
只是在选择文件的时候,不会做相关的校验。html代码如下,
<!-- 图片文件选择 -->
<div style="display:none;">
<input type="file" name="file" id="upload_file" onchange="do_upload()" multiple="multiple" accept=".jpg,.png">
</div>
上传文件时,自己使用的是ajax请求来发送数据,代码如下,
var loadStatus = layer.load(1);
//提交数据
$.ajax({
url: contextPath + '/goods/upload',
type: 'post',
headers:{
'loginToken': "Bearer " + localStorage.loginToken
},
data: formData,
cache: false,//上传文件无需缓存
contentType: false,//必须
processData: false,//用于对data参数进行序列化处理 这里必须false
success: function (result) {
/**
{
code: -1
count: null
data: null
message: "未获取到上传文件信息"
status: "失败"
url: null
}
*/
//console.log("result--->", result);
if (result.code === 0) {
layer.msg("提示信息:图片上传成功!");
var tempArr = result.data;
for(var i = 0, len = tempArr.length; i < len; i++){
filePathList[filePathList.length] = tempArr[i];
}
//console.log("图片上传-filePathList--->", filePathList);
handlerShowPicture();
}else {
layer.msg("错误提示: 图片上传失败");
}
$("#upload_file").parent().html('<input type="file" name="file" id="upload_file" onchange="do_upload()" multiple="multiple" accept=".jpg,.png">');
layer.close(loadStatus);
// setTimeout(function () {
// //延迟1 s 在关闭弹出窗口
// layer.closeAll();
// }, 1000);
},
error: function () {
layer.msg('错误提示:上传图片出现异常,请稍后重试!');
layer.close(loadStatus);
// setTimeout(function () {
// //延迟1 s 在关闭弹出窗口
// layer.closeAll();
// }, 1000);
$("#upload_file").parent().html('<input type="file" name="file" id="upload_file" onchange="do_upload()" multiple="multiple" accept=".jpg,.png">');
},
timeout: 180000,
complete: function () {
//$.messager.progress('close');
}
});
这种方式简单可用,在写过的很多项目中都是使用这种方式来上传文件。
后端代码经过反反复复的摸索之后,操作依赖也并不复杂,一个是接收文件数组的时候,直接在方法的形参中进行接收,代码如下
/**
* @description: 商品图片上传
* @date: 2022/1/28 15:31
* @param : MultipartFile[]
* @return: ResultVO
*/
//@PostMapping("/upload")
@RequestMapping(value="/upload",method= RequestMethod.POST,consumes="multipart/form-data")
public ResultVO uploadFile(@RequestParam MultipartFile[] files){
List<String> filePathList = null;
try{
if(files == null){
return ResultVOUtil.error(-1, "文件不存在");
}
for(int i = 0, len = files.length; i < len; i++){
MultipartFile file = files[i];
long fileSize = file.getSize();
if(fileSize > giftLimitSize || fileSize == 0){
log.error("文件大小超出限制:" + fileSize);
return ResultVOUtil.error(-1, "只能上传大小2M以内的图片");
}
String fileName = file.getOriginalFilename();
if(StringUtils.isBlank(fileName)){
log.error("文件名称错误:" + fileName);
return ResultVOUtil.error(-1, "文件名称错误");
}
String fileSuffixName = fileName.substring(fileName.lastIndexOf(".") + 1);
if(!typeSet.contains(fileSuffixName)){
log.error("文件后缀名错误:" + fileSuffixName);
return ResultVOUtil.error(-1, "文件后缀名错误");
}
}
filePathList = this.goodsManageService.uploadPicture(files);
}catch (Exception ex){
log.error("图片上传操作异常--->" + ex.getMessage());
ex.printStackTrace();
}
log.info("图片上传返回数据为--->" + filePathList);
if(Objects.equal(filePathList, null)){
return ResultVOUtil.error(-1, "图片上传操作失败");
}else{
return ResultVOUtil.success("上传成功", filePathList);
}
}
在service中具体的处理逻辑如下,
@Override
public List<String> uploadPicture(MultipartFile[] files) throws Exception {
if(files == null){
log.error("参数-files-错误{}:", files);
return null;
}
String targetPath = String.join(File.separator, linuxPath, "3");
File tempPath = new File(targetPath);
if(!tempPath.exists()){
tempPath.mkdirs();
}
String destFileName = "";
List<String> filePathList = new ArrayList<String>();
try{
for(int i = 0, len = files.length; i < len; i++) {
MultipartFile file = files[i];
String fileName = file.getOriginalFilename();
String fileSuffixName = fileName.substring(fileName.lastIndexOf(".") + 1);
destFileName = CommonUtil.getUUID() + "." + fileSuffixName;
String targetFile = String.join(File.separator, targetPath, destFileName);
File destFile = new File(targetFile);
if(!destFile.exists()){
destFile.createNewFile();
}
file.transferTo(destFile);
String returnPath = File.separator + String.join(File.separator, "3", destFileName);
boolean isWin = System.getProperty("os.name").toLowerCase().contains("win");
if(isWin){
//windows 系统 进行路径替换 将 / 替换为 \\
returnPath = returnPath.replaceAll("\\\\", "/");
}
filePathList.add(returnPath);
}
}catch (Exception ex){
log.error("文件保存异常:" + ex.getMessage());
return null;
}
return filePathList;
}
主要做的一个操作就是接收文件数组,然后将文件保存到自己指定的一个目录下面即可,核心代码是
file.transferTo(destFile);file表示文件数组中的对象,destFile表示目标文件的全路径,包含文件后缀名,
这句代码的含义就是将上传的文件保存到指定的文件当中。最后按照自己业务的要求返回指定的文件路径,不是文件的
全路径,因为在显示图片的时候传的参数是一个相对路径,后台处理的时候加上路径前缀即可。图片显示的方法如下,
public void showPicture(String filePath, HttpServletResponse response) throws Exception {
if(StringUtils.isBlank(filePath)){
log.error("参数-filePath-错误");
return;
}
String realPath = linuxPath + filePath;
String result = "-1";
File file = new File(realPath);
response.setContentType("image/jpeg");
try (FileInputStream fis = new FileInputStream(realPath);
OutputStream os = response.getOutputStream();){
int len = 0;
byte[] buffer = new byte[1024];
while ((len = fis.read(buffer)) != -1){
os.write(buffer, 0, len);
}
os.flush();
} catch (Exception ex) {
log.error("图片显示服务错误:" + ex.getMessage());
}
}
至此整个多文件上传的功能,全部搞定,功能完成,效果如下