前言

本文主要根据后台接口识别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;
}

文件识别浅谈(含office文件区分)的更多相关文章

  1. oracle: 浅谈sqlnet.ora文件的作用,及SQLNET.AUTHENTICATION_SERVICES设置

    关于sqlnet.ora的说明: *****************************************************FROM ORACLE11G DOCS*********** ...

  2. JAVA NIO之浅谈内存映射文件原理与DirectMemory

    JAVA类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段.本文我主要想结合操作系统中(OS)相关方面的知识介绍一下原 ...

  3. 浅谈python中文件和文件夹的相关操作

    文件操作 文件的打开与关闭 打开文件 使用open(文件名,访问方式)函数,可以打开一个已存在的文件,或者创建一个新的文件. 示例如下: f = open('test.txt') # 访问方式可以省略 ...

  4. 【NIO】NIO之浅谈内存映射文件原理与DirectMemory

    Java类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段.本文我主要想结合操作系统中(OS)相关方面的知识介绍一下原 ...

  5. 浅谈MySQL日志文件|手撕MySQL|对线面试官

    关注微信公众号[程序员白泽],进入白泽的知识分享星球 前言 上周五面试了字节的第三面,深感数据库知识的重要,我也意识到在平时的学习中,自己对于数据库的学习较为薄弱.甚至在有过一定实习经验之后,依旧因为 ...

  6. c#Winform程序调用app.config文件配置数据库连接字符串 SQL Server文章目录 浅谈SQL Server中统计对于查询的影响 有关索引的DMV SQL Server中的执行引擎入门 【译】表变量和临时表的比较 对于表列数据类型选择的一点思考 SQL Server复制入门(一)----复制简介 操作系统中的进程与线程

    c#Winform程序调用app.config文件配置数据库连接字符串 你新建winform项目的时候,会有一个app.config的配置文件,写在里面的<connectionStrings n ...

  7. 浅谈 举家搬迁静态文件到CDN

    由于七牛CDN最近做活动,对于标准用户可以免费使用如下优惠 10 GB 存储空间 10 G/月 下载流量 10 万次/月 PUT/DELETE 请求 100 万次/月 GET 请求 以上这些指标直接就 ...

  8. 浅谈qmake之pro、pri、prf、prl文件

    浅谈qmake之pro.pri.prf.prl文件 转载自:http://blog.csdn.net/dbzhang800/article/details/6348432 尽管每次和cmake对比起来 ...

  9. 浅谈JS中的!=、== 、!==、===的用法和区别 JS中Null与Undefined的区别 读取XML文件 获取路径的方式 C#中Cookie,Session,Application的用法与区别? c#反射 抽象工厂

    浅谈JS中的!=.== .!==.===的用法和区别   var num = 1;     var str = '1';     var test = 1;     test == num  //tr ...

随机推荐

  1. Opencv 张正友相机标定傻瓜教程

    注: 程序所用的OpenCV版本是 2.4.10 ,3.0以上的版本可能会有不同 先贴一下完整的工程代码: #include "opencv2/core/core.hpp" #in ...

  2. Entity Framework 6 编译出错的问题(VS2012)

    更新:其实这个问题是由于VS2012的EF代码生成模板是EF 5.x的,自然会与EF6 的runtime不兼容.起初我按照更新前的方式解决了,后来却发现会出现不止这一处命名空间发生改动而导致的问题. ...

  3. Information centric network (icn) node based on switch and network process using the node

    The present invention relates to an apparatus for supporting information centric networking. An info ...

  4. 1.跟着微软 https://docs.microsoft.com/zh-cn/dotnet/core/ 学习.net core

    10分钟快速使用 安装之后 打开cmd 第一步. dotnet new console -o firstApp 第二步. cd firstApp 第三部.dotnet run 这样就运行了hello ...

  5. Python属性和方法

    关键字:Python 属性 方法原文:http://www.cafepy.com/article/python_attributes_and_methods/python_attributes_and ...

  6. ASP.NET Core 中间件 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core 中间件 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 中间件 上一章节中,我们我们有讲到 Startup 类中的 Confi ...

  7. ATS项目更新(2) 命令行编译Studio解决方案

    1: rem "D:\Microsoft Visual Studio 8\SDK\v2.0\Bin\sdkvars.bat" 2: D: 3: cd ..\..\..\..\..\ ...

  8. hdoj 5087 Revenge of LIS II 【第二长单调递增子】

    称号:hdoj 5087 Revenge of LIS II 题意:非常easy,给你一个序列,让你求第二长单调递增子序列. 分析:事实上非常easy.不知道比赛的时候为什么那么多了判掉了. 我们用O ...

  9. 【转】postgresql 9.4 在linux环境的安装步骤详解

    本文章来为各位介绍一篇关于postgresql 9.4 在linux环境的安装步骤详解,希望文章能够对各位新手朋友带来帮助的哦. 环境说明系统:centos 6.4 64位软件:postgresql ...

  10. Swift 的 Currying 特性 | SwiftCafe 咖啡时间

    Currying 也是 Swift 的众多先进特性之一,用一句话说就是将接受多个参数的函数,转变成每次之接受一个参数的调用序列. 上面一句话说得可能大家感觉不是那么清楚,那么没关系,咱们举一个例子来说 ...