POI和EasyExcel---处理Excel

01 Apache POI简介

Apache POI 是一个开源的Java库,用于处理Microsoft Office格式的文档。它提供了丰富的API,允许开发者在Java应用中读取、写入和操作Excel、Word、PowerPoint等文件,是处理Office文档的常用工具。

核心功能

支持多种格式:包括经典的.xls(Excel 97-2003)、.xlsx(Excel 2007+)、.doc(Word 97-2003)、.docx(Word 2007+)、.ppt(PowerPoint)及Visio等。
跨平台性:作为纯Java库,可在任何支持Java的系统中使用。

主要组件

Excel处理
HSSF:操作.xls格式,适用于旧版Excel。
XSSF:处理.xlsx格式,支持Excel 2007+的新特性。
SXSSF:流式API,适合处理大数据量,避免内存溢出。
Word处理
HWPF:针对.doc格式,功能相对基础。
XWPF:支持.docx,提供更现代的API。
PowerPoint
HSLF:操作.ppt文件。
XSLF:处理.pptx格式。

使用场景

数据导出:将应用数据生成Excel报告。
数据导入:从上传的Excel中读取数据入库。
模板填充:用Word生成合同或账单等文档。
自动化测试:验证导出文件的内容正确性。

快速入门(以Excel为例)

Maven依赖

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

示例:创建Excel并写入数据

  1. 创建Excel工作簿

    Workbook workbook = new XSSFWorkbook(); // 新建一个.xlsx格式的Excel文件
    
  2. 创建工作表

    Sheet sheet = workbook.createSheet("员工数据"); // 添加名为“员工数据”的工作表
    
  3. 构建标题行
    创建行对象:索引从0开始,第一行用于标题。

    Row headerRow = sheet.createRow(0); // 在第1行创建行
    

    填充单元格

    headerRow.createCell(0).setCellValue("姓名"); // A1单元格写入“姓名”
    headerRow.createCell(1).setCellValue("部门"); // B1单元格写入“部门”
    
  4. 添加数据行
    创建数据行:索引1对应Excel的第二行。

    Row dataRow = sheet.createRow(1); // 在第2行创建行
    

    填充数据

    dataRow.createCell(0).setCellValue("张三"); // A2写入“张三”
    dataRow.createCell(1).setCellValue("技术部"); // B2写入“技术部”
    
  5. 保存文件
    使用try-with-resources自动关闭流

    try (FileOutputStream fos = new FileOutputStream("output.xlsx")) {
        workbook.write(fos); // 将工作簿内容写入文件
    }
    

    手动关闭工作簿

    workbook.close(); // 释放资源
    
  6. 生成结果
    • 生成名为output.xlsx的Excel文件,包含“员工数据”工作表,数据如下:

    姓名部门
    张三技术部

注意事项
文件路径output.xlsx保存在程序运行的当前目录。
异常处理:示例中省略了详细异常处理,实际应用应捕获IOException等异常。
资源释放:虽然FileOutputStream自动关闭,但需手动关闭Workbook避免内存泄漏。
版本兼容性XSSFWorkbook适用于.xlsx格式;如需处理旧版.xls,改用HSSFWorkbook

扩展功能
样式设置:可通过CellStyle设置字体、颜色、边框等。
动态数据:结合数据库或循环结构添加多行数据。
读取现有文件:使用WorkbookFactory.create(new File("input.xlsx"))读取并修改现有文件。

优缺点分析

优势
• 功能全面,支持多种Office格式。
• 社区活跃,文档完善。
• 提供流式API处理大文件。

注意事项
• 内存消耗较大,处理大文件时建议使用SXSSF。
• 部分组件(如HWPF)对新格式支持有限。
• 日期和单元格格式需注意Excel的特定处理方式。

官网

Apache POI官网

替代方案

简单Excel操作:可考虑EasyExcel(阿里开源,内存优化)。
纯数据导出:CSV格式更轻量,无需复杂格式时推荐使用。

02 Excel基本读写操作

POI的主要方法

