纯js判断文件流格式类型:pdf,doc,docx,xls,xlsx,ppt,pptx一次搞定!

news2025/1/21 9:23:31

目录

  • 使用js判断文件类型的场景
  • 方法特点
  • 输入输出
  • 方法步骤
      • 1. 查看每种格式文件的16进制码,提取不同文件类型的“特征数”。
      • 2. 先判断大类型,在具体大类下判断小类型
  • 项目地址:
      • 纯前端基于react实现的多类型文件预览:
      • 通过arraybuffer判断文件类型:
  • 结语

使用js判断文件类型的场景

在开发纯前端基于react框架的文件预览组件时,需要根据不同的文件类型,分发给不同的组件去完成预览。网上已有的开源项目通常是通过传递文件名参数,通过后缀名字符串匹配区分文件类型。
但是这种做法需要用户传递准确文件名称与后缀名,如果你的文件是从服务端获取的,也同样要求后端开发准确拥有这些信息。可是,如果能直接从文件流中判断出文件类型的话,文件名参数完全可以省略,就能实现预览效果,对用户或是服务端的依赖就大大降低了。
这个文件预览项目与本文实现的功能函数github地址,都放在文件末尾了。目前实现了react框架pdf,xls,xlsx,docx这几种格式的预览,其中pdf预览实现了toolbar的基础操作功能。欢迎批评交流,如果有帮到你,请帮我点亮star吧。

方法特点

网络上很多方法仅仅通过文件流的头部编码,只能区分文件属于Microsoft2003规范(doc,xls,ppt)或Microsoft2007规范(docx,xlsx,pptx),但是对同一版本内部的具体类型是无法区分的。因为Microsoft2007规范的docx,xlsx,pptx等格式,本质上都是zip类型的文件,文件流开头的几位16进制数都是统一的“0x50,0x4b,0x03,0x04,…”。本方法在区分这两类规范的基础上进行了扩展,能有效识别各个规范下的具体文件类型。

输入输出

输入:文件的arrayBuffer数据
输出:‘file2003,file2007,pdf,doc,docx,xls,xlsx,ppt,pptx,other’之一。

方法步骤

1. 查看每种格式文件的16进制码,提取不同文件类型的“特征数”。

这一步用vscode就能完成。首先可以安装一款Hex Editor插件,用来展示16进制文件流。
在这里插入图片描述
把文件放到vscode中打开,左边是文档的16进制码(0x25,0x50…),右边是将16进制数转换成的ascii码(通过String.fromCharCode就可以转换)。这种模式可以直观地看到每个文件的关键词信息。

pdf文件的开头几个数字是唯一的,因此只要拿到文件流,很好判断一定是pdf文件。
文件头出现的关键词
docx,xlsx,pptx文件的文件头标识信息是一样的,通过这几位固定数字能判断出来是Microsoft2007新标准的文件。
在这里插入图片描述

但是这些文件的结尾位置也出现了关键词,而且是每种类型唯一的,因此不仅可以获取文件头,也可以专门获取文件尾部的一段数据来判断。
下图是docx文件docx文件在结尾位置出现的关键词

下图是xlsx文件,关键词是/worksheets在这里插入图片描述
同理,pptx文件的关键词是ppt/,也出现在文件结尾位置。
Microsoft2003规范的文件开头,都是统一的’d0’, ‘cf’, ‘11’, ‘e0’。
xls的关键词是Microsoft Excel。
doc的关键词是Microsoft Word。
ppt的关键词是PowerPoint Document。

下面的工作就是,获取文件流的不同位置的数据,放在一个数组中,来判断这个数组是否包含以上含有特征的16进制数。

2. 先判断大类型,在具体大类下判断小类型

1)需要整理一下刚刚总结的各个格式与特征数的对应关系,整理成json格式。

