標籤:上傳.下載.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);
@Resource
private 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-Sheet1
WriteSheet writeSheet1 = EasyExcel.writerSheet(0,"分頁1").head(DataVO.class).build();
// Excel-Sheet2
WriteSheet writeSheet2 = EasyExcel.writerSheet(1,"分頁2").head(DataVO.class).build();
// Excel-Sheet3,寫兩個Table
WriteSheet 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> {
/**
* 接收解析的數據塊
*/
@Override
public void invoke(DataVO data, AnalysisContext context) {
System.out.println("DataListener:"+data);
}
/**
* 接收解析的表頭
*/
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
System.out.println("DataListener:"+headMap);
}
@Override
public 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