以下是 Apache POI 中常用核心方法的分类整理,适用于处理 Excel 文件(以 XSSF/HSSF 为例):


1. 工作簿(Workbook)操作

方法名作用示例代码
new XSSFWorkbook()创建新的 .xlsx 工作簿Workbook wb = new XSSFWorkbook();
new HSSFWorkbook()创建新的 .xls 工作簿Workbook wb = new HSSFWorkbook();
createSheet(String name)创建 Sheet 页Sheet sheet = wb.createSheet("数据");
getSheetAt(int index)获取指定索引的 SheetSheet sheet = wb.getSheetAt(0);
getSheet(String name)按名称获取 SheetSheet sheet = wb.getSheet("数据");
write(OutputStream stream)将工作簿写入输出流wb.write(new FileOutputStream("test.xlsx"));
close()关闭工作簿释放资源wb.close();

2. Sheet 页操作

方法名作用示例代码
createRow(int rownum)创建行Row row = sheet.createRow(0);
getRow(int rownum)获取行Row row = sheet.getRow(0);
getPhysicalNumberOfRows()获取实际行数int rowCount = sheet.getPhysicalNumberOfRows();
autoSizeColumn(int column)自动调整列宽sheet.autoSizeColumn(0);
addMergedRegion(CellRangeAddress)合并单元格sheet.addMergedRegion(new CellRangeAddress(0,0,0,2));

3. 行(Row)操作

方法名作用示例代码
createCell(int column)创建单元格Cell cell = row.createCell(0);
getCell(int column)获取单元格Cell cell = row.getCell(0);
getLastCellNum()获取最后一个单元格索引+1int lastCell = row.getLastCellNum();
setHeightInPoints(float height)设置行高(单位:磅)row.setHeightInPoints(20);

4. 单元格(Cell)操作

方法名作用示例代码
setCellValue(String value)设置字符串值cell.setCellValue("Hello");
setCellValue(double value)设置数字值cell.setCellValue(100.5);
setCellValue(boolean value)设置布尔值cell.setCellValue(true);
setCellValue(Date value)设置日期值cell.setCellValue(new Date());
setCellFormula(String formula)设置公式cell.setCellFormula("SUM(A1:B1)");
getStringCellValue()获取字符串值String val = cell.getStringCellValue();
getNumericCellValue()获取数字值double num = cell.getNumericCellValue();
getBooleanCellValue()获取布尔值boolean flag = cell.getBooleanCellValue();
getDateCellValue()获取日期值Date date = cell.getDateCellValue();
getCellType()获取单元格类型CellType type = cell.getCellType();

5. 样式处理(CellStyle)

方法名作用示例代码
createCellStyle()创建单元格样式CellStyle style = wb.createCellStyle();
setFillForegroundColor(short)设置背景颜色style.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
setFillPattern(FillPatternType)设置填充模式style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
setBorderTop(BorderStyle)设置上边框样式style.setBorderTop(BorderStyle.THIN);
setFont(Font font)设置字体样式style.setFont(font);
setDataFormat(short format)设置数据格式(如日期格式)style.setDataFormat(wb.createDataFormat().getFormat("yyyy-MM-dd"));

6. 高级数据操作

日期处理
// 判断单元格是否为日期格式
if (DateUtil.isCellDateFormatted(cell)) {
    Date date = cell.getDateCellValue();
}

// 创建日期格式样式
CellStyle dateStyle = wb.createCellStyle();
CreationHelper createHelper = wb.getCreationHelper();
dateStyle.setDataFormat(createHelper.createDataFormat().getFormat("yyyy-MM-dd"));
cell.setCellStyle(dateStyle);
公式计算
// 设置公式
cell.setCellFormula("A1+B1*0.1");

// 计算公式结果(需要触发计算)
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
evaluator.evaluateFormulaCell(cell); // 计算结果会缓存到单元格

7. 流式写入(SXSSF 用于大数据量)

// 创建 SXSSFWorkbook(默认保留100行在内存中)
SXSSFWorkbook wb = new SXSSFWorkbook(100); 

