专栏首页日常分享文件识别浅谈(含office文件区分)

文件识别浅谈(含office文件区分)

前言

本文主要根据后台接口识别Office文件类型这一话题做一些分享,主要方向还是放在不能获取到文件名情况下的Office文件识别。

可获取到文件名

如果后端接口可以获取到完成的文件名称,则整个过程会变得很轻松,如果不考虑到客户恶意修改后缀名的情况,我们只需要对获取到的文件名进行截取即可,整个截取的代码种类也很多,下面分享一个我的实现。

public static String parseFileType(String originName) {
        if (StringUtils.isBlank(originName)) {
            return null;
        } else {
            int i = originName.lastIndexOf("\\");
            int j = originName.lastIndexOf("/");
            int index = i > j ? i : j;
            if (index == originName.length() - 1) {
                return null;
            } else {
                String name = originName.substring(index + 1);
                if (!name.contains(".")) {
                    return null;
                } else {
                    if (name.lastIndexOf("." ) + 1 == name.length()) {
                        return null;
                    } else {
                        return name.substring(name.lastIndexOf(".") + 1, name.length()).toLowerCase();
                    }
                }
            }
        }
    }

不可获取到文件名(文件流)

我们根据常用文件类型拥有一定固定的文件头这种方式可以实现对接口中的文件流做一定程度上的文件识别,这里分享一个简单的实现方式。

