当前位置: 首页 > news >正文

前端JavaScript获取图片文件的真实格式

常见方式判断图片格式

当我们进行前端开发,需要处理图片上传功能,针对图片格式做判断时,常规的方法都是使用文件后缀名来判断,如下代码所示:

input.addEventListener('change', (e) => {const file = e.target.files[0]const format = file.name.substring(file.name.lastIndexOf('.') + 1).toLowerCase()
}, false) 

以上代码,监听上传控件的事件,得到要上传的文件信息,获取文件名称,然后通过获取文件名称截取文件后缀名,以后缀名作为图片文件的格式。这段代码,大部分人都比较熟悉,也有很多场景下是这样来判断图片格式的,但如果我们强行修改了文件的后缀名,则此方法就失效了。我们知道gif格式图片的位深度是8,如果我们强制把位深度为32的png格式的图片后缀名改成gif,这个图片文件依然可以正常使用:

上图所示,就是将png格式文件后缀名改成了gif,图片系统信息显示格式为gif,但是位深度还是32,图像本质上还是png格式的。

这个时候,单纯的通过后缀名来判断图片的格式,就不再准确了,我们需要另外的方式来获取图片文件的真实格式。而这种方式就需要使用到前端二进制相关的知识,见前文介绍深入理解前端字节二进制知识以及相关API。

修改后缀名的方式

几种位图格式之间,是可以相互修改后缀名,图片仍能正常使用> > gif动图后缀名改成其他位图格式,则动效会失效,变成静态图> > 位图格式的后缀名如果改成矢量图svg,则图片失效,将无法使用> > svg图片文件后缀名改成位图格式,图片也将无法使用

图像数据简单说明

不同格式的图像所存储的数据是不一样的,都有自己特殊的数据结构。依据各个格式图像不同的数据结构,我们通过类型数组中的图像数据,就能判断出图片的真实格式。如jpg格式,它的图像数据结构中,最前面2个字节是一个固定取值 0xFFD8,第三个字节一般也是固定 0xFF。如png格式,它的图像数据结构中,最前面8个字节就是PNG文件署名域,可以很好的标识出当前图像的格式就是PNG。如bmp格式,它的图像数据结构中,最前面14个字节存储的是文件头信息,而最前面2个字节存储的就是文件类型:BM。如webp格式,需要从最前面移动8个字节以后,取接下来的4个字节的信息,代表文件类型:WEBP

针对不同位图的的数据判断,可以使用下面表格列出的方式:

格式标识的字节数对应的十进制值偏移量
jpg3255 216 2550
png8137 80 78 71 13 10 26 100
gif371, 73, 700
webp487, 69, 66, 808
ico40, 0, 1, 00
bmp266 770

其中,偏移量为0,表示取最前面几个字节的数据;webp的偏移量为8,表示从最前面移动8个字节后,再取4个字节的标识符。上面的表格,已经列出了当前浏览器支持的位图图像,字节判断标识,通过读取相应的数据做对比就得到了真实的格式。

以上几种格式中,bmp、gif、webp取到的数据,都能对应各自特有的署名标识,前面有提到 BMWEBP,gif格式的则是 GIF。可以运用字符编码方面的知识,如使用 String.fromCharCode 方法对数值进行转换,具体的前端字符编码知识见前文前端开发中需要搞懂的字符编码

// bmp
String.fromCharCode(66) // B
String.fromCharCode(77) // M// gif
String.fromCharCode(71) // G
String.fromCharCode(73) // I
String.fromCharCode(70) // F// webp
String.fromCharCode(87) // W
String.fromCharCode(69) // E
String.fromCharCode(66) // B
String.fromCharCode(80) // P 

gif格式的署名标识是和版本号一起处理的,一般最前面6个字节标识: 'G'、'I'、'F'、'8'、'7(9)'、'a'。第5个字节可取值7或者9,代表两个不同的版本,即1987年的版本和1989年的版本。

JS读取图片真实格式