//大类
const formatMap = {
    'pdf': ['25', '50', '44', '46'],`在这里插入代码片`
    'file2003': ['d0', 'cf', '11', 'e0'],
    'file2007': ['50', '4b', '03', '04', '14', '00', '06', '00'],
}
//区分xlsx,pptx,docx三种格式的特征数。通过每个文件末尾的关键词检索判断
const format2007Map = {
    xlsx: ['77', '6f', '72', '6b', '73', '68', '65', '65', '74', '73', '2f'],// 转换成ascii码的含义是 worksheets/
    docx: ['77', '6f', '72', '64', '2f'],// 转换成ascii码的含义是 word/
    pptx: ['70', '70', '74', '2f'],// 转换成ascii码的含义是 ppt/
}
//区分xls,ppt,doc三种格式的特征数,关键词同样出现在文件末尾
const pptFormatList = ['50', '6f', '77', '65', '72', '50', '6f', '69', '6e', '74', '20', '44', '6f', '63', '75', '6d', '65', '6e', '74'];// 转换成ascii码的含义是 PowerPoint Document
const format2003Map = {
    xls: ['4d', '69', '63', '72', '6f', '73', '6f', '66', '74', '20', '45', '78', '63', '65', '6c'],// 转换成ascii码的含义是 Microsoft Excel
    doc: ['4d', '69', '63', '72', '6f', '73', '6f', '66', '74', '20', '57', '6f', '72', '64'],// 转换成ascii码的含义是 Microsoft Word
    ppt: pptFormatList.join(',00,').split(',')
}
//xls格式的文件还有一种例外情况,就是保存为.html格式的文件。特征码是office:excel
let xlsHtmlTarget = ['6f', '66', '66', '69', '63', '65', '3a', '65', '78', '63', '65', '6c']; 

2)把arraybuffer数据先使用Unit8Array转换成数组,再转化成16进制(为便于比较,省略了’0x’,只保留后两位)。同时,我们每次运算不需要使用全部的数组数据,而是找到对应位置的一部分,所以通过slice方法来截取我们期望位置的数组。

这里要说明,具体应该截取文件的什么位置,是打开若干个文件之后估计的大概范围,这个范围越大,匹配的成功率越高,但是要在数组遍历的效率之间做一个平衡。不排除有一些文件因为标准兼容的问题,关键词出现的位置跟我们截取的位置不一样,导致最后判断错误的可能性。

 //截取部分数组,并转化成16进制
function getSliceArrTo16(arr, start, end) {
    let newArr = arr.slice(start, end);
    return Array.prototype.map
        .call(newArr, (x) => ('00' + x.toString(16)).slice(-2));
}
//判断arr数组是否包含target数组,且不能乱序。如果数组比较小,直接将两个数组转换成字符串比较。
//在数组长度大于500的情况下,写了如下方法来提高检索的效率:
function isListContainsTarget(target, arr) {
    let i = 0;
    while (i < arr.length) {
        if (arr[i] == target[0]) {
            let temp = arr.slice(i, i + target.length);
            if (temp.join() === target.join()) {
                return true
            }
        }
        i++;
    }
}

具体的判断方法:

export default function getFileTypeFromArrayBuffer(arrayBuffer) {
	try {
        if (Object.prototype.toString.call(arrayBuffer) !== '[object ArrayBuffer]') {
            throw new TypeError("The provided value is not a valid ArrayBuffer type.")
        }
        let arr = new Uint8Array(arrayBuffer);
		let str_8 = getSliceArrTo16(arr, 0, 8).join('');  //只截取了前8位
		//为了简便,匹配的位置索引我选择在代码里直接固定写出,你也可以把相应的索引配置在json数据里。
		//第一次匹配,只匹配数组前八位,得到大范围的模糊类型
		let result = '';
		for (let type in formatMap) {
		    let target = formatMap[type].join('');
		    if (~str_8.indexOf(target)) { //相当于(str_8.indexOf(target) !== '-1')
		        result = type;
		        break;
		    }
		}
		
		if (!result) {
			//第一次匹配失败,不属于file2003,file2007,pdf。有可能是html格式的xls文件
			//通过前50-150位置判断是否是xls
		    let arr_start_16 = getSliceArrTo16(arr, 50, 150);
		    if (~(arr_start_16.join('').indexOf(xlsHtmlTarget.join('')))) {
		        return 'xls';
		    }
		    return 'other';
		}
		if (result == 'pdf') {
		     return result;
		 }
		 if (result == 'file2007') {
		     //默认是xlsx,pptx,docx三种格式中的一种,进行第二次匹配.如果未匹配到,结果仍然是file2007
		     let arr_500_16 = getSliceArrTo16(arr, -500);
		     for (let type in format2007Map) {
		         let target = format2007Map[type];
		         if (isListContainsTarget(target, arr_500_16)) {
		             result = type;
		             break;
		         }
		     }
		     return result;
		 }
		 if (result == 'file2003') {
		     let arr_end_16 = getSliceArrTo16(arr, -550, -440);
		     for (let type in format2003Map) {
		         let target = format2003Map[type];
		         //通过倒数440-550位置判断是否是doc/ppt/xls
		         if (~(arr_end_16.join('').indexOf(target.join('')))) {
		             result = type;
		             break
		         }
		     }
		     return result;
		 }
		//未匹配成功
		return 'other';

    }
        
}