// 写入数据后自动清理临时行
Sheet sheet = wb.createSheet();
for(int i=0; i<100000; i++){
    Row row = sheet.createRow(i);
    // ...写入数据
    if(i % 100 == 0) {
        ((SXSSFSheet)sheet).flushRows(100); // 手动刷新行
    }
}

// 必须显式清理临时文件
wb.dispose(); 

关键注意事项

  1. 资源释放:始终使用 try-with-resourcesfinally 块关闭资源

    try (Workbook wb = new XSSFWorkbook();
         FileOutputStream fos = new FileOutputStream("data.xlsx")) {
        // 操作代码
    }
    
  2. 类型安全:读取数据前必须检查单元格类型

    if (cell.getCellType() == CellType.NUMERIC) {
        // 处理数字
    }
    
  3. 性能优化
    • 避免频繁创建样式对象,应复用 CellStyle
    • 处理 10万+ 行数据时使用 SXSSFWorkbook
    • 禁用公式计算(若不需要):wb.setForceFormulaRecalculation(false);

03 EasyExcel和POI区别

EasyExcel 和 Apache POI 都是用于处理 Excel 文件的 Java 库,但它们在设计理念、性能优化和使用场景上有显著差异。以下是两者的详细对比:


核心区别对比表

对比维度Apache POIEasyExcel
定位通用 Office 文档处理(Excel/Word/PPT 等)专注于 Excel 的高性能读写(阿里开源库)
内存管理DOM 模式(全量加载)或 SAX 模式(流式读取)默认流式读写,内存占用极低
API 复杂度复杂(需手动处理行、列、样式)高度封装,通过注解和监听器简化开发
大数据处理需使用 SXSSFWorkbook 优化原生支持百万级数据,无需额外配置
功能覆盖支持 Excel 全部特性(公式、图表、宏等)专注核心读写,复杂特性支持有限
学习成本较高(需理解 Excel 底层结构)较低(面向对象式开发)
社区生态成熟(Apache 顶级项目,更新频繁)活跃(阿里维护,文档完善但生态较新)

核心场景对比

1. 内存消耗与性能

POI
DOM 模式(XSSF/HSSF):全量加载文件到内存,处理 10万+ 行数据易导致 OOM。
SAX 模式(EventModel):流式读取可处理大文件,但需手动解析事件,开发复杂。
SXSSF:流式写入,适合生成大文件,但仍有内存管理负担。

EasyExcel
默认流式读写:逐行解析/写入,内存占用稳定(约几十MB处理百万行数据)。
自动内存优化:无需手动配置,开发者专注业务逻辑。

2. API 设计

POI

// 写入示例(POI)
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("数据");
Row row = sheet.createRow(0);
Cell cell = row.createCell(0);
cell.setCellValue("POI 示例");
// 需要手动处理样式、资源关闭等

EasyExcel

// 写入示例(EasyExcel)
List<User> dataList = getData();
EasyExcel.write("output.xlsx", User.class)
         .sheet("数据")
         .doWrite(dataList); // 自动根据模型类生成 Excel
3. 功能覆盖

POI
• 支持 Excel 公式计算、条件格式、合并单元格、图表、宏等高级功能。
• 可读取/修改复杂模板(如带有公式和样式的报表)。

EasyExcel
• 专注核心读写,对公式、图表等复杂特性支持较弱。
• 适合纯数据导入导出场景,不适合需要保留复杂格式的模板操作。


选型建议

使用 POI 的场景

• 需要处理 Word/PPT 等非 Excel 文件
• 涉及 Excel 高级功能(如公式计算、动态图表)。
• 需要深度修改复杂模板(保留原有样式和结构)。
• 项目已深度依赖 POI,且无性能瓶颈。

使用 EasyExcel 的场景

大数据量读写(如百万行数据导出或解析)。
• 简单数据导入导出,无需复杂 Excel 特性
• 追求开发效率,希望用注解简化代码。
• 内存敏感型应用(如微服务环境下避免 OOM)。


性能对比示例

