简体   繁体   English

如何使用“文件类型”NPM 模块客户端?

[英]How to Use “file-type” NPM Module Client-Side?

I'm attempting to use the "file-type" NPM module (which I have working on the server) client side to validate mime type prior to a file upload to an S3 bucket.我正在尝试使用“文件类型”NPM 模块(我在服务器上工作)客户端在将文件上传到 S3 存储桶之前验证 mime 类型。

The readme for the module includes an example of using it in the browser:该模块的 自述文件包括一个在浏览器中使用它的示例:

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'}
})();

That obviously won't work directly in the browser due to the "require", and if I link to the NPM file directly, I get:由于“要求”,这显然不能直接在浏览器中工作,如果我直接链接到 NPM 文件,我会得到:

Uncaught ReferenceError: require is not defined

So I've tried using Webpack (with which I'm not well versed at all, but have followed the official Webpack tutorial, and a few other tutorials) to create a "main.js" file and then accessed that via a script tag.所以我尝试使用 Webpack (我一点也不精通,但遵循官方 Webpack 教程和其他一些教程)创建一个“main.js”文件,然后通过脚本标签访问它.

For example, running this through Webpack:例如,通过 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;
}

Results in this when I call the mimeTyping client side:当我调用mimeTyping客户端时,结果如下:

Uncaught ReferenceError: mimeTyping is not defined

I've tried Vinay's answer from this question in the browser:我已经在浏览器中尝试过 Vinay 对这个问题的回答:

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

Which gets me this:这让我明白了:

Uncaught SyntaxError: Cannot use import statement outside a module

I've researched that error a bit, with a common solution being to add "type":"module" to package.json .我已经对该错误进行了一些研究,一个常见的解决方案是将"type":"module"添加到package.json This did not help (error was unchanged).这没有帮助(错误没有改变)。

I found a similar question in the module's Github repository , which suggests:在模块的 Github 存储库中发现了一个类似的问题,这表明:

Although this module is primary designed for Node.js, using it in the browser is possible as well.虽然这个模块主要是为 Node.js 设计的,但也可以在浏览器中使用它。 That is indeed where 'file-type/browser' intended for.这确实是“文件类型/浏览器”的用途。 It provides the right dependencies and right functions to the JavaScript module bundler.它为 JavaScript 模块捆绑器提供正确的依赖关系和正确的功能。 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.一些依赖项,通常存在于 Node.js 环境中,但在浏览器环境中缺失,您可能需要将 (polyfill) 传递给模块捆绑程序。 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.尽管在某些情况下配置它可能有点棘手,但请注意,这是一个由模块捆绑器处理的非常常见的任务。

I'm struggling to understand what next steps that suggests.我正在努力理解下一步的建议。

Another comment at the bottom of that thread indicates the author had success with:该线程底部的另一条评论表明作者在以下方面取得了成功:

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

I'm not sure how to implement that, and what other code I need (I'm guessing this gets passed to Webpack, and probably requires an export statement, and then an import on the client side?)我不确定如何实现它,以及我需要什么其他代码(我猜这会传递给 Webpack,可能需要一个export语句,然后在客户端import一个?)

I've tried passing this into Webpack:我尝试将其传递给 Webpack:

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

module.exports = fileType;

But linking to the output file again gets:但是再次链接到 output 文件会得到:

Uncaught SyntaxError: Cannot use import statement outside a module

I think I understand conceptually what I need to do: pass the NPM module to Webpack, which in turn parses it and finds any dependencies, gets those dependencies, and creates a JavaScript file I can use client side.我从概念上理解我需要做什么:将 NPM 模块传递给 Webpack,后者依次解析它并找到任何依赖项,获取这些依赖项,并创建一个 Z686155AF75A60A0F6E9D80C1F7EDD 文件可以使用客户端 ID。 It seems I'm doing something in there wrong.看来我在那里做错了什么。

I've spent days trying to understand how to use NPM modules client side (still quite foggy in my mind) and trying all sorts of variants on the above code - would really appreciate some guidance (first time posting a question here - please go easy on me.).我花了几天时间试图了解如何使用 NPM 模块客户端(在我的脑海中仍然很模糊)并尝试上述代码的各种变体 - 非常感谢一些指导(第一次在这里发布问题 - 请 go 容易包在我身上。)。

Thank you!谢谢!

Edit: I don't think this is a duplicate - I did review How to import JS library from node_modules , Meteor Npm-module client-side?编辑:我不认为这是重复的 - 我确实审查了如何从 node_modules 导入 JS 库,Meteor Npm-module 客户端? , how to use node.js module system on the clientside , How to call a server-side NodeJS function using client-side JavaScript and Node Js application in client machine but no suggestion I tried in any of those seemed to help. 如何在客户端使用 node.js 模块系统如何使用客户端 JavaScript 调用服务器端 NodeJS function

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

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".我试图解决的问题是缺少扩展名的文件最终以内容/MIME 类型设置为“application/octet”的方式上传,因为浏览器、Uppy 和 S3 似乎都依赖文件扩展名来决定文件类型(而不是解析文件的所谓“魔术字节”),如果缺少文件扩展名,AWS 默认为“应用程序/八位字节”。 This causes issues when users attempt to open the file, as they are not handled correctly (ie a png file without an extension and with an "application/octet" content / MIME type opens a download dialog rather than being previewed, etc.).当用户尝试打开文件时,这会导致问题,因为它们没有得到正确处理(即没有扩展名且具有“应用程序/八位字节”内容/MIME 类型的 png 文件打开下载对话框而不是预览等)。 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.即使存在扩展名,我也想验证 MIME 类型/文件类型,以便我可以排除某些类型的文件,以便以后下载文件时得到适当的处理(将再次验证 MIME 类型)如果使用了不正确的文件扩展名。

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".我使用“文件类型”NPM 模块来确定服务器端的 mimetype,这很简单,但是在生成 AWS 预签名 URL 时更改文件的内容类型/MIME 类型不足以解决问题 - 它仍然被上传为“应用程序/八位字节”。 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.我想使用相同的模块客户端,因此我们在客户端上获得与服务器上完全相同的结果,并且在任何情况下都需要确定 MIME 类型并相应地设置它,预上传但预签名后预签名 URL。 I had no idea how to do this (ie use "file-type" client side - the meat of my question).我不知道如何做到这一点(即使用“文件类型”客户端 - 我的问题的核心)。

I finally gave up on Webpack - nothing I tried worked.我终于放弃了 Webpack - 我没有尝试过。 So I switched to Browserify, and the sample browser code at the "file-type" repository worked at once.所以我切换到 Browserify,“文件类型”存储库中的示例浏览器代码立即工作。 So then I was left trying to figure out how to pass a function through Browserify to use in the client side code.因此,我不得不试图弄清楚如何通过 Browserify 传递 function 以在客户端代码中使用。

This proved impossible for me - I couldn't figure out how to pass the asynchronous IIFE through to my code.这对我来说是不可能的——我不知道如何将异步 IIFE 传递给我的代码。 So instead, I moved my Uppy code into the code I pass to Browserify:因此,我将 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);
})

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

browserify index.js -o main.js

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM