SpringBoot中的上传文件
上传文件的操作在有些功能中属于比较常用的环节,这里整理下SpringBoot环境中上传文件的实现方式。
这里实现的是上传文件的后台接口,前端部分可以用测试工具模拟实现,就先不在这里表述了。
Dto层
使用MultipartFile类型的变量来接收文件
package com.bbzd.business.file.entity.fileService.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.*;
/**
* Description:
* Copyright: Copyright (c) 2013
* Company: www.bbzd.com
*
* @author wangjianzhong
* @version 1.0
*/
@Data
public class UploadFileDto {
/**
* 文件组令牌
*/
@ApiModelProperty(value = "文件组令牌")
@NotBlank(message = "文件组令牌不能为空")
private String token;
/**
* 文件
*/
@ApiModelProperty(value = "文件")
@NotNull(message = "文件不能为空")
private MultipartFile file;
/**
* 用户代码
*/
@ApiModelProperty(value = "用户代码")
@NotBlank(message = "用户代码不能为空")
private String userCode;
/**
* 用户名
*/
@ApiModelProperty(value = "用户名")
@NotBlank(message = "用户名不能为空")
private String userName;
/**
* 搜素标识符
*/
@ApiModelProperty(value = "搜素标识符")
private String searchIdentifier;
/**
* 备注
*/
@ApiModelProperty(value = "备注")
private String remark;
}
Controller层
请求类型 POST
参数类型 MediaType.MULTIPART_FORM_DATA_VALUE
package com.bbzd.business.file.controller;
import com.bbzd.business.file.entity.fileService.dto.UploadFileDto;
import com.bbzd.business.file.service.FileService;
import com.bbzd.common.annotation.LogAnnotation;
import com.bbzd.common.base.Result;
import com.bbzd.common.enums.LogOperateTypeEnum;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.http.MediaType;
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 javax.annotation.Resource;
/**
* Title: FileServiceController
* Description:
* Copyright: Copyright (c) 2013
* Company: www.bbzd.com
*
* @author wangjianzhong
* @version 1.0
*/
@RestController
@RequestMapping("/webApi/file/fileService")
@Api(tags = "file-fileService-文件服务")
public class FileServiceController {
@Resource
FileService fileService;
/**
* @method: 查询文件信息列表
* @author wangjianzhong
* @date 2024/03/05
*/
@PostMapping(value = "/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ApiOperation(value = "上传文件", notes = "上传文件")
@LogAnnotation(operationType = LogOperateTypeEnum.ADD, operateContent = "上传文件")
public Result<?> pageData(@Validated UploadFileDto dto){
return fileService.saveFile(dto);
}
}
Service层
代码有些臃肿,有时间再简化下,本质就是:
将文件转换为字节,再用输出流输出下就可以了
package com.bbzd.business.file.service.impl;
import com.bbzd.business.bsi.audao.MbgBsiParamMapper;
import com.bbzd.business.bsi.model.auGened.MbgBsiParam;
import com.bbzd.business.file.audao.MbgBsiFileGroupMapper;
import com.bbzd.business.file.audao.MbgBsiFileMapper;
import com.bbzd.business.file.entity.fileService.dto.UploadFileDto;
import com.bbzd.business.file.model.auGened.MbgBsiFile;
import com.bbzd.business.file.model.auGened.MbgBsiFileExample;
import com.bbzd.business.file.model.auGened.MbgBsiFileGroup;
import com.bbzd.business.file.model.auGened.MbgBsiFileGroupExample;
import com.bbzd.business.file.service.FileService;
import com.bbzd.business.utils.DateUtils;
import com.bbzd.business.utils.FileTokenUtils;
import com.bbzd.business.utils.StringUtils;
import com.bbzd.common.base.Result;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.stream.Collectors;
/**
* Description: 文件服务实现
* Copyright: Copyright (c) 2013
* Company: www.bbzd.com
*
* @author wangjianzhong
* @version 1.0
*/
@Service
@Transactional(rollbackFor = Exception.class)
public class FileServiceImpl implements FileService {
@Value("${constant.file.saveRootPath}")
private String fileSaveRootPath;
@Value("${constant.file.commonFileDir}")
private String commonFileDir;
@Value("${constant.file.bigFileDir}")
private String bigFileDir;
private final String fileSeparator= File.separator;
@Resource
MbgBsiFileGroupMapper mbgBsiFileGroupMapper;
@Resource
MbgBsiFileMapper mbgBsiFileMapper;
@Resource
private MbgBsiParamMapper mbgBsiParamMapper;
private Set<String> suffixSet=new ConcurrentSkipListSet<>();
public FileServiceImpl(@Qualifier("mbgBsiParamMapper") MbgBsiParamMapper mbgBsiParamMapper) {
MbgBsiParam param = mbgBsiParamMapper.selectByPrimaryKey("file_bigFile_suffix");
if(param!=null&¶m.getValue()!=null){
String[] suffixArr=param.getValue().split("\\|");
Collections.addAll(suffixSet,suffixArr);
}
}
@Scheduled(cron="0 */1 * * * ?")
public void fresh(){
MbgBsiParam param = mbgBsiParamMapper.selectByPrimaryKey("file_bigFile_suffix");
if(param!=null&¶m.getValue()!=null){
suffixSet.clear();
String[] suffixArr=param.getValue().split("\\|");
Collections.addAll(suffixSet,suffixArr);
}
}
/**
* 存储文件
*
* @param dto 输入参数
* @return 结果
*/
@Override
public Result saveFile(UploadFileDto dto) {
//检查输入参数
String groupToken= dto.getToken();
MbgBsiFileGroup fileGroup=null;
MbgBsiFileGroupExample fileGroupExample=new MbgBsiFileGroupExample();
MbgBsiFileGroupExample.Criteria fileGroupCriteria=fileGroupExample.createCriteria();
fileGroupCriteria.andGroupNoEqualTo(groupToken);
List<MbgBsiFileGroup> fileGroupList=mbgBsiFileGroupMapper.selectByExample(fileGroupExample);
if(!CollectionUtils.isEmpty(fileGroupList)){
fileGroup=fileGroupList.get(0);
}
if(fileGroup==null){
return Result.fail("无效令牌,禁止操作");
}
MultipartFile file=dto.getFile();
if(file.isEmpty()){
return Result.fail("文件为空,禁止操作");
}
System.out.println("文件名1:"+file.getName());
System.out.println("文件名2:"+file.getOriginalFilename());
System.out.println("文件名类型:"+file.getContentType());
System.out.println("文件大小:"+file.getSize());
//检查文件空间大小是否满足
MbgBsiFileExample fileExample=new MbgBsiFileExample();
MbgBsiFileExample.Criteria fileCriteria=fileExample.createCriteria();
fileCriteria.andGroupNoEqualTo(fileGroup.getGroupNo());
List<MbgBsiFile> fileList=mbgBsiFileMapper.selectByExample(fileExample);
long fileSizeSum=0L;
if(!CollectionUtils.isEmpty(fileList)){
fileSizeSum=fileList
.stream()
.mapToLong(MbgBsiFile::getSize).sum();
}
long currFileSize=fileSizeSum+file.getSize();
if(currFileSize>fileGroup.getMaxFileSize()){
return Result.fail("文件组空间不够容纳本次上传文件,禁止操作");
}
String suffix=StringUtils.getFileSuffix(file.getOriginalFilename());
String dirTypePath="";
if(suffixSet.contains(suffix.toUpperCase())){
dirTypePath=bigFileDir;
}else{
dirTypePath=commonFileDir;
}
Date now=new Date();
String dateDir=
DateUtils.getYearStr(now)+fileSeparator+
DateUtils.getMonthStr(now)+fileSeparator+
DateUtils.getDateStr(now);
String dirPath=fileSaveRootPath+fileSeparator+dirTypePath+fileSeparator+dateDir;
File dir=new File(dirPath);
if(!dir.exists()){
dir.mkdirs();
}
String fileName=file.getOriginalFilename();
String filePath=dirPath+fileSeparator+fileName;
boolean f=false;
FileOutputStream out=null;
try {
out=new FileOutputStream(filePath);
out.write(file.getBytes());
out.flush();
f=true;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
if(out!=null){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
if(f){
MbgBsiFile mbgBsiFile=new MbgBsiFile();
mbgBsiFile.setFileNo(FileTokenUtils.getFileGroupToken());
mbgBsiFile.setFileId(FileTokenUtils.getFileGroupToken());
mbgBsiFile.setPath(filePath);
mbgBsiFile.setEn(1);
mbgBsiFile.setFileName(file.getOriginalFilename());
mbgBsiFile.setVersion(1);
mbgBsiFile.setUserCode(dto.getUserCode());
mbgBsiFile.setUserName(dto.getUserName());
mbgBsiFile.setDateTime(now);
mbgBsiFile.setUrl("http://XXXXXXXXXXXXX");
mbgBsiFile.setGroupNo(fileGroup.getGroupNo());
mbgBsiFile.setSuffix(suffix);
mbgBsiFile.setSearchIdentifier(dto.getSearchIdentifier());
mbgBsiFile.setSize(file.getSize());
mbgBsiFile.setRemark(dto.getRemark());
mbgBsiFileMapper.insertSelective(mbgBsiFile);
MbgBsiFileGroup udpFileGroup=new MbgBsiFileGroup();
udpFileGroup.setId(fileGroup.getId());
udpFileGroup.setCurrFileSize(currFileSize);
mbgBsiFileGroupMapper.updateByPrimaryKeySelective(udpFileGroup);
}
return Result.success("OK");
}
}
前端测试
实用工具模拟前端发起请求,工具PostMan
比较简单,截图备忘,记录些要点吧:
请求类型 POST
参数类型 form-data
参数名要与dto中的参数名一致,参数类型是file
SpringBoot 上传文件大小限制问题及处理
原理:
SpringBoot默认限制单个上传文件的大小为10MB。如果不在项目中作特殊配置的话,当上传的文件超过这个大小时就会报错。
报错示例:
2024-03-07 10:47:35.167 ERROR 44368 --- [nio-8530-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.multipart.MaxUploadSizeExceededException: Maximum upload size exceeded; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException: the request was rejected because its size (40487302) exceeds the configured maximum (10485760)] with root cause
两种解决办法:
1.在配置文件中添加对上传文件大小的配置
spring:
servlet:
multipart:
max-file-size: 500MB
max-request-size: 500MB
maxFileSize 是单个文件大小
maxRequestSize 是设置总上传的数据大小
只能是MB和KB两种类型,字母大小写随意,Long类型可以的
参数说明:
# 是否开启文件上传,默认true
spring.servlet.multipart.enabled=true
# 写入磁盘的阈值,默认0
spring.servlet.multipart.file-size-threshold=0
# 上传文件的临时保存位置
spring.servlet.multipart.location=E:\\Gitee\\my-work-space\\chapter01\\tmp
# 单文件上传大小限制
spring.servlet.multipart.max-file-size=1MB
# 多文件上传大小限制
spring.servlet.multipart.max-request-size=10MB
# 文件是否延迟解析,默认false
# 当前文件和参数被访问时是否再解析成文件
spring.servlet.multipart.resolve-lazily=false
- 在启动文件中配置上传文件的大小
/**
* 配置上传文件的最大值
* @return
*/
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
//单个文件最大
factory.setMaxFileSize(DataSize.ofBytes(200*1024*1024));
//设置总上传数据总大小
factory.setMaxRequestSize(DataSize.ofBytes(200*1024*1024));
return factory.createMultipartConfig();
}