项目地址:

纯前端基于react实现的多类型文件预览:

https://github.com/react-office-viewer/react-office-viewer.git,

通过arraybuffer判断文件类型:

https://github.com/react-office-viewer/getFileTypeFromArrayBuffer.git

结语

最后,本文最希望的就是为大家提供一种区分文件格式的思路,在实际操作中可以用更加丰富细致的标准来不断提升判断的准确率。欢迎大家留言交流。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/408576.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uniapp全局组件全局使用(不在每个页面template使用,仅支持H5),函数式调用全局组件方法

最简单的使用&#xff0c;在 main.js 编写如下代码&#xff0c;即可将 xxx 组件在每个页面显示 // main.js// 引入组件 import xxx from "/components/xxx.vue";// 将该组件挂载在document.body下 document.body.appendChild(new xxx().$mount().$el); 函数式调用全…

JS高级知识总结

文章目录1. this指向问题2. 对象进阶2.1 对象的定义和使用2.2 对象访问器2.2.1 Getter2.2.2 Setter2.3 对象构造器2.4 对象原型2.4.1 prototype属性2.4.2 \_\_proto\_\_ 属性2.4.3 constructor属性2.4.4 原型链2.5 Object对象2.5.1 管理对象2.5.2 保护对象3. 函数进阶3.1 函数的…

.Net Core中间件

目录 一、什么是中间件 二、中间件的用途 三、中间件的三个概念 四、自定义中间件 五、ASP.NET Core附带中间件组件 六、中间件和过滤器的区别 一、什么是中间件 在浏览网站或者使用手机App加载内容的时候&#xff0c;浏览器或者手机App其实在向Web服务器发送HTTP请求。服…

NodeJS安装(npm包管理器)

1、nodejs下载 windows下的NodeJS安装是比较方便的&#xff0c; 只需要登陆官网&#xff08;Node.js&#xff09;&#xff0c;直接点击64-bit下载安装 2、安装过程基本直接“NEXT”&#xff0c;NodeJS已经集成了npm&#xff0c;所以npm也一并安装好了 3、在cmd窗口输入node -…

React-DevTools开发者工具安装

React开发者工具最简单的安装方式自然是科学上网&#xff0c;通过Google Chrome浏览器访问应用商店安装了。以下介绍另一种安装方式&#xff1a;基于 react-devtools的GitHub项目源码编译进行插件安装。 目录 React-DevTools&#xff1a;GitHub项目地址 React-DevTools&#x…

修改elementUI中el-date-picker内置样式

一.编写背景 今天正在日常的需求编写和bug调试中&#xff0c;产品提出了这样一个需求。为了满足用户在新增数据时的便捷准确&#xff0c;时间选择格式为“年月日时”&#xff0c;即用户不需要选择分合秒&#xff0c;换句话说就是下图中红色框的两项不需要。 二.问题分析。 当时…

基于vscode开发vue3项目的详细步骤教程 3 前端路由vue-router

1、Vue下载安装步骤的详细教程(亲测有效) 1_水w的博客-CSDN博客 2、Vue下载安装步骤的详细教程(亲测有效) 2 安装与创建默认项目_水w的博客-CSDN博客 3、基于vscode开发vue项目的详细步骤教程_水w的博客-CSDN博客 4、基于vscode开发vue项目的详细步骤教程 2 第三方图标库FontAw…

如何使用 JavaScript 读取文件

您可以使用 JavaScript File API 加载选定文件的内容。本节介绍 File API 的基本用法。 现在让我们看看如何使用 File API。 使用 JavaScript 读取文件概述 选择带有输入元素的文件 在 HTML 中&#xff0c;您可以通过将 input 元素的 type 属性设置为 file 来选择文件。 &…

vue 在for循环中设置ref并获取$refs

