xlsx/pptx/docx文件数据格式解析-帮你搞懂数据到底存在哪

序言

在开发过程中经常遇到office文件相关的问题,比如将数据导出成Excel、自动生成报表、自动生成填充合同模板、转pdf等。UOffice是一个为解决这类Office文件问题的项目,里面记录了到目前为止,笔者遇到的相关问题的解决方案。笔者希望UOffice能称为一个完善的集合,但是靠一个人很难做到完善,所以这个项目后续会公开到github上,希望能为大家提供便利,也希望大家共同完善功能,使之更加健壮。

(.xlsx、.docx、.pptx)文件解析

.xlsx、.docx、.pptx是ooxml格式的office文件,简单了来说就是(压缩+xml)的格式。也就说你可以用压缩工具(比如7zip)将一个xlsx(或docx、pptx)文件解压。以xlsx为例,其他文件格式类似。
xlsx文件
xlsx内容

xlsx解压完后
下面详细介绍下主要的几个部分的作用。

xl/sharedStrings.xml

此文件主要保本excel中各个单元格文本的内容。

<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="2" uniqueCount="2">
<si>
<t>我是</t>
<phoneticPr fontId="1" type="noConversion"/>
</si>
<si>
<t>Uoffice</t>
<phoneticPr fontId="1" type="noConversion"/>
</si>
</sst>

其中内容是去重的,相同的内容只能存一份。count代表总数,uniqueCount是去重后的数量。

xl/styles.xml

此文件主要存单元格的格式信息

<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" mc:Ignorable="x14ac">
<fonts count="2" x14ac:knownFonts="1">
<font>
<sz val="11"/>
<color theme="1"/>
<name val="宋体"/>
<charset val="134"/>
<scheme val="minor"/>
</font>
<font>
<sz val="9"/>
<name val="宋体"/>
<charset val="134"/>
<scheme val="minor"/>
</font>
</fonts>
<fills count="2">
<fill>
<patternFill patternType="none"/>
</fill>
<fill>
<patternFill patternType="gray125"/>
</fill>
</fills>
<borders count="1">
<border>
<left/>
<right/>
<top/>
<bottom/>
<diagonal/>
</border>
</borders>
<cellStyleXfs count="1">
<xf numFmtId="0" fontId="0" fillId="0" borderId="0">
<alignment vertical="center"/>
</xf>
</cellStyleXfs>
<cellXfs count="1">
<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0">
<alignment vertical="center"/>
</xf>
</cellXfs>
<cellStyles count="1">
<cellStyle name="常规" xfId="0" builtinId="0"/>
</cellStyles>
<dxfs count="0"/>
<tableStyles count="0" defaultTableStyle="TableStyleMedium2" defaultPivotStyle="PivotStyleLight16"/>
<extLst>
<ext xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" uri="{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}">
<x14:slicerStyles defaultSlicerStyle="SlicerStyleLight1"/>
</ext>
<ext xmlns:x15="http://schemas.microsoft.com/office/spreadsheetml/2010/11/main" uri="{9260A510-F301-46a8-8635-F512D64BE5F5}">
<x15:timelineStyles defaultTimelineStyle="TimeSlicerStyleLight1"/>
</ext>
</extLst>
</styleSheet>

<fonts/>标签存放了xlsx中的字体信息,<fills/>标签存放了xlsx的单元格填充信息,标签存放了单元格边框信息,最重要的是** <cellXfs> **标签,里面综合了单元格的配置信息。

xl/worksheets/sheet1.xml

此文件中主要存放了每个单元格的具体信息

<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" mc:Ignorable="x14ac">
<dimension ref="A1:B1"/>
<sheetViews>
<sheetView tabSelected="1" workbookViewId="0">
<selection activeCell="B1" sqref="B1"/>
</sheetView>
</sheetViews>
<sheetFormatPr defaultColWidth="9" defaultRowHeight="14.4" x14ac:dyDescent="0.25"/>
<sheetData>
<row r="1" spans="1:2" x14ac:dyDescent="0.25">
<c r="A1" t="s">
<v>0</v>
</c>
<c r="B1" t="s">
<v>1</v>
</c>
</row>
</sheetData>
<phoneticPr fontId="1" type="noConversion"/>
<pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
<pageSetup paperSize="9" orientation="portrait"/>
</worksheet>

<sheetData>标签中的row标签代表了每一行,row中的c标签单表了一行中的每一个单元格,c标签中的t="s"表示该单元格为文字,通过v标签标记的索引在sharedStrings.xml中查找。如果c标签中没有t变量,表示单元格为数字,此时v标签内的内容即为单元格内容。c标签中还有一个s变量,s变量指向styles.xml中的cellXfs标签,此处s变量未声明,默认应用cellXfs中的第一个。

总结

ooxml的格式大致如上所示,如果文件中有图像、引用等特殊元素,也是通过不同xml去表示的。
最后放上UOffice中解压文件和聚合文件的代码

protected void initTemplate(InputStream inputStream) {
        String path = File.separator + UUID.randomUUID().toString();
        try (ZipInputStream zipInputStream = new ZipInputStream(inputStream)) {
            ZipEntry nextEntry;
            while ((nextEntry = zipInputStream.getNextEntry()) != null) {
                if (nextEntry.isDirectory()) {
                    String tempPath = path + File.separator + nextEntry.getName();
                    File tempFile = new File(tempPath);
                    if (!tempFile.exists()) {
                        if (!tempFile.mkdirs()) {
                            throw new UOfficeException("创建{}失败", tempFile.getAbsolutePath());
                        }
                    }
                } else {
                    String tempPath = path + File.separator + nextEntry.getName();
                    File tempFile = new File(tempPath);
                    FileOutputStream fileOutputStream = new FileOutputStream(tempFile);
                    byte[] buffer = new byte[1024];
                    int read = 0;
                    while ((read = zipInputStream.read(buffer)) != -1) {
                        fileOutputStream.write(buffer, 0, read);
                    }
                    fileOutputStream.close();
                }
                zipInputStream.closeEntry();
            }
            templatePath = path;
        } catch (Exception e) {
            PathUtils.deleteAllDir(path);
        }
    }
public void writeToStream(OutputStream outputStream) {
        List<PathEntry> pathEntries = PathUtils.pathWalk(templatePath);
        if (ObjectUtils.isNotEmpty(outputStream)) {
            try (ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) {
                for (PathEntry p : pathEntries) {
                    ZipEntry zipEntry = new ZipEntry(p.getName());
                    zipOutputStream.putNextEntry(zipEntry);
                    if (!p.isWhetherDir()) {
                        String s = templatePath + File.separator + p.getName();
                        FileInputStream fileInputStream = new FileInputStream(new File(s));
                        byte[] buffer = new byte[1024];
                        int read = 0;
                        while ((read = fileInputStream.read(buffer)) != -1) {
                            zipOutputStream.write(buffer, 0, read);
                        }
                        fileInputStream.close();
                    }
                    zipOutputStream.closeEntry();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

最后限于本人能力的原因,难免有错误或遗漏之处,请大家多多指正。前两天发起了一个投票“做一个自动填充数据生成xlsx文件功能的价值”,共三人(包括我自己)投票表示很有价值,所以下一篇介绍如何去自动填充数据。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值