当我们了解了前端二进制相关的知识后,就应该知道图片文件也是能通过WebAPI对象,读取到对应的数据:

const reader = new FileReader()
reader.onload = () => {const imgArrayBuffer = reader.resultconst imgUint8Array = new Uint8Array(imgArrayBuffer)
}
reader.readAsArrayBuffer(file) 

以上代码,就是通过 FileReader 对象读取文件的数据,这里是作为 ArrayBuffer 来读取的,然后就可以转换成类型数组进行处理了。

读取到图片文件的 Uint8Array 类型数组数据后,根据上文表格中提到的格式字节数据标识,我们以jpg、bmp和webp为例:

imgUint8Array[0] === 66 && imgUint8Array[1] === 77 // bmp 格式
imgUint8Array[0] === 255 && imgUint8Array[1] === 216 && imgUint8Array[3] === 255 // jpg 格式
imgUint8Array[8] === 87 && imgUint8Array[9] === 69 && imgUint8Array[10] === 66 && imgUint8Array[10] === 80 // webp 格式 

到此,就可以使用这种方式来读取到图片的真实格式,部分判断代码如下:

// 各格式对应图像数据的标识数值
const IMAGEFORMATS = [{ ext: 'png', data: [137, 80, 78, 71, 13, 10, 26, 10] },{ ext: 'jpg', data: [255, 216, 255] },{ ext: 'gif', data: [71, 73, 70] },{ ext: 'ico', data: [0, 0, 1, 0] },{ ext: 'bmp', data: [66, 77] },{ ext: 'webp', data: [87, 69, 66, 80], offset: 8 }
]// 循环判断文件是否符合某个格式对应的标识数值
for (let i = 0; i < IMAGEFORMATS.length; i++) {const { data, offset, ext } = IMAGEFORMATS[i]if (isEqualFormatPrefix(imgUint8Array, data, offset)) {return ext}
} 

不过以上的方式主要是针对位图,如果是svg的图片,则会稍微复杂一些,需要另行处理。

svg格式的判断

svg格式图片是矢量图,对应的数据一般使用 xml 标记语言进行描述,所以我们读取到图像数据后,需要对应的标识署名是 <svg,如果对应的图像数据中拥有该标识,则大致可以判定为svg格式的图片。<svg 标识有4个符号和字母,对应的数值:60, 115, 118, 103,接下来我就需要判断图像文件是否有同样的数据了。

imgUint8Array[0] === 60 && imgUint8Array[1] === 115 && imgUint8Array[3] === 118 && imgUint8Array[3] === 103 // svg 格式 

以上代码就是简单的判断svg格式了。但是,我们一般的svg图片,图像数据最开始是包含有xml标记语言的 <?xm 标签,这个时候我们根据格式再判断:

if (isEqualFormatPrefix(fileUint8Array, [60, 63, 120, 109], offset)) { // 判断是否以 <?xm 开头if (isHasSignCodes(fileUint8Array, [60, 115, 118, 103])) { // 判断是否包含 <svg 标签return'svg'}
} 

注意:以上针对svg格式矢量图的这种判断方式,是以 xml 标记语言的标签符号进行判断的,只能处理通过更改后缀名的方式伪造的图片文件。当我们伪造一个假的文件,包含有 <svg 标签标识时,则可以逃避这种判断。

总结

浏览器支持的图片格式中,除了svg以外,其他几种位图格式,都可以较好的通过读取图像二进制数据的方式判断出图片文件的真实格式,能够防止文件伪造绕开判断,造成不必要的异常等问题。

最后

最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

相关文章:

前端JavaScript获取图片文件的真实格式

常见方式判断图片格式 当我们进行前端开发&#xff0c;需要处理图片上传功能&#xff0c;针对图片格式做判断时&#xff0c;常规的方法都是使用文件后缀名来判断&#xff0c;如下代码所示&#xff1a; input.addEventListener(change, (e) > {const file e.target.files[…...

