Java导入导出

news2024/9/24 12:25:00

目录

  • 前端
  • Apache POI
    • 引依赖
    • 导入
    • 导出
    • 工具类
  • EasyExcel
    • 引依赖
    • 读Excel
    • 指定列名
    • 多个Sheet
    • 使用提供的转换器或自定义格式转换器对读取到的数据进行格式转换
    • 行头即列名
    • 写Excel
    • 指定导出列
    • 复杂头写入
    • 日期、数字或自定义格式转换对导出数据进行处理
    • 自定义样式
    • 合并单元格
    • 动态列表头
    • 自定义拦截器,对单元格进行操作
    • 工具类

前端

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<title>上传与下载</title>
	</head>
	<body>
		<form action="http://10.3.121.7:8889/file/upload" method="post" enctype="multipart/form-data">
			<input type="file" name="file">
			<button type="submit">上传</button>
		</form>
		<!-- download 属性用于指示浏览器下载该文件,而不是直接在浏览器中打开。 -->
		<a href="http://10.3.121.7:8889/file/download" download>下载</a>
	</body>
</html>

在接收文件时可用的参数名与 HTML 表单中 input 标签的 name 属性相同。如果不指定 name 属性,将默认使用 file 作为参数名。

Apache POI

最原始的Java导入导出方法。

引依赖

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>4.1.2</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.2</version>
</dependency>

导入

