poi解析word和excel,并且获取其中文字、图片、音频和视频的位置

这篇具有很好参考价值的文章主要介绍了poi解析word和excel,并且获取其中文字、图片、音频和视频的位置。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1 目的

        最近在做一个项目,要求解析出来word和excel中的一些属性,开始没当回事,以为很简单,但是做着做着发现不对劲,国内好像没人会有这种需求,也是费了很多事时间才找到方法,分享出去让你们少走弯路,我也是个新手,勿喷。

2 技术选型

        当然是poi了,免费,文档全,下面是我用多的maven,直接上最新版本,干就完了。

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

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-scratchpad</artifactId>
            <version>5.2.2</version>
        </dependency>

3 实现

        属实很麻烦,老版本和新版本的解析方法还不一样。

3.1 xlsx

        为什么先说xlsx,因为他是最简单的,首先是文本解析,没什么好说的直接上代码:

try (XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream(file)))
{
    // 获取sheet数量
    int numberOfSheets = workbook.getNumberOfSheets();
    for (int i = 0; i < numberOfSheets; i++)
    {
        // 读取每个sheet
        XSSFSheet sheet = workbook.getSheetAt(i);
        // 遍历每一排
        for (Row row : sheet)
        {
            // 遍历每一格,并且根据每一排进行合并
            StringBuilder s = new StringBuilder();
            for (Cell cell : row)
            {    
                // 防止出现特殊符号读取不到
                String value = new DataFormatter().formatCellValue(cell);
                s.append(value);
            }
        }
    }
}

        没啥可说的i就是每一页,row就是每一行。

        然后是图片,图片其实也挺简单的:

// 遍历形状获取图片和对象
XSSFDrawing drawing = sheet.createDrawingPatriarch();
List<XSSFShape> shapes = drawing.getShapes();
for (XSSFShape shape : shapes)
{
    // 获取图片
    if (shape instanceof XSSFPicture)
    {
        XSSFPicture picture = (XSSFPicture) shape;
        // 位置信息
        XSSFClientAnchor anchor = picture.getClientAnchor();
        // 图片所在的起始行
        int row1 = anchor.getRow1();
        // 获取图片数据
        byte[] data = picture.getPictureData().getData();
        // 获取文件类型
        String type =           
picture.getPictureData().getPackagePart().getContentTypeDetails().getSubType();
    }
}

        图片也属于一种形状,所以遍历所有形状,然后获取位置和数据。

        然后是视频和音频,这个就很离谱了,取出来并不是什么.mp4或者是什么.mp3文件,而是.bin文件,我们把xlsx后缀名改成zip然后到xl\embeddings中就能看到(其实想截图的,但是csdn上传不了图片了,靠),我理解他其实是一个ole2文件,当咱们把一些视频文件嵌入到excel中时,excel会再进行封装一遍,所以常规的方法是解析不出来的。

        我们首先先把.bin文件解析出来:

// 获取嵌入对象
if (shape instanceof XSSFObjectData)
{
    XSSFObjectData objectData = (XSSFObjectData) shape;
    if(objectData.getFileName().contains("bin"))
    {
        // .bin文件
        InputStream embeddedStream = objectData.getObjectPart().getInputStream();
    }
}

        embeddedStream 其实就是.bin文件,但是我们不能直接使用,需要进一步解析,可以参照官网,利用POIFSFileSystem进行解析,写了个工具类:

https://poi.apache.org/components/poifs/fileformat.html

public static OfficeEmbed getEmbedInfo(InputStream i)
{
    try (POIFSFileSystem fs = new POIFSFileSystem(i))
    {
        Ole10Native ole10 = Ole10Native.createFromEmbeddedOleObject(fs.getRoot());
        // 文件名称
        String fileName = ole10.getLabel();
        // 后缀名
        String suffix = fileName.substring(fileName.lastIndexOf('.') + 1);
        // 字节
        byte[] b = ole10.getDataBuffer();
        if(StringUtils.isNotBlank(suffix))
        {
            return new OfficeEmbed(getFileType(suffix.toLowerCase()), suffix, b);
        }
    }
    catch (Ole10NativeException | IOException e) {
        e.printStackTrace();
    }
    return null;
}

        继续继续,这样数据就能解析出来了,但是位置还是没有,请看下面:

// 解析出来的对象,OfficeEmbed 是我自己封装的对象
OfficeEmbed officeEmbed = OfficeUtils.getEmbedInfo(embeddedStream);
if(officeEmbed != null)
{
    String type = officeEmbed.getType();
    if(!OTHER.equals(type))
    {
        // 位置信息
        ChildAnchor chAnc = shape.getAnchor();
        if (chAnc instanceof ClientAnchor)
        {
            // 获取所在行
            ClientAnchor anc = (ClientAnchor) chAnc;
            // 获取字节
            byte [] b = officeEmbed.getB();
            // 获取后缀
            String suffix = officeEmbed.getSuffix();
        }
    }
}

3.2 xls

        xls解析和xlsx差不多,但是嵌入对象解析有点不一样:

if (shape instanceof HSSFObjectData)
{
    HSSFObjectData objectData = (HSSFObjectData) shape;
    if(objectData.hasDirectoryEntry())
    {
        DirectoryNode dn = (DirectoryNode) objectData.getDirectory();
        OfficeEmbed officeEmbed = OfficeUtils.getEmbedInfo(dn);
    }
}

3.3 docx

        word的话我是直接根据段数读取

try (XWPFDocument document = new XWPFDocument( new FileInputStream(file)))
{
    for(XWPFParagraph para : paragraphs)
    {
        // 获取段落文本
        String text = para.getText();
        // 获取图片和文件
        for(XWPFRun run : para.getRuns())
{
    // 获取此段落所有嵌入图像
    List<XWPFPicture> xwpfPictures = run.getEmbeddedPictures();
    for(XWPFPicture item : xwpfPictures)
    {
        // 字节
        byte[] b = item.getPictureData().getData();
        // 类型
        String type = item.getPictureData().getPackagePart().getContentType();
        // 后缀
        String suffix = type.substring(type.lastIndexOf('/') + 1);
    }
    // 嵌入文件
    List<CTObject> c = run.getCTR().getObjectList();
    for(CTObject item : c)
    {
        NodeList nn = item.getDomNode().getChildNodes();
        for(int j=0 ; j<nn.getLength() ; j++)
        {
            Node node = nn.item(j);
            if(node != null)
            {
                String s = node.getNodeName();
                if("o:OLEObject".equals(s))
                {
                    NamedNodeMap namedNodeMap = node.getAttributes();
                    String rId = namedNodeMap.getNamedItem("r:id").getNodeValue();
                    PackagePart packagePart = document.getPartById(rId);
                    OfficeEmbed officeEmbed = OfficeUtils.getEmbedInfo(packagePart.getInputS
                    if(officeEmbed != null)
                    {
                        String type = officeEmbed.getType();
                        if(!OTHER.equals(type))
                        {
                            byte [] b = officeEmbed.getB();
                            // 获取后缀
                            String suffix = officeEmbed.getSuffix();
                        }
                    }
                }
            }
        }
    }
}
    }
}

        其实就是根据rid去xml中找文件去。

        文本框这样获取,从外网抄过来的方法:

// 获取文本框
String rtx = "declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' declare namespace wps='http://schemas.microsoft.com/office/word/2010/wordprocessingShape' declare namespace v='urn:schemas-microsoft-com:vml'.//*/wps:txbx/w:txbxContent | .//*/v:textbox/w:txbxContent";
XmlObject[] textBoxObjects =  para.getCTP().selectPath(rtx);
for (int j =0; j < textBoxObjects.length; j+=2)
{
    XWPFParagraph embeddedPara = null;
    XmlObject[] paraObjects = textBoxObjects[j].
            selectChildren(
                    new QName("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "p"));
    StringBuffer stringBuffer = new StringBuffer();
    for (int z=0 ; z<paraObjects.length ; z++)
    {
        embeddedPara = new XWPFParagraph(
                CTP.Factory.parse(paraObjects[z].xmlText()), para.getBody());
        //Here you have your paragraph;
        stringBuffer.append(embeddedPara.getText());
    }
    String textBox = stringBuffer.toString();
}

         然后是表格:

List<IBodyElement> list  = document.getBodyElements();
for(IBodyElement bodyElement : list)
{
    if (bodyElement instanceof XWPFTable) {
        XWPFTable xwpfTable = (XWPFTable) bodyElement;
        // 位置
        int zxz = document.getPosOfTable(xwpfTable);
        String result = xwpfTable.getText();
    }
}

3.4 doc

        最恶心的来了

try (HWPFDocument document = new HWPFDocument( new FileInputStream(file)))
{
    PicturesTable picturesTable = document.getPicturesTable();
// 处理文字
Range range = document.getRange();
for(int i=0 ; i<range.numParagraphs() ; i++)
{
    Paragraph paragraph = range.getParagraph(i);
    // 处理图片
    for (int j = 0; j < paragraph.numCharacterRuns(); j++)
    {
        CharacterRun run = paragraph.getCharacterRun(j);
        // 判断是否含有嵌入对象
        int picOffset = run.getPicOffset();
        if(run.isOle2())
        {
            OfficeEmbed officeEmbed = this.getOle2Result(document,picOffset);
            if(officeEmbed != null)
            {
                byte [] b = officeEmbed.getB();
                // 获取后缀
                String suffix = officeEmbed.getSuffix();
            }
        }
        // 获取图片
        if (picOffset >= 0)
        {
            Picture picture = picturesTable.extractPicture(run,true);
            if(picture != null)
            {
                byte[] b = picture.getContent();
                String type = picture.getMimeType();
                // 后缀
                String suffix = type.substring(type.lastIndexOf('/') + 1);
                if(!"emf".equals(suffix) && !"x-emf".equals(suffix))
                {
                    
                }
            }
        }
    }
    // 处理文本
    String paragraphText = paragraph.text();
    // 处理文本框
    int endOffset = paragraph.getEndOffset();
    String boxText = boxMap.get(endOffset + "");
}
}

        文本框有特殊情况,文本框的位置是在_fspaMain中,我反正是没找到方法直接获取,我用反射获取的:

    /**
     * 反射获取_fspaMain,并且获取其中的所在段落
     * @param document 读取的doc文件
     * @return 段落数组
     */
    private List<String> getTextBoxPosition(HWPFDocument document) throws NoSuchFieldException, IllegalAccessException
    {
        List<String> strings = new ArrayList<>();
        java.lang.reflect.Field fspaField = HWPFDocument.class.getDeclaredField("_fspaMain");
        fspaField.setAccessible(true);
        FSPATable fspaMain = (FSPATable) fspaField.get(document);
        String s = fspaMain.toString();
        Matcher matcher = FPSA_PATTERN.matcher(s);
        while (matcher.find())
        {
            strings.add(matcher.group());
        }
        return strings;
    }

        然后

// 反射得到_fspaMain属性
List<String> textBoxPosition = getTextBoxPosition(document);
Map<String, String> boxMap = new HashMap<>();
// 获取textbox中的值
if(CollectionUtil.isNotEmpty(textBoxPosition))
{
    Range range = document.getMainTextboxRange();
    StringBuilder stringBuffer = new StringBuilder();
    int sum = 0;
    for(int i=0 ; i<range.numParagraphs() ; i++)
    {
        Paragraph paragraph = range.getParagraph(i);
        String text = paragraph.text();
        boolean e = paragraph.isWidowControlled();
        if(e)
        {
            stringBuffer.append(text);
        }else
        {
            if(textBoxPosition.size() > sum)
            {
                stringBuffer.append(text);
                boxMap.put(textBoxPosition.get(sum), stringBuffer.toStr
                stringBuffer.setLength(0);
                sum ++;
            }
        }
    }
}

        doc的嵌入文件也是不一样的,他是根据偏移量进行命名的,不看poi底层打死也找不到方法:

/**
     * 根据objId获取嵌入文件
     * @param doc doc文件
     * @param objId 对象id
     * @return 嵌入对象
     */
    private OfficeEmbed getOle2Result(HWPFDocument doc, int objId)
    {
        Entry entry = doc.getObjectsPool().getObjectById("_" + objId);
        if (entry == null) {
            log.info("Referenced OLE2 object '{}' not found in ObjectPool",objId);
            return null;
        }
        if(entry.isDirectoryEntry())
        {
            DirectoryNode dn = (DirectoryNode) entry;
            OfficeEmbed officeEmbed = OfficeUtils.getEmbedInfo(dn);
            if(officeEmbed != null)
            {
                String type = officeEmbed.getType();
                if(!OTHER.equals(type))
                {
                    return officeEmbed;
                }
            }
        }
        return null;
    }

        over 感谢观看。文章来源地址https://www.toymoban.com/news/detail-730316.html

到了这里,关于poi解析word和excel,并且获取其中文字、图片、音频和视频的位置的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java中使用POI将word转为PDF时无法显示文字

    背景: 在windos上本地调试时使用POI将word转为PDF时, PDF无法显示文字的原因以及解决方案: 原因1 :字体不存在问题, word中使用的字体在系统(windows或者linux)上一定要已经安装, 否则PDF无法显示文字, 将需要的字体下载下来, 复制到 1) windows 的 C:WindowsFonts 文件夹下面, 然后重启机器

    2023年04月10日
    浏览(86)
  • Java poi之word文本图片内容提取

    应公司需求,需实现以下功能 word文本内容的替换; word文本内容的提取; word文档中图片的提取存放 此文章将使用Apache POI实现Word文档中文本内容及图片的提取; Apache POI 是基于 Office Open XML 标准(OOXML)和 Microsoft 的 OLE 2 复合文档格式(OLE2)处理各种文件格式的开源项目。

    2024年02月10日
    浏览(53)
  • Java POI导出Word、Excel、Pdf文档(可在线预览PDF)

    1、导入依赖Pom.xml        dependency             groupIdorg.apache.poi/groupId             artifactIdpoi/artifactId             version3.14/version         /dependency 2、Controller   3、Service a、pdfService b、wordService c、excelService  4、Utils 5、模板截图   6、前端

    2024年02月08日
    浏览(59)
  • java使用poi解析word表格,把数据入库

    77、java解析word表格,把数据入库

    2023年04月09日
    浏览(42)
  • java 使用POI-TL根据word模版,生成word文件,含图片,富文本。

    1.引入mavna坐标` 2 .poi-tl-ext插件主要用于富文本内容格式在word展现 3.word模版创建 3.具体代码实现 4.本文的miniourl路径实质为网络路径的文件。

    2024年02月16日
    浏览(68)
  • 使用poi-tl向word插入图片、文本、表格行循环

    工作中难免会向word中操作数据,本文主要介绍poi-tl的使用,先来看效果图 核心介绍: 标签 1、插入文本标签 : {{var}} 2、插入图片标签: {{@var}} 操作步骤: 1、引入依赖 2、Java核心代码 官方网址:http://deepoove.com/poi-tl/ 1、准备模版,定义好需要的标签 2、查询模版 3、获取需要填

    2024年02月05日
    浏览(120)
  • hutool poi、apache poi实现导入导出以及解析excel

    一、前言 看了例子之后后续需要更加深入学习或者更多理解其他API的话,建议看官方文档。hutool项目是中国人维护的,有中文文档,阅读起来很方便。apache poi比较底层一点,可以更加自由去二次开发自己所需的功能。 hutool官方文档 hutool官方gitee apache poi官方文档 二、基于

    2024年02月09日
    浏览(54)
  • JAVA POI的excel中包含图片进行读取保存,单张图片,多张图片

    ---------------------------------------------效果---------------------------------------------------------- 1.单张图片 2.多张图片

    2024年02月11日
    浏览(45)
  • Java poi之Excel文本图片内容提取

    应公司需求,需实现以下功能 Excel文本内容的替换; Excel文本内容的提取; Excel中图片的提取存放 此文章将使用Apache POI实现Excel文件中文本内容及图片的提取; Apache POI 是基于 Office Open XML 标准(OOXML)和 Microsoft 的 OLE 2 复合文档格式(OLE2)处理各种文件格式的开源项目。

    2024年02月05日
    浏览(82)
  • Apache POI 解析复杂的excel表格

    一:场景说明        最近接到一个需求,让我解析Excel表。这要是简单常规的Excel表,那我还能摸一摸鱼给他整出来,主要是给我的Excel表长得跟下图中的Excel表一样复杂难搞,这可把我难倒了。于是开启了我的百度之旅,有可能是我不会百度或者理解能力太差,反正就是

    2024年02月04日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包