1、准备一个Excel文件
假设我们有一个简单的 Excel 文件,其中包含图像及其名称的列表。
2、使用的工具
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
3、解析 Excel 文件
现在我们将完成读取 Excel 文件、提取数据和处理图像的代码。
3.1、读取文件
InputStream in = Test.class.getResourceAsStream("/excel/12.xlsx");
Java 有多种读取文件的方法。上面代码是仅读取位于类路径中的文件。
3.2、 创建XSSFWorkbook对象
try (XSSFWorkbook workbook = new XSSFWorkbook(in)) {
XSSFSheet sheet = workbook.getSheetAt(0);
...
}
这通过在内存中缓冲整个流来构造一个工作簿对象。代表 sheet
工作簿的第一张工作表。
3.3、提取图像
XSSFDrawing patriarch = sheet.createDrawingPatriarch();
List<XSSFShape> shapes = patriarch.getShapes();
Map<Integer, byte[]> imageByLocations = shapes.stream()
.filter(Picture.class::isInstance)
.map(s -> (Picture) s)
.map(this::toMapEntry)
.collect(Collectors.toMap(Pair::getKey, Pair::getValue));
图像不会保存在工作表的单元格中,而是保存在名为 XSSFDrawing(POI 行话中)的 SpreadsheetML Drawing中。您可以将 SpreadsheetML Drawing视为用于放置形状的画布。从父级(SpreadsheetML drawing)中,您可以获取所有形状。形状可以是图片,也可以是连接器、图形框架等等。由于我们这次只针对图片,因此我们过滤形状 .filter(Picture.class::isInstance)。下一步是通过 this::toMapEntry 找出图片位于哪一行。最后,我们构建一个包含行号和图像字节的map。
3.4、查找图像位置
Pair<Integer, byte[]> toMapEntry(Picture picture) {
byte[] data = picture.getPictureData().getData();
ClientAnchor anchor = picture.getClientAnchor();
return Pair.of(anchor.getRow1(), data);
}
由于形状没有放置在单元格中,因此我们需要找到它们的锚定位置。形状的左上角 (anchor.getRow1()) 为我们提供了行号。我们构建一个中间对象 org.apache.commons.lang3.tuple.Pair,它允许我们构建map。
3.5 、迭代每一行
List<Image> images = StreamSupport.stream(sheet.spliterator(), false)
.filter(this::isNotHeader)
.map(row -> new Image(row.getCell(0).toString(), imageByLocations.get(row.getRowNum())))
.collect(toList());
我们迭代每一行以提取单元格内容。在我们的例子中,只有文件名存储在第一个单元格row.getCell(0).toString()
(索引 0)中。通过行号,我们可以从上一步构建的地图中获取图像。
3.6、保存图片
images.forEach(image -> {
String pathname = String.format("%s/%s.jpg", System.getProperty("user.home"), image.fileName);
FileUtils.writeByteArrayToFile(new File(pathname), image.bytes);
});
最后一步是将图像写入文件系统。为了示例,我们将图像写入主文件夹中。
4、完整代码
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.Picture;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFShape;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import static java.util.stream.Collectors.toList;
public class Test {
public static void main(String[] args) throws IOException {
InputStream in = Test.class.getResourceAsStream("/excel/12.xlsx");
new Test().importExcel(in);
}
void importExcel(InputStream in) throws IOException {
try (XSSFWorkbook workbook = new XSSFWorkbook(in)) {
XSSFSheet sheet = workbook.getSheetAt(0);
XSSFDrawing patriarch = sheet.createDrawingPatriarch();
List<XSSFShape> shapes = patriarch.getShapes();
Map<Integer, byte[]> imageByLocations = shapes.stream()
.filter(Picture.class::isInstance)
.map(s -> (Picture) s)
.map(this::toMapEntry)
.collect(Collectors.toMap(Pair::getKey, Pair::getValue));
List<Image> images = StreamSupport.stream(sheet.spliterator(), false)
.filter(this::isNotHeader)
.map(row -> new Image(row.getCell(0).toString(), imageByLocations.get(row.getRowNum())))
.collect(toList());
for (Image image : images) {
try {
String pathname = String.format("%s/%s.jpg", System.getProperty("user.home"), image.fileName);
FileUtils.writeByteArrayToFile(new File(pathname), image.bytes);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
boolean isNotHeader(Row row) {
return !row.getCell(0).toString().toLowerCase().contains("name");
}
Pair<Integer, byte[]> toMapEntry(Picture picture) {
byte[] data = picture.getPictureData().getData();
ClientAnchor anchor = picture.getClientAnchor();
return Pair.of(anchor.getRow1(), data);
}
static class Image {
String fileName;
byte[] bytes;
Image(String fileName, byte[] bytes) {
this.fileName = fileName;
this.bytes = bytes;
}
}
}