处理 100万行 x 10列 数据:

指标POI (SXSSF)EasyExcel
内存占用约 500MB(需调优)稳定在 50MB 以内
写入时间15-20 秒8-12 秒
代码复杂度高(需管理临时文件)低(全自动管理)

集成与扩展

POI
• 可通过自定义 CellStyleFont 实现复杂样式。
• 支持与 JasperReports 等报表工具集成。

EasyExcel
• 提供 Spring Boot Starter,快速集成 Web 应用。
• 支持 监听器(Listener) 实现增量数据处理:
java // 流式读取示例 EasyExcel.read("input.xlsx", User.class, new UserDataListener()) .sheet() .doRead();


总结

EasyExcel大数据量 Excel 处理的终极方案,牺牲部分功能换取极致性能。
Apache POI全能型选手,适合需要精细控制 Excel 内容的场景。
• 两者可结合使用:用 EasyExcel 处理数据主体,POI 补充复杂格式调整。

04 EasyExcel 使用指南

一、基础准备

1. 添加依赖(Maven)
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.3.3</version>
</dependency>
2. 定义数据模型类
@Data // Lombok 注解自动生成 getter/setter
public class User {
    @ExcelProperty("姓名")      // 对应 Excel 列标题
    private String name;

    @ExcelProperty("年龄")
    private Integer age;

    @ExcelProperty(value = "入职日期", converter = LocalDateStringConverter.class)
    private LocalDate hireDate;
}

二、写入 Excel

1. 简单写入
// 准备数据
List<User> users = Arrays.asList(
    new User("张三", 28, LocalDate.now()),
    new User("李四", 32, LocalDate.now().minusYears(1))
);

// 写入文件
String fileName = "users.xlsx";
EasyExcel.write(fileName, User.class)
    .sheet("员工列表")       // Sheet 名称
    .doWrite(users);        // 写入数据
2. 自定义样式
EasyExcel.write(fileName, User.class)
    .registerWriteHandler(new AbstractColumnStyleStrategy() {
        @Override
        protected void setHeadCellStyle(Cell cell, Head head) {
            // 设置标题行样式
            CellStyle style = cell.getSheet().getWorkbook().createCellStyle();
            style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
            style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
            cell.setCellStyle(style);
        }
    })
    .sheet()
    .doWrite(users);

三、读取 Excel

1. 基础读取
// 定义监听器
public class UserListener extends AnalysisEventListener<User> {
    private List<User> cachedList = new ArrayList<>();

    // 每读取一行数据触发
    @Override
    public void invoke(User user, AnalysisContext context) {
        cachedList.add(user);
        if (cachedList.size() >= 100) {
            saveData(); // 批量处理
            cachedList.clear();
        }
    }

    // 读取完成后触发
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        saveData();
    }

    private void saveData() {
        // 模拟入库操作
        System.out.println("保存数据: " + cachedList);
    }
}

// 执行读取
EasyExcel.read("users.xlsx", User.class, new UserListener())
    .sheet()
    .doRead();
2. 读取指定 Sheet
EasyExcel.read(fileName, User.class, new UserListener())
    .sheet(1)           // 读取第二个 Sheet(索引从0开始)
    // .sheet("Sheet名") // 或按名称读取
    .doRead();

四、高级功能

1. 自定义数据转换器
public class GenderConverter implements Converter<Integer> {
    @Override
    public Integer convertToJavaData(ReadCellData<?> cellData) {
        return "男".equals(cellData.getStringValue()) ? 1 : 0;
    }

    @Override
    public WriteCellData<?> convertToExcelData(Integer value) {
        return new WriteCellData<>(value == 1 ? "男" : "女");
    }
}

// 在模型类中使用
public class User {
    @ExcelProperty(value = "性别", converter = GenderConverter.class)
    private Integer gender;
}
2. 动态表头
// 动态生成表头
List<List<String>> head = new ArrayList<>();
head.add(Collections.singletonList("姓名"));
head.add(Collections.singletonList("年龄"));