今天面了一个来华为要求月薪25K,明显感觉他背了很多面试题...

最近有朋友去华为面试&#xff0c;面试前后进行了20天左右&#xff0c;包含4轮电话面试、1轮笔试、1轮主管视频面试、1轮hr视频面试。 据他所说&#xff0c;80%的人都会栽在第一轮面试&#xff0c;要不是他面试前做足准备&#xff0c;估计都坚持不完后面几轮面试。 其实&…...

11 Advanced CNN

文章目录GoogLeNetInception Module1x1 Conv计算效果代码实现总结ResNet (残差网络)问题引入梯度消失与传统神经网络的比较代码实现课程来源&#xff1a; 链接对于前篇中所提到问题&#xff0c;设计出的是一种类似于LeNet5的线性结构&#xff0c;而对于大多数问题&#xff0c;简…...

亿级高并发电商项目---万达商城项目搭建(二)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是小童&#xff0c;Java开发工程师&#xff0c;CSDN博客博主&#xff0c;Java领域新星创作者 &#x1f4d5;系列专栏&#xff1a;前端、Java、Java中间件大全、微信小程序、微信支付、若依框架、Spring全家桶 &#x1f4…...

UML术语标准和分类

一、UML术语标准 1&#xff0e;中文UML术语标准 中国软件行业协会&#xff08;CSIA&#xff09;与日本UML建模推进协会&#xff08;UMTP&#xff09;共同在中国推动的UML专家认证&#xff0c;两个协会共同颁发认证证书、两国互认&#xff0c;CSIA与UMTP共同推出了UML中文术语…...

LeetCode 刷题系列 -- 151. 反转字符串中的单词

给你一个字符串 s &#xff0c;请你反转字符串中 单词 的顺序。单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。注意&#xff1a;输入字符串 s中可能会存在前导空格、尾随空格或…...

二十二、Gtk4-ListView

GTK 4添加了新的列表对象GtkListView、GtkGridView和GtkColumnView。这个新特性在Gtk API参考—列表小构件概述中有描述。 GTK 4还有其他实现列表的方法。它们是GtkListBox和GtkTreeView&#xff0c;它们是从GTK 3接管的。在Gtk开发博客中有一篇关于Matthias Clasen所写的列表…...

ASP.NET Core3.1实战教程---基于Jquery单文件上传

这个必须记录一下费劲啊&#xff01;废了我2天的时间&#xff0c;昔日的net快速已经没落....就文件上传都这么费劲。 先说下要求&#xff08;在线apk文件上传实现手机端整包更新&#xff09;&#xff1a; 1、为了简化需求文件上传和数据提交分开执行 2、选完文件后按钮变成上…...

10 卷积神经网络CNN(基础篇)

文章目录全连接CNN过程卷积过程下采样过程全连接层卷积原理单通道卷积多通道卷积改进多通道总结以及课程代码卷积改进PaddingStride下采样过程大池化层&#xff08;Max Pooling&#xff09;简单卷积神经网络的实现课程代码本篇课程来源&#xff1a; 链接部分文本来源参考&#…...

Windows下LuaBridge2.8的环境配置及简单应用

Windows下LuaBridge2.8的环境配置及简单应用 LuaBridge2.8下载链接&#xff1a; https://github.com/vinniefalco/LuaBridge/tags 关于Lua的环境配置可参考以下链接&#xff08;这里不做简述&#xff09;&#xff1a; https://ufgnix0802.blog.csdn.net/article/details/125341…...

每天10个前端小知识 【Day 10】

前端面试基础知识题 1. es5 中的类和es6中的class有什么区别&#xff1f; 在es5中主要是通过构造函数方式和原型方式来定义一个类&#xff0c;在es6中我们可以通过class来定义类。 class类必须new调用&#xff0c;不能直接执行。 class类执行的话会报错&#xff0c;而es5中…...

【LeetCode】1223. 掷骰子模拟

