如何使用 "file-type" NPM 模块客户端?

How to Use "file-type" NPM Module Client-Side?

我正在尝试使用 "file-type" NPM module(我在服务器上工作)客户端在将文件上传到 S3 存储桶之前验证 MIME 类型。

模块的 readme 包括在浏览器中使用它的示例:

const FileType = require('file-type/browser');

const url = 'https://upload.wikimedia.org/wikipedia/en/a/a9/Example.jpg';

(async () => {
    const response = await fetch(url);
    const fileType = await FileType.fromStream(response.body);

    console.log(fileType);
    //=> {ext: 'jpg', mime: 'image/jpeg'}
})();

由于“要求”,这显然不能直接在浏览器中工作,如果我直接 link 到 NPM 文件,我得到:

Uncaught ReferenceError: require is not defined

所以我尝试使用 Webpack(我一点也不精通,但已经遵循了官方的 Webpack 教程和其他一些教程)来创建一个“main.js”文件并然后通过脚本标签访问它。

例如,运行 这通过 Webpack:

import * as mimeFromBuffer  from 'file-type/browser.js';

export function mimeTyping(mimeValidationBytes){
    (async () => {
        const fileType = await mimeFromBuffer(mimeValidationBytes);
        console.log(fileType);
        //=> {ext: 'jpg', mime: 'image/jpeg'}
    })();
    return;
}

当我调用 mimeTyping 客户端时的结果:

Uncaught ReferenceError: mimeTyping is not defined

我已经在浏览器中尝试了 Vinay 的回答 from this question

import fileType from 'file-type'; 

const blob = file.slice(0, fileType.minimumBytes);

const reader = new FileReader();
reader.onloadend = function(e) {
  if (e.target.readyState !== FileReader.DONE) {
    return;
  }

  const bytes = new Uint8Array(e.target.result);
  const { ext, mime } = fileType.fromBuffer(bytes);

  // ext is the desired extension and mime is the mimetype
};
reader.readAsArrayBuffer(blob);

这让我明白了:

Uncaught SyntaxError: Cannot use import statement outside a module

我研究了一下这个错误,一个常见的解决方案是将 "type":"module" 添加到 package.json。这没有帮助(错误没有改变)。

我找到了一个 similar question in the module's Github repository,它表明:

Although this module is primary designed for Node.js, using it in the browser is possible as well. That is indeed where 'file-type/browser' intended for. It provides the right dependencies and right functions to the JavaScript module bundler. Some dependencies, which are typically present in a Node.js environment, but are missing in a browser environment, you may need to pass (polyfill) to your module bundler. Although in some cases it may a bit tricky to configure it, note that this is a pretty common task handled by a module bundler.

我很难理解建议的后续步骤。

该主题底部的另一条评论表明作者在以下方面取得了成功:

import { fromBuffer } from 'file-type/core';
...
const buffer = await uploadedFile.arrayBuffer();
const types = await fromBuffer(buffer);

我不确定如何实现它,以及我需要什么其他代码(我猜这会传递给 Webpack,并且可能需要一个 export 语句,然后是一个 import 在客户端?)

我试过将其传递到 Webpack 中:

const fileType = require('file-type/browser');

module.exports = fileType;

但是再次 link 到输出文件得到:

Uncaught SyntaxError: Cannot use import statement outside a module

认为 我从概念上理解我需要做什么:将 NPM 模块传递给 Webpack,Webpack 反过来解析它并找到任何依赖项,获取这些依赖项,并创建一个JavaScript 文件我可以使用客户端。看来我做错了什么。

我花了几天时间试图了解如何在客户端使用 NPM 模块(我脑子里仍然很模糊)并在上面的代码上尝试各种变体 - 非常感谢一些指导(第一次发布问题在这里 - 请对我放轻松!)。

谢谢!

编辑:我不认为这是重复的 - 我确实审查了 , Meteor Npm-module client-side?, how to use node.js module system on the clientside, and 但我尝试过的任何建议似乎都没有帮助。

终于成功了。万一其他人坚持这一点,这里有一个解释(抱歉不够简洁 - 可能这应该是一个博客 post...)。

为了进一步充实用例,我使用 Uppy 允许用户将文件上传到 AWS S3 存储桶。它的工作方式是,当用户上传文件时,Uppy 调用我的服务器,在服务器上生成 AWS 预签名 URL 并将其传回客户端。然后客户端使用预签名 URL 将文件直接上传到 S3 存储桶,绕过服务器,这样文件在任何时候都不会通过服务器。

我试图解决的问题是缺少扩展名的文件最终上传时内容/MIME 类型设置为“application/octet”,因为浏览器、Uppy 和 S3 似乎都依赖于文件扩展名来决定文件类型(而不是解析文件的所谓“魔术字节”),如果缺少文件扩展名,AWS 默认为“application/octet”。当用户尝试打开文件时,这会导致问题,因为它们没有得到正确处理(即没有扩展名且内容/MIME 类型为“application/octet”的 png 文件打开下载对话框而不是预览等。 ).我还想在扩展名存在的情况下验证 MIME 类型/文件类型,以便我可以排除某些类型的文件,以便在稍后下载文件时对文件进行适当处理(MIME 类型将再次被验证)如果使用了不正确的文件扩展名。

我使用“文件类型”NPM 模块来确定服务器端的 mimetype,这很简单,但是在生成 AWS 预签名 URL 时更改文件的内容类型/MIME 类型是不足以解决问题 - 它仍然以“application/octet”的形式上传。我想在客户端使用相同的模块,因此我们在客户端上获得与在服务器上完全相同的结果,并且在任何情况下都需要确定 MIME 类型并在上传前相应地设置它,但是 post-pre-签名 URL。我不知道该怎么做(即使用“文件类型”客户端——我的问题所在)。

我终于放弃了 Webpack——我试过的都没有用。所以我切换到 Browserify,“文件类型”存储库中的示例浏览器代码立即运行!因此,我只能尝试弄清楚如何通过 Browserify 传递函数以在客户端代码中使用。

事实证明这对我来说是不可能的——我不知道如何将异步 IIFE 传递到我的代码中。因此,我将我的 Uppy 代码移到了传递给 Browserify 的代码中:

// 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);
})

这一切都进入了一个我称之为“index.js”的文件,然后,通过“npm install -g browserify”在命令行安装了 Browserify,我在命令行使用它来创建文件("main.js") 我 link 在我的客户端代码中:

browserify index.js -o main.js