SpringBoot3文件管理

標籤:上傳.下載.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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章