1223. 掷骰子模拟 题目描述 有一个骰子模拟器会每次投掷的时候生成一个 1 到 6 的随机数。 不过我们在使用它时有个约束&#xff0c;就是使得投掷骰子时&#xff0c;连续 掷出数字 i 的次数不能超过 rollMax[i]&#xff08;i 从 1 开始编号&#xff09;。 现在&#xff0c;…...

SPSS数据分析软件的安装与介绍(附网盘链接)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…...

2022年38女神节大促美妆、珠宝、母婴、保健电商数据回顾

近期&#xff0c;我们陆续接收到了品牌商家朋友们对于2022年女神节大促期间部分品类的数据需求&#xff0c;希望能对今年的大促活动有一个更宏观的认知、更精准的预测&#xff0c;从而拿到更好的数据效果。 为此&#xff0c;在距离大促开启一个月的备货阶段&#xff0c;鲸参谋决…...

Java笔记-线程同步

目录线程的同步---以三个窗口售票100张为例方式一&#xff1a;同步代码块方式二&#xff1a;同步方法使用同步机制的作用&#xff1a;线程的同步—以三个窗口售票100张为例 &#xff08;1&#xff09;问题&#xff1a;卖票的过程出现重票和错票 &#xff08;2&#xff09;原因…...

通过python 调用OpenAI api_key提交问题解答

通过python 调用OpenAI api_key提交问题解答✨可以通过网页版的jupyter notebook调用&#xff0c;也可以通过spyder窗口等IDE窗口. &#x1f33c;通过python 调用OpenAI api_key接口&#xff0c;可以避免国内网页不能访问的问题。前提是需要自己已经注册了OpenAI帐号&#xff…...

图表控件LightningChart .NET再破世界纪录,支持实时可视化 1 万亿个数据点

LightningChart.NET SDK 是一款高性能数据可视化插件工具&#xff0c;由数据可视化软件组件和工具类组成&#xff0c;可支持基于 Windows 的用户界面框架&#xff08;Windows Presentation Foundation&#xff09;、Windows 通用应用平台&#xff08;Universal Windows Platfor…...

什么是响应性?

响应性&#xff1a; 这个术语在今天的各种编程讨论中经常出现&#xff0c;但人们说它的时候究竟是想表达什么意思呢&#xff1f;本质上&#xff0c;响应性是一种可以使我们声明式地处理变化的编程范式。一个经常被拿来当作典型例子的用例即是 Excel 表格&#xff1a; 这里单元…...

黑马】后台管理176-183

一、新建订单管理的分支二、创建一个订单管理的vue文件进行组件页面的路由配置import Order from ../components/order/Order.vue{path:/orders,component:Order},注意上面的components不要忘记少加一个s&#xff01;三&#xff0c;获取后台数据面包屑导航粘贴过来文本输入框&a…...

Typescript - 类型守卫(typeof / in / instanceof / 自定义类型保护的类型谓词)通俗易懂详细教程

前言 类型守卫用于获取变量类型信息&#xff0c;通常使用在条件块语句中。类型守卫是返回布尔值的常规函数&#xff0c;接受一个类型并告诉 TypeScript 是否可以缩小到更具体的类型。类型守卫具有唯一的属性&#xff0c;可以确保测试的值返回的是布尔值类型。 TypeScript 使用了…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

C# 表达式和运算符(求值顺序)

求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如&#xff0c;已知表达式3*52&#xff0c;依照子表达式的求值顺序&#xff0c;有两种可能的结果&#xff0c;如图9-3所示。 如果乘法先执行&#xff0c;结果是17。如果5…...

前端开发者常用网站

Can I use网站&#xff1a;一个查询网页技术兼容性的网站 一个查询网页技术兼容性的网站Can I use&#xff1a;Can I use... Support tables for HTML5, CSS3, etc (查询浏览器对HTML5的支持情况) 权威网站&#xff1a;MDN JavaScript权威网站&#xff1a;JavaScript | MDN...