标签:上传.下载.Excel.导入.导出;
一、简介
在项目中,文件管理是常见的复杂功能;
首先文件的类型比较多样,处理起来比较复杂,其次文件涉及大量的IO操作,容易引发内存溢出;
不同的文件类型有不同的应用场景;
比如:图片常用于头像和证明材料;Excel偏向业务数据导入导出;CSV偏向技术层面数据搬运;PDF和Word用于文档类的材料保存等;
下面的案例只围绕普通文件和Excel两种类型进行代码实现;
二、工程搭建
1、工程结构
2、依赖管理
普通文件的上传下载,依赖spring-boot
框架即可,而Excel类型选择easyexcel
组件,该组件内部依赖了apache-poi
组件的4.1.2
版本;
<!-- 基础框架组件 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>${spring-boot.version}</version>
</dependency><!-- Excel组件 -->
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>${easyexcel.version}</version><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId></exclusion></exclusions>
</dependency>
三、上传下载
1、配置管理
在配置文件中,添加max-file-size
单个文件大小限制和max-request-size
请求最大限制两个核心参数;
需要说明的一点是:如何设定参数值的大小,与业务场景和服务器的处理能力都有关系,在测试的过程中优化即可;
spring:# 文件配置servlet:multipart:enabled: true# 文件单个限制max-file-size: 10MB# 请求最大限制max-request-size: 20MB
2、上传下载
这里提供一个文件批量上传接口和一个文件下载接口,把文件管理在工程中的resources/file
目录下,下载接口中需要指定该目录下的文件名称;
@RestController
public class FileWeb {private static final Logger logger = LoggerFactory.getLogger(FileWeb.class);@Resourceprivate FileService fileService ;/*** 文件上传*/@PostMapping("/file/upload")public String upload (HttpServletRequest request,@RequestParam("file") MultipartFile[] fileList) throws Exception {String uploadUser = request.getParameter("uploadUser");if (uploadUser.isEmpty()){return "upload-user is empty";}logger.info("upload-user:{}",uploadUser);for (MultipartFile multipartFile : fileList) {// 解析文件信息和保存fileService.dealFile(multipartFile);}return "success" ;}/*** 文件下载*/@GetMapping("/file/download")public void upload (@RequestParam("fileName") String fileName,HttpServletResponse response) throws Exception {if (!fileName.isBlank()){String filePath = ResourceUtils.getURL("m1-04-boot-file/src/main/resources/file").getPath();File file = new File(filePath,fileName) ;response.setHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));response.setContentType("application/octet-stream");Files.copy(Paths.get(file.getPath()), response.getOutputStream());}}
}/*** 文件服务类*/
@Service
public class FileService {private static final Logger logger = LoggerFactory.getLogger(FileService.class);public void dealFile (MultipartFile multipartFile) throws Exception {logger.info("Name >> {}",multipartFile.getName());logger.info("OriginalFilename >> {}",multipartFile.getOriginalFilename());logger.info("ContentType >> {}",multipartFile.getContentType());logger.info("Size >> {}",multipartFile.getSize());// 文件输出地址String filePath = ResourceUtils.getURL("m1-04-boot-file/src/main/resources/file").getPath();File writeFile = new File(filePath, multipartFile.getOriginalFilename());multipartFile.transferTo(writeFile);}
}
使用Postman测试文件批量上传接口:
四、Excel文件
1、Excel创建
基于easyexcel
组件中封装的EasyExcel
工具类,继承自EasyExcelFactory
工厂类,实现Excel单个或多个Sheet
的创建,并且在单个Sheet
中写多个Table
数据表;
@Service
public class ExcelService {/*** Excel-写单个Sheet*/public static void writeSheet () throws Exception {// 文件处理String basePath = getAbsolutePath();File file = new File(basePath+"/easy-excel-01.xlsx") ;checkOrCreateFile(file);// 执行写操作EasyExcel.write(file).head(DataVO.class).sheet(0,"用户信息").doWrite(DataVO.getSheet1List());}/*** Excel-写多个Sheet*/public static void writeSheets () throws Exception {// 文件处理String basePath = getAbsolutePath();File file = new File(basePath+"/easy-excel-02.xlsx") ;checkOrCreateFile(file);ExcelWriter excelWriter = null;try {excelWriter = EasyExcel.write(file).build();// Excel-Sheet1WriteSheet writeSheet1 = EasyExcel.writerSheet(0,"分页1").head(DataVO.class).build();// Excel-Sheet2WriteSheet writeSheet2 = EasyExcel.writerSheet(1,"分页2").head(DataVO.class).build();// Excel-Sheet3,写两个TableWriteSheet writeSheet3 = EasyExcel.writerSheet(2,"分页3").build();WriteTable dataTable = EasyExcel.writerTable(0).head(DataVO.class).build();WriteTable dataExtTable = EasyExcel.writerTable(1).head(DataExtVO.class).build();// 执行写操作excelWriter.write(DataVO.getSheet1List(), writeSheet1);excelWriter.write(DataVO.getSheet2List(), writeSheet2);excelWriter.write(DataVO.getSheet1List(),writeSheet3,dataTable) ;excelWriter.write(DataExtVO.getSheetList(),writeSheet3,dataExtTable) ;} catch (Exception e){e.printStackTrace();} finally {if (excelWriter != null){excelWriter.close();}}}
}/*** 实体类,这里的注解会解析为Excel中的表头*/
public class DataVO {@ExcelProperty("编号")private Integer id ;@ExcelProperty("名称")private String name ;@ExcelProperty("手机号")private String phone ;@ExcelProperty("城市")private String cityName ;@ExcelProperty("日期")private Date date ;
}
文件效果:
2、Excel读取
对于读取Excel文件来说,则需要根据具体的样式来定了,在easyexcel
组件中还可以添加读取过程的监听器;
@Service
public class ExcelService {/*** Excel-读取数据*/public static void readExcel () throws Exception {// 文件处理String basePath = getAbsolutePath();File file = new File(basePath+"/easy-excel-01.xlsx") ;if (!file.exists()){return ;}// 读取数据List<DataVO> dataList = EasyExcel.read(file).head(DataVO.class).sheet(0).headRowNumber(1).doReadSync();dataList.forEach(System.out::println);}/*** Excel-读取数据使用解析监听器*/public static void readExcelListener () throws Exception {// 文件处理String basePath = getAbsolutePath();File file = new File(basePath+"/easy-excel-01.xlsx") ;if (!file.exists()){return ;}// 读取数据,并且使用解析监听器DataListener dataListener = new DataListener() ;List<DataVO> dataSheetList = EasyExcel.read(file,dataListener).head(DataVO.class).sheet(0).headRowNumber(1).doReadSync();dataSheetList.forEach(System.out::println);}
}
3、解析监听
继承AnalysisEventListener
类,并重写其中的方法,可以监听Excel的解析过程,或者添加一些自定义的处理逻辑;
public class DataListener extends AnalysisEventListener<DataVO> {/*** 接收解析的数据块*/@Overridepublic void invoke(DataVO data, AnalysisContext context) {System.out.println("DataListener:"+data);}/*** 接收解析的表头*/@Overridepublic void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {System.out.println("DataListener:"+headMap);}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {System.out.println("DataListener:after...all...analysed");}
}
4、导入导出
实际上Excel文件的导入导出,原理与文件的上传下载类似,只不过这里使用easyexcel
组件中的API来直接处理Excel的写和读;
@RestController
public class ExcelWeb {@GetMapping("excel/download")public void download(HttpServletResponse response) throws IOException {response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");String fileName = URLEncoder.encode("Excel数据", StandardCharsets.UTF_8).replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");EasyExcel.write(response.getOutputStream(), DataVO.class).sheet("用户").doWrite(DataVO.getSheet1List());}@ResponseBody@PostMapping("excel/upload")public String upload(@RequestParam("file") MultipartFile file) throws IOException {List<DataVO> dataList = EasyExcel.read(file.getInputStream(), DataVO.class, new DataListener()).sheet().doReadSync();dataList.forEach(System.out::println);return "success";}
}
使用Postman测试单个Excel上传接口:
五、参考源码
文档仓库:
https://gitee.com/cicadasmile/butte-java-note源码仓库:
https://gitee.com/cicadasmile/butte-spring-parent