@PostMapping(value = "/upload")
public Result upload(@RequestParam("file") MultipartFile file) {
    Result<Object> result = new Result<>();
    try {
        InputStream is = file.getInputStream();
        // 创建 Workbook 对象
        XSSFWorkbook workbook = new XSSFWorkbook(is);

        // 获取第一个 Sheet
        XSSFSheet sheet = workbook.getSheetAt(0);

        // 遍历数据表格
        for (org.apache.poi.ss.usermodel.Row item : sheet) {
            XSSFRow row = (XSSFRow) item;
            Iterator<Cell> cells = row.cellIterator();

            // 遍历行中的每个单元格
            while (cells.hasNext()) {
                XSSFCell cell = (XSSFCell) cells.next();
                //需要确保表格的数据都是数字,也有其他方法获取其他类型数据
                double value = cell.getNumericCellValue();
                System.out.print(value + " ");
            }
            System.out.println();
        }
        // 关闭输入流
        is.close();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    result.setSuccess(true);
    return result;
}

不使用springboot时:

HttpServletRequest request
// 获取上传文件
Part filePart = request.getPart("file");
String fileName = filePart.getSubmittedFileName();
InputStream fileContent = filePart.getInputStream();

导出

public void download(HttpServletResponse response) {
    try {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        //处理文件名乱码
        String fileName = URLEncoder.encode("test.xlsx", "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        // 创建 Workbook 对象,即工作簿Excel文件
        XSSFWorkbook workbook = new XSSFWorkbook();

        // 创建 Sheet 对象
        XSSFSheet sheet = workbook.createSheet("test");

        // 创建行对象和单元格对象
        // 第一行,即列表头
        XSSFRow row = sheet.createRow(0);
        XSSFCell cell1 = row.createCell(0);
        XSSFCell cell2 = row.createCell(1);
        XSSFCell cell3 = row.createCell(2);
        XSSFCell cell4 = row.createCell(3);


        // 设置单元格的值
        cell1.setCellValue("Hello");
        cell2.setCellValue("World");
        // 往第三个单元格塞入日期值,这需要进行格式处理
        XSSFCreationHelper helper = workbook.getCreationHelper();
        XSSFCellStyle style = workbook.createCellStyle();
        style.setDataFormat(helper.createDataFormat().getFormat("yyyy-MM-dd"));
        cell3.setCellValue(new Date());
        cell3.setCellStyle(style);
        // 第四个单元格塞入数字,保留两位小数
        XSSFCellStyle style1 = workbook.createCellStyle();
        style1.setDataFormat(HSSFDataFormat.getBuiltinFormat("0.00"));
        cell4.setCellStyle(style1);
        cell4.setCellValue(3.1415d);

        // 输出数据到文件
        ServletOutputStream outputStream = response.getOutputStream();
        workbook.write(outputStream);
        outputStream.close();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

工具类

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Component;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

@Component
public class ExcelUtils {

    /**
     * 将指定类型的对象列表导出为 Excel 文件
     *
     * @param list     对象列表
     * @param clazz    对象类型
     * @param response 获取输出流
     * @throws IOException IO 异常
     */
    public static <T> void toExcel(List<T> list, Class<T> clazz, HttpServletResponse response) throws IOException {
        // 创建 Excel 工作簿并写入数据
        Workbook workbook = new XSSFWorkbook();
        writeHeader(workbook, clazz);
        writeBody(workbook, clazz, list);

        // 将 Excel 工作簿写入输出流
        ServletOutputStream out = response.getOutputStream();
        workbook.write(out);
    }

    /**
     * 写入 Excel 表头
     *
     * @param workbook Excel 工作簿
     * @param clazz    对象类型
     */
    public static void writeHeader(Workbook workbook, Class<?> clazz) {
        Sheet sheet = workbook.createSheet();
        Row headerRow = sheet.createRow(0);
        List<Field> fields = getFields(clazz);
        for (int i = 0; i < fields.size(); i++) {
            Field field = fields.get(i);
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(field.getName());
        }
    }

    /**
     * 写入 Excel 数据
     *
     * @param workbook Excel 工作簿
     * @param clazz    对象类型
     * @param list     对象列表
     */
    public static <T> void writeBody(Workbook workbook, Class<T> clazz, List<T> list) {
        Sheet sheet = workbook.getSheetAt(0);
        List<Field> fields = getFields(clazz);
        for (int i = 0; i < list.size(); i++) {
            Row row = sheet.createRow(i + 1);
            T obj = list.get(i);
            for (int j = 0; j < fields.size(); j++) {
                Field field = fields.get(j);
                field.setAccessible(true);
                try {
                    Object value = field.get(obj);
                    if (value != null) {
                        Cell cell = row.createCell(j);
                        cell.setCellValue(value.toString());
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 获取对象的所有字段,包括父类的字段
     *
     * @param clazz 对象类型
     * @return 对象的所有字段
     */
//    public static List<Field> getFields(Class<?> clazz) {
//        List<Field> fields = new ArrayList<>();
//        while (clazz != null) {
//            fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
//            clazz = clazz.getSuperclass();
//        }
//        return fields;
//    }

    /**
     * 将输入流中的 Excel 文件解析为指定类型的对象列表
     *
     * @param inputStream 输入流
     * @param clazz       对象类型
     * @return 对象列表
     * @throws IOException IO 异常
     * @throws Exception   Excel 解析异常
     */
    public static <T> List<T> readExcel(InputStream inputStream, Class<T> clazz) throws IOException, Exception {
        // 读取 Excel 工作簿
        Workbook workbook = WorkbookFactory.create(inputStream);
        if (workbook.getNumberOfSheets() < 1) {
            throw new Exception("Excel 文件中没有工作表");
        }
        // 解析 Excel 工作表
        List<String> header = getHeader(workbook.getSheetAt(0).getRow(0));
        List<Field> fields = getFields(clazz);
        List<T> list = new ArrayList<>();
        for (int i = 1; i <= workbook.getSheetAt(0).getLastRowNum(); i++) {
            T obj = newInstance(clazz);
            for (int j = 0; j < header.size(); j++) {
                String fieldName = header.get(j);
                Field field = fields.stream()
                        .filter(f -> f.getName().equalsIgnoreCase(fieldName))
                        .findFirst()
                        .orElse(null);
                if (field != null) {
                    Class<?> fieldType = field.getType();
                    Object cellValue = getCellValue(workbook.getSheetAt(0).getRow(i).getCell(j), fieldType);
                    setFieldValue(obj, field, cellValue);
                }
            }
            list.add(obj);
        }
        return list;
    }

    // 获取 Excel 表头
    private static List<String> getHeader(Row row) {
        List<String> header = new ArrayList<>();
        for (Cell cell : row) {
            header.add(cell.getStringCellValue());
        }
        return header;
    }

    // 获取实体类的所有属性
    private static List<Field> getFields(Class<?> clazz) {
        List<Field> fields = new ArrayList<>();
        Class<?> superClazz = clazz.getSuperclass();
        if (superClazz != null) {
            fields.addAll(getFields(superClazz));
        }
        fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
        return fields;
    }

    // 根据类型获取单元格的值
    private static Object getCellValue(Cell cell, Class<?> targetType) {
        if (cell == null) {
            return null;
        } else if (targetType == String.class) {
            return cell.getStringCellValue();
        } else if (targetType == Integer.class) {
            return (int) cell.getNumericCellValue();
        } else if (targetType == Double.class) {
            return cell.getNumericCellValue();
        } else if (targetType == Boolean.class) {
            return cell.getBooleanCellValue();
        } else if (targetType == Date.class) {
            return cell.getDateCellValue();
        } else if (targetType == byte[].class) {
            // TODO: 处理二进制文件单元格的值
            return null;
        } else {
            return null;
        }
    }

    // 设置实体类对象的属性值
    private static void setFieldValue(Object obj, Field field, Object value) throws IllegalAccessException {
        field.setAccessible(true);
        field.set(obj, value);
    }

    // 创建指定类型的新实例
    private static <T> T newInstance(Class<T> clazz) throws Exception {
        try {
            return clazz.newInstance();
        } catch (InstantiationException | IllegalAccessException ex) {
            throw new Exception("无法创建新的 " + clazz.getName() + " 实例", ex);
        }
    }
}

EasyExcel

EasyExcel 是一款基于阿里巴巴的 EasyPOI 开源项目封装的 Java Excel 读写库,它具有轻量、易用和高性能的特点。

引依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.2.10</version>
</dependency>

读Excel

读数据需要一个监听器

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
public class ExcelListener<T> extends AnalysisEventListener<T> {
    private final List<T> rows = new ArrayList<>();
    private final List<String> head = new ArrayList<>();

    @Override
    public void invoke(T object, AnalysisContext context) {
        rows.add(object);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        log.info("read {} rows", rows.size());
    }

    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        head.addAll(headMap.values());
    }

    public List<T> getRows() {
        return rows;
    }

    public List<String> getHead() {
        return head;
    }

}
try {
    InputStream inputStream = file.getInputStream();
    ExcelListener<Object> listener = new ExcelListener<>();
    ExcelReaderBuilder read = EasyExcel.read(inputStream, listener);
    read.doReadAll();
    //数据
    System.out.println(listener.getRows());
    //列名
    System.out.println(listener.getHead());
} catch (IOException e) {
    throw new RuntimeException(e);
}

指定列名

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

import java.io.Serializable;

@Data
public class Task implements Serializable {

    private static final long serialVersionUID = 1L;

    @ExcelProperty(value = "*车架号")
    private String vin;

    @ExcelProperty(value = "*任务信息")
    private String taskInfo;
}

使用注解ExcelProperty

try {
    InputStream inputStream = file.getInputStream();
    ExcelListener<Task> listener = new ExcelListener<>();
    EasyExcel.read(inputStream, Task.class, listener).sheet().doRead();
    List<Task> rows = listener.getRows();
    System.out.println(rows);
} catch (IOException e) {
    throw new RuntimeException(e);
}
try {
    InputStream inputStream = file.getInputStream();
    ExcelListener<Task> listener = new ExcelListener<>();
    ExcelReader reader = EasyExcel.read(inputStream, Task.class, listener).build();
    ReadSheet sheet = EasyExcel.readSheet().build();
    reader.read(sheet);
    List<Task> rows = listener.getRows();
    System.out.println(rows);
} catch (IOException e) {
    throw new RuntimeException(e);
}

多个Sheet

try {
    InputStream inputStream = file.getInputStream();
    ExcelListener<Task> listener = new ExcelListener<>();
    ExcelReader reader = EasyExcel.read(inputStream, Task.class, listener).build();
    List<ReadSheet> readSheets = reader.excelExecutor().sheetList(); //文件里Sheet的信息
    /*
    [ReadSheet{sheetNo=0, sheetName='Sheet1'} com.alibaba.excel.read.metadata.ReadSheet@4aee277, 
    ReadSheet{sheetNo=1, sheetName='Sheet2'} com.alibaba.excel.read.metadata.ReadSheet@57915590, 
    ReadSheet{sheetNo=2, sheetName='Sheet3'} com.alibaba.excel.read.metadata.ReadSheet@6f73f2d9]
     */
    System.out.println(readSheets);
    System.out.println(readSheets.size()); //3
    ReadSheet sheet = EasyExcel.readSheet(0).build();
    ReadSheet sheet1 = EasyExcel.readSheet(1).build();
    reader.read(sheet, sheet1);
    List<Task> rows = listener.getRows();
    result.setResult(rows);
} catch (IOException e) {
    throw new RuntimeException(e);
}
try {
    InputStream inputStream = file.getInputStream();
    ExcelListener<Task> listener = new ExcelListener<>();
    EasyExcel.read(inputStream, Task.class, listener).doReadAll();
    result.setResult(listener.getRows());
} catch (IOException e) {
    throw new RuntimeException(e);
}

注:ExcelListener的doAfterAllAnalysed 方法会在每个sheet读取完毕后调用一次。然后所有sheet读完后都会往同一个ExcelListener里面写,即最后所有的数据都会汇总到一个ExcelListener对象里。

当在EasyExcel的read方法中确定了实体类时,如上面的Task实体类,那么就会将Sheet列名和实体类里的ExcelProperty注解的value值匹配上,如果Sheet里没有这两列或者两列下没有值但是其他列有值那么返回的数据依然没有值。

监听器invoke方法读取到的数据是默认从第一行开始的。对应的就是Sheet的第2行。也就是该方法不触碰列名那一行。

使用提供的转换器或自定义格式转换器对读取到的数据进行格式转换

行头即列名

当Excel没有列名或者有多行列名可以使用这个办法来说明。

EasyExcel.read(inputStream, listener).sheet().headRowNumber(0).doRead();

headRowNumber方法用于指定行头有几行参数有:

  • 0:表示没有行头,即第一行就是数据
  • 1:默认值就是1,表示第一行是行头,从第2行开始是数据
  • 2:表示行头有两行,从第3行开始是数据

如果想要读取行头,上面的监听器invokeHeadMap方法保存了行头的数据。

写Excel

使用ExcelProperty注解指定写的列名,ExcelIgnore注解让对象的某属性不写。

@GetMapping(value = "/download")
public void download(HttpServletResponse response) {
    try {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("UTF-8");
        String fileName = URLEncoder.encode("测试.xlsx", "UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
        ServletOutputStream outputStream = response.getOutputStream();
        ArrayList<Task> tasks = new ArrayList<>();
        Task task = new Task();
        task.setVin("123");
        task.setTaskInfo("123");
        tasks.add(task);
        EasyExcel.write(outputStream, Task.class).sheet("Sheet").doWrite(tasks);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}
ExcelWriter excelWriter = EasyExcel.write(outputStream, Task.class).build();
WriteSheet sheet = EasyExcel.writerSheet("Sheet").build();
excelWriter.write(tasks, sheet);
excelWriter.finish();

不使用注解的话就使用head方法指定列表头。

指定导出列

使用注解。或者:

EasyExcel.write().excludeColumnFiledNames(List)
EasyExcel.write().includeColumnFiledNames(List)

ExcelProperty注解的index属性可以指定这一列在Excel中的位置。数字从0开始。

复杂头写入

例如:
在这里插入图片描述
只需要使用注解ExcelProperty

@ExcelProperty(value = {"任务", "*车架号"})
private String vin;

@ExcelProperty(value = {"任务", "*任务信息"})
private String taskInfo;

日期、数字或自定义格式转换对导出数据进行处理

自定义样式

例:@NumberFormat@DateTimeFormat

try {
    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    response.setCharacterEncoding("UTF-8");
    String fileName = URLEncoder.encode("测试.xlsx", "UTF-8");
    response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
    ServletOutputStream outputStream = response.getOutputStream();
    ArrayList<Task> tasks = new ArrayList<>();
    Task task = new Task();
    task.setVin("123");
    task.setTaskInfo("123");
    tasks.add(task);
    //头的策略
    WriteCellStyle headWriteCellStyle = new WriteCellStyle();
    // 背景设置为红色
    headWriteCellStyle.setFillForegroundColor(IndexedColors.BLACK.getIndex());
    WriteFont headWriteFont = new WriteFont();
    headWriteFont.setFontHeightInPoints((short) 20);
    headWriteFont.setColor(IndexedColors.RED.getIndex());
    headWriteCellStyle.setWriteFont(headWriteFont);
    // 内容的策略
    WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
    // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定
    contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
    // 背景绿色
    contentWriteCellStyle.setFillForegroundColor(IndexedColors.BLUE.getIndex());
    WriteFont contentWriteFont = new WriteFont();
    // 字体大小
    contentWriteFont.setFontHeightInPoints((short) 20);
    contentWriteCellStyle.setWriteFont(contentWriteFont);
    // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
    HorizontalCellStyleStrategy horizontalCellStyleStrategy =
            new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    ExcelWriter excelWriter = EasyExcel.write(outputStream, Task.class).registerWriteHandler(horizontalCellStyleStrategy).build();
    WriteSheet sheet = EasyExcel.writerSheet("Sheet").build();
    excelWriter.write(tasks, sheet);
    excelWriter.finish();
} catch (IOException e) {
    throw new RuntimeException(e);
}

合并单元格

// 每隔2行会合并 把eachColumn 设置成 3 也就是我们数据的长度,所以就第一列会合并。当然其他合并策略也可以自己写
LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 0);

动态列表头

//列表头
List<List<String>> headList = new ArrayList<>();
//因为可能会有复杂列表头所以每一列的头都是一个List
List<String> head1 = new ArrayList<>();
head1.add("第一行头");
head1.add("第二行头");
headList.add(head1);
EasyExcel.write(outputStream).head(headList).sheet("Sheet").doWrite(tasks);

在这里插入图片描述

自定义拦截器,对单元格进行操作

/**
 * 自定义拦截器。对第一行第一列的头超链接到:https://github.com/alibaba/easyexcel
 */
public class CustomCellWriteHandler implements CellWriteHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(CustomCellWriteHandler.class);

    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
        Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {

    }

    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell,
        Head head, Integer relativeRowIndex, Boolean isHead) {

    }

    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
        List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        // 这里可以对cell进行任何操作
        LOGGER.info("第{}行,第{}列写入完成。", cell.getRowIndex(), cell.getColumnIndex());
        if (isHead && cell.getColumnIndex() == 0) {
            CreationHelper createHelper = writeSheetHolder.getSheet().getWorkbook().getCreationHelper();
            Hyperlink hyperlink = createHelper.createHyperlink(HyperlinkType.URL);
            hyperlink.setAddress("https://github.com/alibaba/easyexcel");
            cell.setHyperlink(hyperlink);
        }
    }
}
/**
 * 自定义拦截器.对第一列第一行和第二行的数据新增下拉框,显示 测试1 测试2
 */
public class CustomSheetWriteHandler implements SheetWriteHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(CustomSheetWriteHandler.class);

    @Override
    public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {

    }

    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        LOGGER.info("第{}个Sheet写入成功。", writeSheetHolder.getSheetNo());

        // 区间设置 第一列第一行和第二行的数据。由于第一行是头,所以第一、二行的数据实际上是第二三行
        CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(1, 2, 0, 0);
        DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper();
        DataValidationConstraint constraint = helper.createExplicitListConstraint(new String[] {"测试1", "测试2"});
        DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList);
        writeSheetHolder.getSheet().addValidationData(dataValidation);
    }
}

使用registerWriteHandler方法将上面的自定义拦截器注册即可。

工具类

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.byd.demo.config.ExcelListener;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Component
public class ExcelUtils {

    /**
     * 从 excel 文件中读取数据
     *
     * @param inputStream 文件输入流
     * @param sheetNo     工作表序号,从 0 开始
     * @param clazz       数据对应的类类型
     * @return 读取的数据列表
     */
    public static List<Object> readExcel(InputStream inputStream, int sheetNo,
                                         Class<?> clazz) {
        ExcelListener<Object> listener = new ExcelListener<>();
        ExcelReaderBuilder readerBuilder = EasyExcel.read(inputStream, clazz, listener);
        readerBuilder.sheet(sheetNo).doRead();
        return listener.getRows();
    }

    /**
     * 将数据写入 excel 文件并下载
     *
     * @param response  HttpServletResponse 对象
     * @param fileName  下载的文件名(不包含后缀)
     * @param sheetName 工作表名称
     * @param dataList  数据列表
     * @throws IOException IO 异常
     */
    public static void writeExcel(HttpServletResponse response, String fileName, String sheetName,
                                  List<?> dataList) throws IOException {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        //处理文件名乱码
        fileName = URLEncoder.encode(fileName, "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");

        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet(sheetName);

        // 写入表头
        int rowIndex = 0;
        Row headerRow = sheet.createRow(rowIndex++);
        buildHeader(headerRow, dataList.get(0).getClass());

        // 写入数据
        for (Object data : dataList) {
            Row row = sheet.createRow(rowIndex++);
            buildRow(row, data);
        }

        // 输出到 HttpServletResponse
        OutputStream outputStream = response.getOutputStream();
        ExcelWriterBuilder writerBuilder = EasyExcel.write(outputStream);
        writerBuilder.sheet(sheetName).doWrite(dataList);

        workbook.write(outputStream);
        outputStream.flush();
        outputStream.close();
    }

    /**
     * 从 MultipartFile 中读取数据
     *
     * @param file    MultipartFile 对象
     * @param sheetNo 工作表序号,从 0 开始
     * @param clazz   数据对应的类类型
     * @return 读取的数据列表
     * @throws IOException IO 异常
     */
    public static List<Object> readExcel(MultipartFile file, int sheetNo,
                                         Class<?> clazz) throws IOException {
        InputStream inputStream = file.getInputStream();
        return readExcel(inputStream, sheetNo, clazz);
    }

    /**
     * 构建表头行
     *
     * @param headerRow 表头行对象
     * @param clazz     数据类型
     */
    private static void buildHeader(Row headerRow, Class<?> clazz) {
        List<Field> fields = getAllFields(clazz);
        int cellIndex = 0;
        for (Field field : fields) {
            ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
            if (excelProperty != null) {
                Cell cell = headerRow.createCell(cellIndex++);
                cell.setCellValue(excelProperty.value()[0]);
            }
        }
    }

    /**
     * 构建数据行
     *
     * @param row  行对象
     * @param data 数据对象
     */
    private static void buildRow(Row row, Object data) {
        List<Field> fields = getAllFields(data.getClass());
        int cellIndex = 0;
        for (Field field : fields) {
            field.setAccessible(true);
            ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
            if (excelProperty != null) {
                try {
                    Object value = field.get(data);
                    Cell cell = row.createCell(cellIndex++);
                    setCellValue(cell, value);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 获取类及其父类的所有字段(不包括Object类)
     *
     * @param clazz 类类型
     * @return 所有字段列表
     */
    private static List<Field> getAllFields(Class<?> clazz) {
        List<Field> fields = new ArrayList<>();
        while (clazz != null && !clazz.equals(Object.class)) {
            fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
            clazz = clazz.getSuperclass();
        }
        return fields;
    }

    /**
     * 设置单元格的值
     *
     * @param cell  单元格对象
     * @param value 值对象
     */
    private static void setCellValue(Cell cell, Object value) {
        if (value == null) {
            cell.setCellValue("");
            return;
        }

        if (value instanceof Number) {
            cell.setCellValue(((Number) value).doubleValue());
        } else if (value instanceof Boolean) {
            cell.setCellValue((Boolean) value);
        } else if (value instanceof RichTextString) {
            cell.setCellValue((RichTextString) value);
        } else {
            cell.setCellValue(value.toString());
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/430921.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关于el-input-number 计数器 只能点击一次,之后点击没有效果

哈喽 大家好啊&#xff0c;今天使用计数器发现&#xff0c;点击一次了后就显示不了了 这里怎么点击都点击不了&#xff0c;只能点击一次 然后我发现我代码是这样写的&#xff1a; gitem是一个对象 gitem.saleNum gitem.reduceQuantity || 1;// 剩余可以编辑的数量 因为我这…

【SSL】ssl证书简介、ssl证书生成工具与ssl证书生成步骤

ssl证书简介、ssl证书生成工具与ssl证书生成步骤一、ssl证书是什么&#xff1f;二、ssl证书生成工具有哪些&#xff1f;2.1、工具一&#xff1a;CFSSL2.2、工具二&#xff1a;OpenSSL2.3、工具三&#xff1a;XCA三、ssl证书有什么用&#xff1f;四、ssl证书生成步骤4.1 步骤1&a…

01-SpringBoot入门应用

入门程序&#xff1a;使用SpringBoot开发一个web应用&#xff0c;浏览器发起请求/hello后&#xff0c;给浏览器返回字符串“Hello World ~” 1. 创建springboot工程&#xff0c;并勾选web开发相关依赖 由于我的idea没有Spring Initializr选项&#xff0c;所以我选择使用Maven…

ChatGPT常见术语清单;大厂ChatGPT混战汇总;提示工程技巧完全指南(中译);真机会VS假机会 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; 『OpenAI/ChatGPT 高频常见词解释清单』一份速查表搞懂最热门 AI 术语 OpenAI 和 ChatGPT 的爆火&#xff0c;以超高的频率将大量AI术…

ISE的仿真库编译步骤

Modelsim10.4与ISE14.7联合仿真 1、指定modelsim的安装位置 2、编译安装仿真库 &#xff08;1&#xff09;编译仿真库 点击“开始菜单 -> Xilinx ISE Design Suite 13.2 -> ISE Design Tools -> 64-bit Tools -> Simulation Library Compilation Wizard”&#xff…

Node 会话控制

文章目录Node 会话控制概述cookie运行流程设置cookie获取cookie删除cookiesession运行流程设置session获取session删除sessionsession和cookie的区别token运行流程JWT创建token校验tokenNode 会话控制 概述 所谓会话控制就是 对会话进行控制。 HTTP 是一种无状态的协议&…

DAY 38 LINUX iptables防火墙

所谓防火墙也称之为防护墙&#xff0c;它是一种位于内部网络与外部网络之间的网络安全系统。一项信息安全的防护系统。按照给定的规则&#xff0c;允许或者限制网络报文通过 硬件防⽕墙&#xff1a;通过硬件和软件的组合&#xff0c;基于硬件的防⽕墙保护整个内部网络安全。&a…

如何使用Win10搭建我的世界Minecraft服务器

简单几步在windwos搭建我的世界服务器,并通过cpolar工具将本地服务暴露到公网连接 1. Java环境搭建 以windows10系统为例&#xff0c;配置java环境&#xff0c;搭建我的世界服务器,下载最新版java版本 Java Downloads | Oracle 选择exe文件&#xff0c;下载完成后双击安装包…

JVM程序计数器到底存储的是当前执行的指令地址,还是下一条指令的地址

JVM程序计数器到底存储的是当前执行的指令地址&#xff0c;还是下一条指令的地址&#xff1f; 说法一&#xff1a;chatgpt 程序计数器&#xff08;Program Counter&#xff0c;PC&#xff09;存储的是下一条指令的地址。在CPU执行指令时&#xff0c;程序计数器始终跟踪着当前已…

【机器人仿真Webots教程】-Webots安装

Webots安装 文章目录Webots安装1. Webots简介2. Webots安装2.1 系统要求2.2 验证显卡驱动2.3 安装3. Webots仿真3.1 world文件3.2 Controller文件3.3 Supervisor Controller4. 启动方式1. Webots简介 webots是一个开源3D移动机器人仿真框架。其为Cyberbotics公司旗下研发开源框…

Fastjson反序列化漏洞复现(实战案例)

本文转载于&#xff1a;https://blog.csdn.net/jinzezhi/article/details/124274123 漏洞介绍 FastJson在解析json的过程中&#xff0c;支持使用autoType来实例化某一个具体的类&#xff0c;并调用该类的set/get方法来访问属性。通过查找代码中相关的方法&#xff0c;即可构造出…

【Python_Opencv图像处理框架】图像基本操作+90bb5729-b33a-4e82-a0d9-faa3e5cbf621

写在前面 很幸运能选择Python语言进行学习&#xff0c;这是有关Opencv的图像处理的第一篇文章&#xff0c;讲解了有关图像处理的一些基础操作&#xff0c;作为初学者&#xff0c;我尽己所能&#xff0c;但仍会存在疏漏的地方&#xff0c;希望各位看官不吝指正❤️ 写在中间 1…

unity物体运动经过特定点并绘出轨迹

经过线如果有圆滑可以参考 Unity物体运动时画出轨迹_天人合一peng的博客-CSDN博客 并修改里面的数值轨迹会有变化 float angle Mathf.Min(1, Vector3.Distance(this.transform.position, targetPos) / distanceToTarget) * 45; this.transform.rotation this.t…

App灰度发布实现路径之小程序容器

灰度从字面意思理解就是存在于黑与白之间的一个平滑过渡的区域&#xff0c;所以说对于互联网产品来说&#xff0c;上线和未上线就是黑与白之分&#xff0c;而实现未上线功能平稳过渡的一种方式就叫做灰度发布。 灰度发布将新版本应用程序推送给一部分用户进行测试和反馈的过程…

7、ThingsBoard使用docker compose集群部署

1、概述 今天我将讲解官方文档说的使用docker compose集群部署ThingsBoard,这种部署方式也是目前企业中常用的形式,希望大家能够掌握,我不是直接使用官方的镜像,我是自己拉起代码,然后自己构建镜像,在传到服务器上,使用自己的镜像来部署。而且这种部署中间有个大坑,我…

【大数据Hadoop】HDFS3.3.1-Namenode-缓存管理

缓存管理前言缓存概念HDFS 集中式缓存有两个主要概念。缓存管理命令HDFS 集中式缓存架构CacheManager 类实现CacheReplicationMonitor前言 Hadoop 2.3.0 版本新增了集中式缓存管理&#xff08;Centralized Cache Management&#xff09;功能&#xff0c;允许用户将一些文件和目…

数字滤波器设计——FIR 滤波器

数字滤波器设计实践介绍 此示例说明如何使用 Signal Processing Toolbox 产品中的 designfilt 函数&#xff0c;根据频率响应设定设计 FIR 和 IIR 滤波器。该示例重点讲述低通滤波器&#xff0c;但大多数结果也适用于其他响应类型。 此示例主要介绍数字滤波器的设计&#xff…

MATLAB | 如何用MATLAB如何绘制各式各样精致的三元相图(ternary plot)

整了个大活&#xff0c;写了一个能够生成非常精致三元相图的函数&#xff0c;这种图主要用于展示三种变量之间的比例&#xff0c;本期实验绘制效果如下&#xff1a; 编写不易&#xff0c;这个工具写的脑壳痛&#xff0c;求多多点赞&#xff0c;依旧先介绍咋使用&#xff0c;工具…

嵌入式软件架构

总目录链接>> AutoSAR入门和实战系列总目录 总目录链接>> AutoSAR BSW高阶配置系列总目录 文章目录1 嵌入式软件分类 – BAREMETAL2 嵌入式软件分类 – 实时操作系统 (RTOS)3 嵌入式软件分类 – “通用”操作系统4 嵌入式软件分类 – 容器/微服务微服务容器5 嵌…

入行 4 年,跳槽 2 次,我摸透了软件测试这一行

最近几年行业在如火如荼的发展壮大&#xff0c;以及其他传统公司都需要大批量的软件测试人员&#xff0c;但是20年的疫情导致大规模裁员&#xff0c;让人觉得行业寒冬已来&#xff0c;软件测试人员的职业规划值得我们深度思考。 大家都比较看好软件测试行业&#xff0c;只是因为…