乐闻世界logo
搜索文章和话题

JS 如何在客户端使用 file-type 检测文件类型?

6 个月前提问
3 个月前修改
浏览次数128

2个答案

1
2

在JavaScript中,要在客户端使用file-type来检测文件类型,首先需要引入file-type这个库。这个库可以帮助我们读取文件的二进制数据,并分析这些数据来确定文件的实际类型。以下是如何使用file-type库检测文件类型的步骤:

  1. 安装file-type库。如果你在使用的是现代前端项目,可能会使用npm或yarn来安装。可以运行以下命令来安装它:
bash
npm install file-type # 或者 yarn add file-type
  1. 引入file-type模块。在你的JavaScript模块中,你可以使用import导入file-type。
javascript
import fileType from 'file-type';
  1. 读取文件。在浏览器中,通常我们可以通过<input type="file">来让用户选择文件,然后使用FileReader API读取这个文件的二进制数据。

  2. 使用file-type库来检测文件类型。file-type接受一个BufferUint8Array,并返回一个包含文件类型信息的对象。

以下是一个完整的示例,展示了如何在浏览器中使用file-type来检测用户选择的文件类型:

html
<!-- index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>File Type Detection</title> </head> <body> <input type="file" id="fileInput"> <script type="module"> import fileType from 'file-type/browser'; document.getElementById('fileInput').addEventListener('change', async (event) => { const file = event.target.files[0]; if (!file) { console.log("No file selected!"); return; } const arrayBuffer = await file.arrayBuffer(); const typedArray = new Uint8Array(arrayBuffer); const result = await fileType.fromBuffer(typedArray); if (result) { console.log(`File type: ${result.mime}`); console.log(`File extension: ${result.ext}`); } else { console.log("Could not determine the file type."); } }); </script> </body> </html>

上面的代码中,我们首先在HTML文件中创建了一个文件输入元素。然后在JavaScript模块中,我们引入了file-type的浏览器版本,并为文件输入元素添加了change事件监听器。当用户选择文件时,我们读取这个文件的内容,将其转换为Uint8Array,然后使用file-typefromBuffer方法分析这个数组,最终输出文件的类型信息。

需要注意的是,file-type版本更新可能会带来API的变化,因此在使用时需要参考当时最新的官方文档。

2024年6月29日 12:07 回复

Finally got this working. In case anyone else is stuck on this, here's an explanation (apologies for the lack of brevity - probably this should be a blog post...).

To flesh out the use case a bit further, I'm using Uppy to allow users to upload files to an AWS S3 bucket. The way this works is that, when the user uploads a file, Uppy makes a call to my server where an AWS pre-signed URL is generated and passed back to the client. The client then uses that pre-signed URL to upload the file directly to the S3 bucket, bypassing the server, such that the file doesn't pass through the server at any point.

The problem I was attempting to solve was that files missing an extension ended up uploaded with the content / MIME type set as "application/octet", because it seems the browser, Uppy, and S3 all rely on the file extension to decide the file type (rather than parsing the so-called "magic bytes" of the file), and if the file extension is missing, AWS defaults to "application/octet". This causes issues when users attempt to open the file, as they are not handled correctly (i.e. a png file without an extension and with an "application/octet" content / MIME type opens a download dialog rather than being previewed, etc.). I also want to validate the MIME type / file type in cases even where the extension exists so that I can exclude certain types of files, and so the files get handled appropriately when they are later downloaded (where the MIME type will again be validated) if an incorrect file extension is used.

I use the "file-type" NPM module to determine the mimetype server side, and that's straight forward enough, but changing the file's content type / MIME type when generating the AWS pre-signed URL is not enough to fix the problem - it still gets uploaded as "application/octet". I wanted to use the same module client side so we get the exact same results on the client as on the server, and needed in any case to determine the MIME type and set it accordingly pre-upload but post-pre-signed URL. I had no idea how to do this (i.e. use "file-type" client side - the meat of my question).

I finally gave up on Webpack - nothing I tried worked. So I switched to Browserify, and the sample browser code at the "file-type" repository worked at once! So then I was left trying to figure out how to pass a function through Browserify to use in the client side code.

This proved impossible for me - I couldn't figure out how to pass the asynchronous IIFE through to my code. So instead, I moved my Uppy code into the code I pass to Browserify:

shell
// Get the dependency, which we've added to node via "npm install file-type": const FileType = require('file-type/browser'); // When the user adds a file for upload to Uppy... uppy.on('file-added', (file) => { // 1. Create a filereader: const filereader = new FileReader(); filereader.onloadend = function(evt) { // 4. Once the filereader has successfully finished reading the file... if (evt.target.readyState === FileReader.DONE) { // Get the unsigned 8 bit int8Array (ie Uint8Array) of the 600 bytes (this looks like '[119,80,78,71...]'): const uint = new Uint8Array(evt.target.result); // Determine the mime type using the "file-type" NPM package: (async () => { // Pass in our 600 bytes ("uint"): const fileType = await FileType.fromBuffer(uint); console.log(fileType); // outputs => {ext: 'jpg', mime: 'image/jpeg'} // Do some validation here... // // Assign the results to the file for upload - we're done!: file.extension = fileType.ext; file.meta.type = fileType.mime; file.type = fileType.mime; })(); } } // 2. Grab the first 600 bytes of the file for mime type analysis server side - most mime // types use the first few bytes, but some (looking at you, Microsoft...) start at the // 513th byte; ISO CD files start at the 32,770th byte, so we'll ignore those rather than // send that much data for each file - users of this system certainly aren't expected to // upload ISO CD files! Also, some .zip files may start their identifying bytes at the // 29,153nd byte - we ignore those too (mostly, .zip files start at the first, 31st, or 527th byte). const blob = file.data.slice(0, 600); // 3. Start reading those 600 bytes...continues above at 'filereader.onloadend': filereader.readAsArrayBuffer(blob); })

That all goes into a file I call "index.js" and then, having installed Browserify at the command line via "npm install -g browserify", I use this at the command line to create the file ("main.js") I link to in my client side code:

shell
browserify index.js -o main.js
2024年6月29日 12:07 回复

你的答案