// 动态写入数据
List<List<Object>> data = new ArrayList<>();
data.add(Arrays.asList("张三", 28));
data.add(Arrays.asList("李四", 32));

EasyExcel.write("dynamic.xlsx")
    .head(head)
    .sheet()
    .doWrite(data);
3. Web 导出(Spring Boot)
@GetMapping("/export")
public void export(HttpServletResponse response) throws IOException {
    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    response.setHeader("Content-Disposition", "attachment;filename=users.xlsx");
    
    List<User> users = userService.getAllUsers();
    EasyExcel.write(response.getOutputStream(), User.class)
        .sheet("用户数据")
        .doWrite(users);
}

五、性能优化

1. 分页查询写入(百万级数据)
// 分页查询参数
int pageSize = 1000;
int totalPage = 1000;

try (ExcelWriter excelWriter = EasyExcel.write("large_data.xlsx").build()) {
    for (int page = 1; page <= totalPage; page++) {
        // 模拟分页查询
        List<User> data = userService.getUsersByPage(page, pageSize);
        
        WriteSheet writeSheet = EasyExcel.writerSheet()
            .head(User.class)
            .build();
        
        excelWriter.write(data, writeSheet);
    }
}
2. 关闭自动合并(提升速度)
EasyExcel.write(fileName, User.class)
    .autoCloseStream(true)
    .autoTrim(true)         // 自动去除空格
    .ignoreEmptyRow(false)  // 不忽略空行
    .registerWriteHandler(new NoMergeStrategy()) // 禁止自动合并
    .sheet()
    .doWrite(data);

六、常见问题处理

1. 日期格式不一致
// 自定义日期转换器
public class CustomDateConverter implements Converter<LocalDate> {
    @Override
    public LocalDate convertToJavaData(ReadCellData<?> cellData) {
        return LocalDate.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("yyyy/MM/dd"));
    }

    @Override
    public WriteCellData<?> convertToExcelData(LocalDate value) {
        return new WriteCellData<>(value.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
    }
}
2. 处理空单元格
public class UserListener extends AnalysisEventListener<User> {
    @Override
    public void invoke(User user, AnalysisContext context) {
        if (user.getName() == null) {
            throw new ExcelAnalysisException("姓名不能为空");
        }
    }
}

通过以上方法,可以快速实现高性能的 Excel 读写操作。对于更复杂需求(如公式计算),仍需结合 Apache POI 或其它工具实现。EasyExcel 的详细文档可参考:官方文档

05 核心方法

以下是 EasyExcel 的核心方法分类整理,涵盖数据绑定、读写控制、样式处理等关键功能:


一、注解驱动(核心数据绑定)

1. @ExcelProperty
@ExcelProperty("姓名")                 // 绑定列标题
private String name;

@ExcelProperty(index = 2)             // 按列索引绑定(从0开始)
private Integer age;

@ExcelProperty(value = "日期", converter = CustomDateConverter.class)
private Date hireDate;                // 自定义数据转换器
2. @ExcelIgnore
@ExcelIgnore                         // 忽略该字段,不参与读写
private String secretKey;
3. @DateTimeFormat
@DateTimeFormat("yyyy年MM月dd日")     // 日期格式转换
private LocalDate birthday;

二、写入 Excel 核心方法

1. 基础写入
// 简单写入
EasyExcel.write("output.xlsx", User.class)
    .sheet("用户数据")
    .doWrite(userList);

// 追加写入(需使用 ExcelWriter)
try (ExcelWriter excelWriter = EasyExcel.write(fileName, User.class).build()) {
    WriteSheet writeSheet = EasyExcel.writerSheet().build();
    excelWriter.write(dataList, writeSheet);
}
2. 表头控制
// 动态表头
List<List<String>> head = Arrays.asList(
    Arrays.asList("基本信息", "姓名"),  // 多级表头
    Arrays.asList("基本信息", "年龄")
);

EasyExcel.write(fileName)
    .head(head)
    .sheet()
    .doWrite(data);
