小樱知识 > 数码解读怎么更改文件类型(「译」JavaScript二进制判断文件类型比后缀更靠谱)

怎么更改文件类型(「译」JavaScript二进制判断文件类型比后缀更靠谱)

提问时间:2022-12-26 15:16:46来源:小樱知识网


1.借助 input 元素限制文件类型

在日常工作中,文件上传是一个很常见的功能。 在某些情况下,我们希望能够限制上传文件的类型,比如限制上传 PNG 格式的图片。 对于这个问题,首先想到的是通过 input 元素的 accept 属性来限制上传的文件类型:

<input type="File" id="inputFile" accept="image/png" />

虽然这个方案可以满足大部分场景,但是如果用户将 JPEG 格式图片的后缀改为.png,则可以成功突破这个限制。 那么这个问题应该怎么解决呢? 我们可以通过读取文件的二进制数据来识别正确的文件类型。 在介绍实际的实现方案之前,先介绍一下相关的知识。

2.获取图片的二进制数据

查看图片对应的二进制数据,可以使用一些编辑器,比如 Windows 平台下的 WinHex 或者 Synalyze It! macOS 平台下的 Pro 十六进制编辑器。 不过,这里我们使用 Visual Studio Code 编辑器中的 Binary Viewer 扩展程序来查看个人头像对应的二进制数据。

3.识别图片文件类型

计算机不是通过图片的后缀名来区分不同的图片类型,而是通过“特殊数字”来区分。 对于特定类型的文件,前几个字节的内容是固定的,可以根据这些字节的内容来判断文件的类型。不同图片类型的”特殊数字“如下:

下面使用 Binary Viewer 扩展程序来验证头像图像类型是否正确?

从上图可以看出,PNG 类型图片的前 8 个字节为 0x89 50 4E 47 0D 0A 1A 0A。 将 bytefer-avatar.png 文件改为 bytefer-avatar.jpeg,用编辑器打开查看图片的二进制内容,会发现文件的前 8 个字节没有变化。 但是如果使用 input[type=”file”]输入元素读取文件信息,则会输出如下结果:

File lastModified: 1658647747405 lastModifiedDate: Sun Jul 24 2022 15:29:07 name: "bytefer-avatar.jpeg" size: 47318 type: "image/jpeg" // 变成了jpeg webkitRelativePath: "" [[Prototype]]: File

例子表明 L 文件扩展名或文件的 MIME 类型无法识别正确的文件类型, 下面介绍上传图片时如何通过读取图片的二进制信息来保证图片类型正确。

4.如何识别图片类型

4.1 定义 readBuffer 函数

拿到文件对象后,可以通过 FileReader API 读取文件的内容。 因为不需要读取文件的完整信息,所以封装了一个 readBuffer 函数来读取文件中指定范围的二进制数据。

function readBuffer(file, start = 0, end = 2) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => { resolve(reader.result); }; reader.onerror = reject; reader.readAsArrayBuffer(file.slice(start, end)); }); }

对于 PNG 类型的图像,文件的前 8 个字节是 0x89 50 4E 47 0D 0A 1A 0A。 因此,在检测选择的文件是否为 PNG 类型图片时,只需要读取数据的前 8 个字节,逐个判断每个字节的内容是否一致。

4.2 定义 check 函数

为了实现逐字节比较和更好的代码重用,继续定义一个检查函数:

function check(headers) { return (buffers, options = { offset: 0 }) => headers.every( (header, index) => header === buffers[options.offset + index] ); }

4.3 识别 PNG 文件类型

基于前面定义的 readBuffer 和 check 函数,可以实现检测 PNG 图片的功能。HTML 内容如下:

<div> Choose File:<input type="file" id="inputFile" accept="image/*" onchange="handleChange(event)" /> <p id="realFileType"></p> </div>

JavaScript 代码如下:

const isPNG = check([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); const realFileElement = document.querySelector('#realFileType'); async function handleChange(event) { const file = event.target.files[0]; const buffers = await readBuffer(file, 0, 8); const uint8Array = new Uint8Array(buffers); realfileElement.innerText = `The type of ${file.name} is:${ isPNG(uint8Array) ? 'image/png' : file.type }`; }

上述示例运行成功后,对应的检测结果如下图所示:

完整示例代码如下:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>File Type Detect Demo</title> </head> <body> <div> <input type="file" id="inputFile" accept="image/*" onchange="handleChange(event)" /> <p id="realFileType"></p> </div> <script> function check(headers) { return ( buffers, options = { offset: 0, } ) => headers.every( (header, index) => header === buffers[options.offset + index] ); } function readBuffer(file, start = 0, end = 2) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => { resolve(reader.result); }; reader.onerror = reject; reader.readAsArrayBuffer(file.slice(start, end)); }); } const isPNG = check([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); const realFileElement = document.querySelector('#realFileType'); async function handleChange(event) { const file = event.target.files[0]; const buffers = await readBuffer(file, 0, 8); const uint8Array = new Uint8Array(buffers); realFileElement.innerText = `The type of ${file.name} is:${ isPNG(uint8Array) ? 'image/png' : file.type }`; } </script> </body> </html>

如果需要判断 JPG 文件类型,需要重新定义一个 isJPEG 函数。

const isJPEG = check([0xff, 0xd8, 0xff]);

5.如何识别非图片类型

如果你想检测其他类型的文件,比如 PDF 文件呢?需要首先使用 Binary Viewer 扩展来查看 PDF 文件的二进制内容:

从上图可以看出,PDF 文件的前 4 个字节是 0x25 50 44 46,对应的字符串是%PDF。为了让用户更直观地识别检测类型,可以定义一个 stringToBytes 函数:

function stringToBytes(string) { return [...string].map((character) => character.charCodeAt(0)) }

基于 stringToBytes 函数,可以很容易地定义一个 isPDF 函数,如下所示:

const isPDF = check(stringToBytes('%PDF'));

使用 isPDF 函数,可以实现 PDF 文件检测功能。 但是在实际工作中,会遇到各种类型的文件。 对于这种情况,可以使用一个优秀的第三方库来实现文件检测的功能,比如 file-type 库。

这就是如何使用 JavaScript 检测文件类型。 实际工作中,对于文件上传场景,出于安全考虑,建议大家在开发过程中限制文件上传的类型。 对于更严格的场景,可以考虑使用本文介绍的方法验证文件类型。

参考资料

https://medium.com/frontend-canteen/how-to-detect-file-type-using-javascript-251f67679035

https://github.com/sindresorhus/file-type#readme

以上内容就是为大家推荐的怎么更改文件类型(「译」JavaScript二进制判断文件类型比后缀更靠谱)最佳回答,如果还想搜索其他问题,请收藏本网站或点击搜索更多问题

内容来源于网络仅供参考
二维码

扫一扫关注我们

版权声明:所有来源标注为小樱知识网www.xiaoyin02.com的内容版权均为本站所有,若您需要引用、转载,只需要注明来源及原文链接即可。

本文标题:怎么更改文件类型(「译」JavaScript二进制判断文件类型比后缀更靠谱)

本文地址:https://www.xiaoyin02.com/smjd/849354.html

相关文章