跳转至

Java 操作 XLSX 文件:从入门到实践

简介

在日常的开发工作中,处理 Excel 文件是一项常见的任务。XLSX 是 Microsoft Excel 2007 及更高版本使用的文件格式,基于 XML 标准。Java 作为一种广泛应用的编程语言,提供了多种方式来操作 XLSX 文件。本文将深入探讨 Java 操作 XLSX 的基础概念、使用方法、常见实践以及最佳实践,帮助读者快速掌握这一技能并在项目中高效应用。

目录

  1. 基础概念
    • XLSX 文件结构
    • Java 操作 XLSX 的相关库
  2. 使用方法
    • 读取 XLSX 文件
    • 写入 XLSX 文件
  3. 常见实践
    • 数据处理与转换
    • 样式设置
  4. 最佳实践
    • 性能优化
    • 错误处理与异常处理
  5. 小结
  6. 参考资料

基础概念

XLSX 文件结构

XLSX 文件本质上是一个压缩包,包含多个 XML 文件和相关资源。其中,主要的 XML 文件包括: - workbook.xml:包含工作簿的全局信息,如工作表列表、共享字符串表等。 - worksheet*.xml:每个工作表对应一个 XML 文件,存储工作表的实际数据、单元格格式等信息。 - styles.xml:存储所有的样式信息,如字体、颜色、对齐方式等。

Java 操作 XLSX 的相关库

  • Apache POI:是一个广泛使用的 Java 库,提供了丰富的 API 来操作各种 Office 格式文件,包括 XLSX。它支持读取、写入、修改 XLSX 文件的内容和样式。
  • EasyExcel:是一个基于 Apache POI 封装的轻量级库,专注于简化 Excel 文件的读写操作。它在性能和易用性方面有不错的表现。

使用方法

读取 XLSX 文件

以 Apache POI 为例,读取 XLSX 文件的代码示例如下:

import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Cell;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class XlsxReader {
    public static void main(String[] args) {
        String filePath = "path/to/your/file.xlsx";
        try (FileInputStream fis = new FileInputStream(new File(filePath));
             Workbook workbook = new XSSFWorkbook(fis)) {

            Sheet sheet = workbook.getSheetAt(0);
            for (Row row : sheet) {
                for (Cell cell : row) {
                    String cellValue = getCellValue(cell);
                    System.out.print(cellValue + "\t");
                }
                System.out.println();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static String getCellValue(Cell cell) {
        if (cell == null) {
            return "";
        }

        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue();
            case NUMERIC:
                if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) {
                    return cell.getDateCellValue().toString();
                } else {
                    return String.valueOf(cell.getNumericCellValue());
                }
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                return cell.getCellFormula();
            default:
                return "";
        }
    }
}

写入 XLSX 文件

以下是使用 Apache POI 写入 XLSX 文件的示例:

import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Cell;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class XlsxWriter {
    public static void main(String[] args) {
        String filePath = "path/to/output/file.xlsx";
        try (Workbook workbook = new XSSFWorkbook();
             FileOutputStream fos = new FileOutputStream(new File(filePath))) {

            Sheet sheet = workbook.createSheet("Sheet1");
            for (int i = 0; i < 5; i++) {
                Row row = sheet.createRow(i);
                for (int j = 0; j < 3; j++) {
                    Cell cell = row.createCell(j);
                    cell.setCellValue("Cell (" + i + ", " + j + ")");
                }
            }

            workbook.write(fos);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

常见实践

数据处理与转换

在读取 XLSX 文件后,通常需要对数据进行处理和转换。例如,将读取到的数据转换为 Java 对象,以便进行后续的业务逻辑处理。

import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Cell;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getters and Setters
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

public class DataProcessing {
    public static void main(String[] args) {
        String filePath = "path/to/person_data.xlsx";
        List<Person> personList = new ArrayList<>();

        try (FileInputStream fis = new FileInputStream(new File(filePath));
             Workbook workbook = new XSSFWorkbook(fis)) {

            Sheet sheet = workbook.getSheetAt(0);
            for (Row row : sheet) {
                if (row.getRowNum() == 0) {
                    // Skip header row
                    continue;
                }
                Cell nameCell = row.getCell(0);
                Cell ageCell = row.getCell(1);

                String name = getCellValue(nameCell);
                int age = (int) Double.parseDouble(getCellValue(ageCell));

                Person person = new Person(name, age);
                personList.add(person);
            }

            for (Person person : personList) {
                System.out.println("Name: " + person.getName() + ", Age: " + person.getAge());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static String getCellValue(Cell cell) {
        if (cell == null) {
            return "";
        }

        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue();
            case NUMERIC:
                if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) {
                    return cell.getDateCellValue().toString();
                } else {
                    return String.valueOf(cell.getNumericCellValue());
                }
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                return cell.getCellFormula();
            default:
                return "";
        }
    }
}

样式设置

可以使用 Apache POI 对 XLSX 文件的单元格进行样式设置,如字体、颜色、对齐方式等。

import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class StyleSetting {
    public static void main(String[] args) {
        String filePath = "path/to/styled_file.xlsx";
        try (Workbook workbook = new XSSFWorkbook();
             FileOutputStream fos = new FileOutputStream(new File(filePath))) {

            Sheet sheet = workbook.createSheet("Styled Sheet");
            Row row = sheet.createRow(0);
            Cell cell = row.createCell(0);
            cell.setCellValue("Styled Cell");

            CellStyle style = workbook.createCellStyle();
            Font font = workbook.createFont();
            font.setFontName("Arial");
            font.setFontHeightInPoints((short) 14);
            font.setColor(Font.COLOR_RED);
            style.setFont(font);
            style.setAlignment(CellStyle.ALIGN_CENTER);

            cell.setCellStyle(style);

            workbook.write(fos);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

最佳实践

性能优化

  • 使用流式处理:对于大型 XLSX 文件,使用 Apache POI 的 SAX 模式(事件驱动模式)进行读取,可以避免将整个文件加载到内存中,从而提高性能。例如:
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

import java.io.IOException;
import java.io.InputStream;

public class StreamingXlsxReader {
    public static void main(String[] args) throws IOException, SAXException {
        String filePath = "path/to/large_file.xlsx";
        OPCPackage pkg = OPCPackage.open(new File(filePath));
        XSSFReader xssfReader = new XSSFReader(pkg);
        SharedStringsTable sst = xssfReader.getSharedStringsTable();

        XMLReader parser = XMLReaderFactory.createXMLReader();
        ContentHandler handler = new ContentHandler(sst);
        parser.setContentHandler(handler);

        InputStream sheet1 = xssfReader.getSheet("rId1");
        InputSource sheetSource = new InputSource(sheet1);
        parser.parse(sheetSource);
    }

    private static class ContentHandler extends DefaultHandler {
        private SharedStringsTable sst;
        private StringBuilder cellValue = new StringBuilder();
        private boolean isCellValue = false;

        public ContentHandler(SharedStringsTable sst) {
            this.sst = sst;
        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            if ("c".equals(qName)) {
                isCellValue = true;
                cellValue.setLength(0);
            }
        }

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            if (isCellValue) {
                cellValue.append(ch, start, length);
            }
        }

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            if ("c".equals(qName)) {
                isCellValue = false;
                String cellText = cellValue.toString();
                if (cellText.startsWith("s")) {
                    int idx = Integer.parseInt(cellText.substring(1));
                    cellText = sst.getItemAt(idx).getString();
                }
                System.out.print(cellText + "\t");
            } else if ("row".equals(qName)) {
                System.out.println();
            }
        }
    }
}
  • 批量写入:在写入 XLSX 文件时,尽量减少对文件的写入操作次数。可以先将数据存储在内存中,然后一次性写入文件。

错误处理与异常处理

在操作 XLSX 文件时,要做好错误处理和异常处理。例如,在读取文件时可能会遇到文件不存在、格式错误等问题,在写入文件时可能会遇到权限不足等问题。使用 try-catch 块捕获异常,并进行适当的处理,如记录日志、向用户提示错误信息等。

import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class ExceptionHandling {
    public static void main(String[] args) {
        String filePath = "path/to/file.xlsx";
        try (FileInputStream fis = new FileInputStream(new File(filePath));
             Workbook workbook = new XSSFWorkbook(fis)) {
            // 正常处理逻辑
        } catch (IOException e) {
            System.err.println("Error reading XLSX file: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

小结

本文详细介绍了 Java 操作 XLSX 文件的相关知识,包括基础概念、使用方法、常见实践以及最佳实践。通过学习这些内容,读者可以掌握如何使用 Java 读取、写入和处理 XLSX 文件,并且能够在实际项目中优化性能、处理异常,提高开发效率和代码质量。

参考资料