3. 样式处理
// 注册样式策略
.write(fileName, User.class)
.registerWriteHandler(new AbstractColumnStyleStrategy() {
    @Override
    protected void setContentCellStyle(Cell cell, Head head, Integer relativeRowIndex) {
        // 设置内容单元格样式
        CellStyle style = cell.getSheet().getWorkbook().createCellStyle();
        style.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex());
        cell.setCellStyle(style);
    }
})
.sheet()
.doWrite(data);

三、读取 Excel 核心方法

1. 监听器模式(流式读取)
// 定义监听器
public class DataListener extends AnalysisEventListener<User> {
    private List<User> cachedList = new ArrayList<>();

    @Override
    public void invoke(User user, AnalysisContext context) {
        cachedList.add(user);
        if (cachedList.size() >= 100) {
            processBatch(cachedList); // 批量处理
            cachedList.clear();
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        processBatch(cachedList);
    }
}

// 执行读取
EasyExcel.read("input.xlsx", User.class, new DataListener())
    .sheet()
    .doRead();
2. 读取控制
// 读取指定范围
.read(fileName, User.class, listener)
.headRowNumber(2)       // 跳过前两行(表头在第3行)
.sheet(0)               // 第一个Sheet
.doRead();

// 中断读取(在监听器中)
context.readSheetHolder().interrupt();

四、高级功能方法

1. 自定义转换器
public class StatusConverter implements Converter<Integer> {
    @Override
    public Integer convertToJavaData(ReadCellData<?> cellData) {
        return "启用".equals(cellData.getStringValue()) ? 1 : 0;
    }

    @Override
    public WriteCellData<?> convertToExcelData(Integer value) {
        return new WriteCellData<>(value == 1 ? "启用" : "禁用");
    }
}

// 在模型类中使用
@ExcelProperty(converter = StatusConverter.class)
private Integer status;
2. 动态模板写入
// 使用模板文件
String template = "template.xlsx";
EasyExcel.write(fileName, User.class)
    .withTemplate(template)
    .sheet()
    .doWrite(data);
3. Web 导出
// Spring Boot 响应导出
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
EasyExcel.write(response.getOutputStream(), User.class)
    .autoCloseStream(false)  // 不要自动关闭流
    .sheet()
    .doWrite(data);

五、性能优化方法

1. 分页查询写入
try (ExcelWriter excelWriter = EasyExcel.write("large.xlsx").build()) {
    for (int page = 1; ; page++) {
        List<User> data = userService.getPage(page, 1000);
        if (data.isEmpty()) break;
        
        WriteSheet sheet = EasyExcel.writerSheet()
            .head(User.class)
            .build();
        excelWriter.write(data, sheet);
    }
}
2. 禁用自动合并
.registerWriteHandler(new NoMergeStrategy()) // 提升写入速度

六、关键工具类

1. ExcelReaderBuilder
ExcelReaderBuilder readerBuilder = EasyExcel.read()
    .file(fileName)
    .head(User.class)
    .registerReadListener(listener);
2. ExcelWriterBuilder
ExcelWriterBuilder writerBuilder = EasyExcel.write()
    .file(fileName)
    .head(User.class)
    .autoCloseStream(true);

七、异常处理

1. 捕获解析异常
public class DataListener extends AnalysisEventListener<User> {
    @Override
    public void onException(Exception exception, AnalysisContext context) {
        if (exception instanceof ExcelAnalysisException) {
            // 处理业务异常
        }
    }
}
2. 设置读取校验
.read(fileName, User.class, listener)
.extraRead(CoreConstants.SUPPORT_EXTRA_PROPERTY, "strict") // 严格模式
.doRead();

最佳实践建议

  1. 注解优先:尽量使用 @ExcelProperty 代替手动映射
  2. 批量处理:读取时每 100-1000 行处理一次数据
  3. 资源释放:使用 try-with-resources 管理 ExcelWriter
  4. 避免反射:大数据场景可关闭反射缓存
    WriteSheet writeSheet = EasyExcel.writerSheet()
        .useDefaultStyle(false)  // 关闭默认样式缓存
        .build();
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值