一、单循环动态设置ref 1.设置&#xff1a;【:ref“‘XXX’ index”】XXX -->自定义ref的名字 2.获取&#xff1a;let ref eval(‘this.$refs.XXX’ index)[0] 3.示例&#xff1a; 代码如下所示 <template><div class"ref_test"><div v-fo…

常用的几种布局方式---Flex 布局(垂直居中展示)

常用的几种布局方式---Flex 布局(垂直居中展示&#xff09; 前言一、默认使用静态布局二、flex布局1.父元素container1.1.display:flex1.2.flex-direction属性1.3.主轴与侧轴1.4.justify-content属性1.5.align-items属性1.6.flex-wrap属性2.子元素items 前言 怎样让一个元素在…

Vue2 概述

什么是Vue 引用官网的一段话:Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是&#xff0c;Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0c;不仅易于上手&#xff0c;还便于与第三方库或既有项…

如何统计前端项目有多少行代码

方法一&#xff1a;输入命令 前端项目核心代码主要在src目录下&#xff0c;打开项目找到src目录&#xff0c;右键点击 git bash here &#xff0c;然后输入命令&#xff1a; 1.包括空行&#xff08;会列出每个文件的代码行数&#xff09;&#xff1a; find . "(" …

Vue注册组件的几种方式,你都知道吗?

在开发中,经常需要将业务进行组件化,我们就需要去注册组件,下面列举几种常用的注册组件的方法 局部注册组件 1.首先需要将要注册的组件进行引入 2.在script标签中进行注册,注意:组件注册的名字和引入的名字需要保持一致即可进行简写 3.在template标签中以html的标签格式进行使…

web前端面试高频考点——Vue的高级特性(动态组件、异步加载、keep-alive、mixin、Vuex、Vue-Router)

系列文章目录 内容参考链接Vue基本使用Vue的基本使用&#xff08;一文掌握Vue最基础的知识点&#xff09;Vue通信和高级特性Vue组件间的通信及高级特性&#xff08;多种组件间的通信、自定义v-model、nextTick、插槽&#xff09;Vue高级特性Vue的高级特性&#xff08;动态组件…

Vue 进阶系列丨大文件切片上传

Vue 进阶系列教程将在本号持续发布&#xff0c;一起查漏补缺学个痛快&#xff01;若您有遇到其它相关问题&#xff0c;非常欢迎在评论中留言讨论&#xff0c;达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧&#xff01;2013年7月28日&#xff0c;尤雨溪第一次在 GItHub…

Layui的基本使用(前端登录操作步骤)

目录 一.介绍layui 二.Layui相关的基本操作 1.下载Layui网站 &#xff1a;Layui - 经典开源模块化前端 UI 框架 ----------接下来的操作是实现一个Layui前端登录的效果------------- 2.创建项目 3.使用mybatis plus自动生成代码 (1).导入依赖 (2).导入生成类 (3).运行…

vscode+live server——更改端口号——基础积累

最近在写前台&#xff0c;通过jq写的&#xff0c;之前我一直嚷嚷jq基础薄弱&#xff0c;现在练手的来了。。。。 运行页面的时候&#xff0c;可以使用live server 1.vscode软件中安装live server 2.在html页面右击&#xff0c;有个open with live server 3.电脑默认的浏览器会…

Vue3中使用hooks,hooks究竟是个啥?如何理解

1.前言 最近被hooks这个词老是被提出&#xff0c;我经常听到但是又不会用&#xff0c;于是乎抽时间认真学习一下。 2.vue3 hooks 2.1 为什么vue3 可以使用hooks 因为vue2由选项式Api转为组合式Api。底层源码改了好多。 组合式API的好处&#xff1b; 就是在函数内可以使用声…

使用pynecone开发python web应用

环境•windows 10 64bit•python 3.8.15•pynecone 0.1.14简介Pynecone 是一个基于纯 python 的用于开发 web app 的开源框架&#xff0c;它依赖于 nodejs&#xff0c;不过不需要另外编写前端代码&#xff0c;这对于没有接触过前端的朋友是非常友好的。安装第一步&#xff0c;安…

Vue项目常见的错误以及解决办法

错误分类 vue项目中遇到的问题大体上分为两类&#xff1a;一类是基础的语法错误&#xff0c;另一类就是其它错误 这里简单分享以下我在项目中遇到的错误和解决办法&#xff0c;如果你也有&#xff0c;希望对你会有帮助 GET http://dida100.com:8888/api/cart 401 (Unauthoriz…