public static String getFileTypeByStream(byte[] b) {
        Map FILE_TYPE_MAP = new HashMap();
        FILE_TYPE_MAP.put("d0cf11e0a1b11ae10000","doc"); //MS Excel 注意:word、msi 和 excel的文件头一样    
        FILE_TYPE_MAP.put("504b0304", "docx");//docx,xlxs,pptx文件
        FILE_TYPE_MAP.put("d0cf11e0a1b11ae10000", "wps");//WPS文字wps、表格et、演示dps都是一样的
        FILE_TYPE_MAP.put("255044462D312E", "pdf"); //Adobe Acrobat (pdf) 
        
        StringBuilder stringBuilder = new StringBuilder();
        if (b == null || b.length <= 0) {
            return null;
        }
        for (byte element : b) {
            int v = element & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        String filetypeHex = String.valueOf(stringBuilder.toString()).substring(0, 20);
        System.out.println(filetypeHex);
        Iterator<Entry<String, String>> entryiterator = FILE_TYPE_MAP.entrySet().iterator();
        while (entryiterator.hasNext()) {
            Entry<String, String> entry = entryiterator.next();
            String fileTypeHexKey = entry.getKey().toUpperCase();
            if (filetypeHex.toUpperCase().startsWith(fileTypeHexKey)) {
                return entry.getValue();
            }
        }
        return null;
    }

这种方式对一些图片视频文件类型识别较为准确,但是对于office文件的识别表现的不是太尽如人意,他无法区分excel,ppt,word等三种文件,且有些形式的office文件识别还存在一定的匹配错误。

根据微软文档给出的信息可以知道MS Office是以二进制进行存储的,需要根据其文档给出的读取方式进行读取,这样应该是可以判断各个文件类型的,笔者没有对这个文档做深入研究,这里主要讲一下我对新版本office的处理方式(xlxs、pptx、docx)的处理方式,细心的读者可能已经发现docx和zip的16进制文件头是基本一致,我们将docx文件使用压缩文件打开,可以发现其中包含一个文件:[Content_Types].xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
    <Default Extension="png" ContentType="image/png"/>
    <Default Extension="bin" ContentType="application/vnd.openxmlformats-officedocument.oleObject"/>
    <Default Extension="jpeg" ContentType="image/jpeg"/><Default Extension="emf" ContentType="image/x-emf"/>
    <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
    <Default Extension="xml" ContentType="application/xml"/>
    <Default Extension="jpg" ContentType="image/jpeg"/>
    <Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
    <Override PartName="/customXml/itemProps1.xml" ContentType="application/vnd.openxmlformats-officedocument.customXmlProperties+xml"/>
    <Override PartName="/word/numbering.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml"/>
    <Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>
    <Override PartName="/word/settings.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"/>
    <Override PartName="/word/webSettings.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml"/>
    <Override PartName="/word/footnotes.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml"/>
    <Override PartName="/word/endnotes.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml"/>
    <Override PartName="/word/header1.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml"/>
    <Override PartName="/word/header2.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml"/>
    <Override PartName="/word/footer1.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml"/>
    <Override PartName="/word/footer2.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml"/>
    <Override PartName="/word/header3.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml"/>
    <Override PartName="/word/footer3.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml"/>
    <Override PartName="/word/header4.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml"/>
    <Override PartName="/word/footer4.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml"/>
    <Override PartName="/word/fontTable.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml"/>
    <Override PartName="/word/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>
    <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
    <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
</Types>

其基本包含了整个文件中包含的内容,我们对其中一个key-value进行识别即可,docx的标志性PartName为"/word/document.xml“,xlsx的标志性PartName为/xl/workbook.xml。

private static List<String> parseFilePartNameList(MultipartFile file) throws IOException, JDOMException{
        List<String> partNames=new ArrayList<>();
        ZipInputStream zipStream = new ZipInputStream(file.getInputStream());
        BufferedInputStream bufferStream = new BufferedInputStream(zipStream);
        ZipEntry entry;
        while ((entry = zipStream.getNextEntry()) != null) {
            String fileName = entry.getName();
            if (fileName.equals("[Content_Types].xml")) {
                SAXBuilder builder = new SAXBuilder();
                byte[] xmlbytes = new byte[(int) entry.getSize()];
                bufferStream.read(xmlbytes, 0, (int) entry.getSize());
                InputStream byteArrayInputStream = new ByteArrayInputStream(xmlbytes);
                org.jdom.Document document = builder.build(byteArrayInputStream);
                org.jdom.Element foo = document.getRootElement();
                List<Element> chilLst = foo.getChildren();
                for (Element child : chilLst) {
                    String partNameValue = child.getAttributeValue("PartName");
                    if (StringUtils.isBlank(partNameValue)) {
                        partNames.add(partNameValue);
                    }
                }
            }
        }
        return partNames;
    }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 解压大文件提示C盘空间不够的问题

    今天在服务器解压一个之前上传的数据,大概有180GB,虽然当前盘还有984GB的富余。

    Rekent
  • JSP/Servlet Web 学习笔记 DayThree —— 实现一个登陆小界面

      利用JSP、HTML、JS实现了一个简易的登陆系统。根据前两天的所学,实现了如下功能:

    Rekent
  • 在Struts2 Action中快速简便的访问Request、Session等变量

    全部方法(共四种)可以参考:http://blog.csdn.net/itmyhome1990/article/details/7019476

    Rekent
  • mysql部分替换

    UPDATE `cdb_pms` SET `subject` = REPLACE(`subject`, ‘Welcome to’, ‘欢迎光临’) WHE...

    用户1191760
  • mysql部分替换

    用户1272546
  • AS3 使用Loader对象加载外部图片

    比如要在loading图片的时候显示一个背景gif图(表示正在加载图片)或是显示加载进步等等

    meteoric
  • 从ES6到ES10的新特性万字大总结(不得不收藏)

    ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会)在标准ECMA-262中定义的脚本语言规范。这种语言在万维网上应用广泛,它往往被称为Jav...

    陈大鱼头
  • 点击时扩散效果

    ProsperLee
  • 秘钥认证用户自动控制

    明哥的运维笔记
  • 性能测试-Jmeter性能测试实战

    接口地址:http://www.baidu.com/s?ie=utf-8&wd=jmeter性能测试

    用户6367961

扫码关注云+社区

领取腾讯云代金券