您现在的位置是:首页 > 技术教程 正文

前端文件上传识别文件类型的几种方法,快看你是哪个?

admin 阅读: 2024-03-30
后台-插件-广告管理-内容页头部广告(手机)

在我们的日常开发过程中,我们会经常接触到一些文件上传的事情,其中在前端这边识别识别文件类型的是非常常见的功能,例如来限制文件上传的类型,接下来我们来了解一下最常见的几种方式。

通过文件扩展名判断类型

最简单快捷的方法就是 hiyaJavaScript 获取文件名的扩展名,对比扩展名来判断文件类型,如下代码所示:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. <title>文件类型识别</title>
  7. </head>
  8. <body>
  9. <h2>文件类型识别</h2>
  10. <input type="file" id="fileInput" />
  11. <p id="fileType">文件类型</p>
  12. <script>
  13. document
  14. .getElementById("fileInput")
  15. .addEventListener("change", function (event) {
  16. const file = event.target.files[0];
  17. if (!file) {
  18. document.getElementById("fileType").textContent = "没有文件被选中";
  19. return;
  20. }
  21. const fileName = file.name;
  22. const extensionIndex = fileName.lastIndexOf(".");
  23. let fileType;
  24. if (extensionIndex === -1 || extensionIndex === 0) {
  25. fileType = "未知";
  26. } else {
  27. fileType = fileName.substring(extensionIndex + 1);
  28. }
  29. document.getElementById("fileType").textContent =
  30. "文件类型: " + fileType;
  31. });
  32. </script>
  33. </body>
  34. </html>

当然 JavaScript 这方面的代码你也可以这样子实现,看你需求:

  1. document
  2. .getElementById("fileInput")
  3. .addEventListener("change", function (event) {
  4. const file = event.target.files[0];
  5. if (!file) {
  6. document.getElementById("fileType").textContent = "没有文件被选中";
  7. return;
  8. }
  9. let fileType = file.type;
  10. if (!fileType) {
  11. const fileNameParts = file.name.split(".");
  12. const extension = fileNameParts[fileNameParts.length - 1];
  13. if (extension === "md") {
  14. fileType = "text/markdown";
  15. }
  16. }
  17. document.getElementById("fileType").textContent = "文件类型: " + fileType;
  18. });

上传一个文件,文件的类型被输出出来了:

20240123154449

%20

仅通过文件扩展名来判断类型存在安全和准确性风险,因为它可能被恶意用户篡改以上传危险文件,且不能可靠地反映文件的真实内容,同时还无法识别没有扩展名的文件。

%20

通过%20MIME%20类型判断

%20

当我们使用%20HTML%20的%20%20标签时,可以通过文件的%20type%20属性获取%20MIME%20类型:

%20
  1. document
  2. %20%20.getElementById("fileInput")
  3. %20%20.addEventListener("change",%20function%20(event)%20{
  4. %20%20%20%20const%20file%20=%20event.target.files[0];
  5. %20%20%20%20if%20(!file)%20{
  6. %20%20%20%20%20%20document.getElementById("fileType").textContent%20=%20"没有文件被选中";
  7. %20%20%20%20%20%20return;
  8. %20%20%20%20}
  9. %20
  10. %20%20%20%20document.getElementById("fileType").textContent%20=%20"文件类型:%20"%20+%20file.type;
  11. %20%20});
%20

这段代码为文件输入元素添加了一个%20change%20事件监听器。当用户选择文件后,会触发这个事件。事件处理函数会获取用户选中的文件,并显示它的%20MIME%20类型。

%20

%20

%20 %20 %20

MIME(多用途互联网邮件扩展,Multi-purpose%20Internet%20Mail%20Extensions)最初是为了改进电子邮件中的文件传输而设计的,但它的使用范围已经扩展到互联网的其他领域。MIME%20定义了一系列的数据类型和数据格式,用于在网络上交换数据。

%20 %20

但是%20MIME%20类型有时可能不准确或为空。

%20

%20

%20

使用%20FileReader%20读取文件内容

%20

FileReader%20是%20HTML5%20提供的%20API,可以用来读取文件的内容,通过读取文件内容的一部分,可以更准确地判断文件类型:

%20
  1. <!DOCTYPE%20html>
  2. <html%20lang="en">
  3. %20%20<head>
  4. %20%20%20%20<meta%20charset="UTF-8"%20/>
  5. %20%20%20%20<meta%20http-equiv="X-UA-Compatible"%20content="IE=edge"%20/>
  6. %20%20%20%20<meta%20name="viewport"%20content="width=device-width,%20initial-scale=1.0"%20/>
  7. %20%20%20%20<title>读取文件内容</title>
  8. %20%20</head>
  9. %20%20<body>
  10. %20%20%20%20<input%20type="file"%20id="fileInput"%20/>
  11. %20%20%20%20<div%20id="fileContent">文件类型</div>
  12. %20
  13. %20%20%20%20<script>
  14. %20%20%20%20%20%20document
  15. %20%20%20%20%20%20%20%20.getElementById("fileInput")
  16. %20%20%20%20%20%20%20%20.addEventListener("change",%20function%20(event)%20{
  17. %20%20%20%20%20%20%20%20%20%20const%20file%20=%20event.target.files[0];
  18. %20%20%20%20%20%20%20%20%20%20if%20(!file)%20{
  19. %20%20%20%20%20%20%20%20%20%20%20%20document.getElementById("fileContent").textContent%20=
  20. %20%20%20%20%20%20%20%20%20%20%20%20%20%20"没有文件被选中";
  21. %20%20%20%20%20%20%20%20%20%20%20%20return;
  22. %20%20%20%20%20%20%20%20%20%20}
  23. %20
  24. %20%20%20%20%20%20%20%20%20%20const%20reader%20=%20new%20FileReader();
  25. %20
  26. %20%20%20%20%20%20%20%20%20%20reader.onloadend%20=%20function%20(e)%20{
  27. %20%20%20%20%20%20%20%20%20%20%20%20const%20arr%20=%20new%20Uint8Array(e.target.result).subarray(0,%204);
  28. %20%20%20%20%20%20%20%20%20%20%20%20let%20header%20=%20"";
  29. %20%20%20%20%20%20%20%20%20%20%20%20for%20(let%20i%20=%200;%20i%20<%20arr.length;%20i++)%20{
  30. %20%20%20%20%20%20%20%20%20%20%20%20%20%20header%20+=%20arr[i].toString(16).padStart(2,%20"0");
  31. %20%20%20%20%20%20%20%20%20%20%20%20}
  32. %20
  33. %20%20%20%20%20%20%20%20%20%20%20%20let%20fileType%20=%20"未知";
  34. %20%20%20%20%20%20%20%20%20%20%20%20switch%20(header)%20{
  35. %20%20%20%20%20%20%20%20%20%20%20%20%20%20case%20"89504e47":
  36. %20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fileType%20=%20"图片%20(PNG)";
  37. %20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break;
  38. %20%20%20%20%20%20%20%20%20%20%20%20%20%20case%20"47494638":
  39. %20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fileType%20=%20"图片%20(GIF)";
  40. %20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break;
  41. %20%20%20%20%20%20%20%20%20%20%20%20%20%20case%20"ffd8ffe0":
  42. %20%20%20%20%20%20%20%20%20%20%20%20%20%20case%20"ffd8ffe1":
  43. %20%20%20%20%20%20%20%20%20%20%20%20%20%20case%20"ffd8ffe2":
  44. %20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fileType%20=%20"图片%20(JPEG)";
  45. %20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break;
  46. %20%20%20%20%20%20%20%20%20%20%20%20%20%20case%20"25504446":
  47. %20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fileType%20=%20"文档%20(PDF)";
  48. %20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break;
  49. %20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20更多文件类型...
  50. %20%20%20%20%20%20%20%20%20%20%20%20%20%20default:
  51. %20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fileType%20=%20"未知";
  52. %20%20%20%20%20%20%20%20%20%20%20%20}
  53. %20%20%20%20%20%20%20%20%20%20%20%20document.getElementById("fileContent").textContent%20=
  54. %20%20%20%20%20%20%20%20%20%20%20%20%20%20"文件类型:%20"%20+%20fileType;
  55. %20%20%20%20%20%20%20%20%20%20};
  56. %20
  57. %20%20%20%20%20%20%20%20%20%20reader.onerror%20=%20function%20()%20{
  58. %20%20%20%20%20%20%20%20%20%20%20%20document.getElementById("fileContent").textContent%20=%20"文件读取出错";
  59. %20%20%20%20%20%20%20%20%20%20};
  60. %20
  61. %20%20%20%20%20%20%20%20%20%20reader.readAsArrayBuffer(file);
  62. %20%20%20%20%20%20%20%20});
  63. %20%20%20%20</script>
  64. %20%20</body>
  65. </html>

在上面的代码中读取了文件的前四个字节来判断文件类型。它包含了用于标识文件格式的特定模式或“魔术数字”。

20240123162334

%20

文件类型被正确输出。

%20

HTML5%20File%20API

%20

使用%20HTML5%20File%20API%20可以读取用户在浏览器中选择的文件的信息,包括文件类型。这个%20API%20提供了一个标准的方式来获取关于文件的信息,如文件名、大小以及%20MIME%20类型。

%20
  1. document
  2. %20%20.getElementById("fileInput")
  3. %20%20.addEventListener("change",%20function%20(event)%20{
  4. %20%20%20%20const%20file%20=%20event.target.files[0];
  5. %20%20%20%20if%20(!file)%20{
  6. %20%20%20%20%20%20document.getElementById("fileInfo").textContent%20=%20"没有选择文件";
  7. %20%20%20%20%20%20return;
  8. %20%20%20%20}
  9. %20
  10. %20%20%20%20const%20fileInfo%20=%20`文件名:%20${file.name}<br>文件大小:%20${file.size}%20字节<br>文件类型:%20${file.type}`;
  11. %20%20%20%20document.getElementById("fileInfo").innerHTML%20=%20fileInfo;
  12. %20%20});
%20

最终输出效果如下图所示:

%20

%20

%20

装%20X%20必备,WebAssembly

%20

使用%20WebAssembly%20(WASM)%20对文件进行识别通常涉及到较为复杂的操作,因为你需要将文件处理逻辑编译为%20WASM%20模块,然后在%20JavaScript%20中调用这个模块来处理文件。

%20

第一步,我们以%20C%20为例子,接下来我们编写一个%20C%20代码,如下:

%20
  1. #include%20<string.h>
  2. #include%20<emscripten.h>
  3. %20
  4. //%20检查%20PNG
  5. int%20is_png(const%20unsigned%20char%20*buffer)%20{
  6. %20%20%20%20const%20unsigned%20char%20png_signature[8]%20=%20{0x89,%20'P',%20'N',%20'G',%200x0D,%200x0A,%200x1A,%200x0A};
  7. %20%20%20%20return%20memcmp(buffer,%20png_signature,%208)%20==%200;
  8. }
  9. %20
  10. //%20检查%20JPEG
  11. int%20is_jpeg(const%20unsigned%20char%20*buffer)%20{
  12. %20%20%20%20return%20buffer[0]%20==%200xFF%20&&%20buffer[1]%20==%200xD8%20&&%20buffer[2]%20==%200xFF;
  13. }
  14. %20
  15. //%20检查%20GIF
  16. int%20is_gif(const%20unsigned%20char%20*buffer)%20{
  17. %20%20%20%20return%20strncmp((const%20char%20*)buffer,%20"GIF",%203)%20==%200;
  18. }
  19. %20
  20. //%20检查%20PDF
  21. int%20is_pdf(const%20unsigned%20char%20*buffer)%20{
  22. %20%20%20%20return%20strncmp((const%20char%20*)buffer,%20"%PDF-",%205)%20==%200;
  23. }
  24. %20
  25. //%20主函数,用于检测文件类型
  26. EMSCRIPTEN_KEEPALIVE
  27. int%20check_file_type(const%20unsigned%20char%20*buffer,%20int%20length)%20{
  28. %20%20%20%20if%20(is_pdf(buffer))%20{
  29. %20%20%20%20%20%20%20%20return%201;
  30. %20%20%20%20}%20else%20if%20(is_png(buffer))%20{
  31. %20%20%20%20%20%20%20%20return%202;
  32. %20%20%20%20}%20else%20if%20(is_jpeg(buffer))%20{
  33. %20%20%20%20%20%20%20%20return%203;
  34. %20%20%20%20}%20else%20if%20(is_gif(buffer))%20{
  35. %20%20%20%20%20%20%20%20return%204;
  36. %20%20%20%20}
  37. %20%20%20%20return%200;%20//%20未知类型
  38. }

在上面的这些代码中,通过比较输入流的前 8 个字节与这个序列来判断是否为某个文件。

代码编写完成之后,我们要在终端执行一行命令如下所示:

emcc index.c -s WASM=1 -o index.js -s EXPORTED_FUNCTIONS='["_check_file_type", "_malloc", "_free"]' -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]'

最终会生成这两个文件:

20240123165353

%20

这个时候我们就可以在我们前端中使用了,编写如下代码:

%20
  1. <!DOCTYPE%20html>
  2. <html%20lang="en">
  3. %20%20<head>
  4. %20%20%20%20<meta%20charset="UTF-8"%20/>
  5. %20%20%20%20<title>文件类型识别</title>
  6. %20%20%20%20<script%20src="./index.js"></script>
  7. %20%20</head>
  8. %20%20<body>
  9. %20%20%20%20<input%20type="file"%20id="fileInput"%20/>
  10. %20
  11. %20%20%20%20<script>
  12. %20%20%20%20%20%20const%20fileInput%20=%20document.getElementById("fileInput");
  13. %20
  14. %20%20%20%20%20%20Module.onRuntimeInitialized%20=%20()%20=>%20{
  15. %20%20%20%20%20%20%20%20fileInput.addEventListener("change",%20(event)%20=>%20{
  16. %20%20%20%20%20%20%20%20%20%20const%20file%20=%20event.target.files[0];
  17. %20%20%20%20%20%20%20%20%20%20if%20(!file)%20{
  18. %20%20%20%20%20%20%20%20%20%20%20%20return;
  19. %20%20%20%20%20%20%20%20%20%20}
  20. %20
  21. %20%20%20%20%20%20%20%20%20%20const%20reader%20=%20new%20FileReader();
  22. %20%20%20%20%20%20%20%20%20%20reader.onload%20=%20(e)%20=>%20{
  23. %20%20%20%20%20%20%20%20%20%20%20%20const%20buffer%20=%20new%20Uint8Array(e.target.result);
  24. %20
  25. %20%20%20%20%20%20%20%20%20%20%20%20//%20分配内存并将数据复制到%20WebAssembly%20的内存空间
  26. %20%20%20%20%20%20%20%20%20%20%20%20const%20dataPtr%20=%20Module._malloc(buffer.length);
  27. %20%20%20%20%20%20%20%20%20%20%20%20Module.HEAPU8.set(buffer,%20dataPtr);
  28. %20
  29. %20%20%20%20%20%20%20%20%20%20%20%20//%20调用%20WebAssembly%20函数
  30. %20%20%20%20%20%20%20%20%20%20%20%20const%20fileType%20=%20Module.ccall(
  31. %20%20%20%20%20%20%20%20%20%20%20%20%20%20"check_file_type",%20//%20C%20函数名
  32. %20%20%20%20%20%20%20%20%20%20%20%20%20%20"number",%20//%20返回值类型
  33. %20%20%20%20%20%20%20%20%20%20%20%20%20%20["number",%20"number"],%20//%20参数类型
  34. %20%20%20%20%20%20%20%20%20%20%20%20%20%20[dataPtr,%20buffer.length]%20//%20参数
  35. %20%20%20%20%20%20%20%20%20%20%20%20);
  36. %20
  37. %20%20%20%20%20%20%20%20%20%20%20%20//%20输出文件类型到控制台
  38. %20%20%20%20%20%20%20%20%20%20%20%20console.log("文件类型%20ID:",%20fileType);
  39. %20
  40. %20%20%20%20%20%20%20%20%20%20%20%20//%20释放内存
  41. %20%20%20%20%20%20%20%20%20%20%20%20Module._free(dataPtr);
  42. %20%20%20%20%20%20%20%20%20%20};
  43. %20%20%20%20%20%20%20%20%20%20reader.readAsArrayBuffer(file);
  44. %20%20%20%20%20%20%20%20});
  45. %20%20%20%20%20%20};
  46. %20%20%20%20</script>
  47. %20%20</body>
  48. </html>

这样,我们就可以根据不同的 id 来返回不同的文件类型了:

20240123165551

通过以上步骤,我们实现了一个使用 WebAssembly 来识别文件类型并在浏览器控制台上输出结果的功能。

总结

除了这些之外,我们还可以使用第三方库 file-type 来实现文件类型检测,这里我们就不再进行 demo 演示了,感兴趣的可以去官方文档中进行查阅。

另外最近对脚手架的仓库代码进行了重构,之后会越来越规范,规模会越来越大,感兴趣的小伙伴可以加入来一起开发,春招没有项目的朋友也可以以此作为春招的项目:

20240123170503

标签:
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

在线投稿:投稿 站长QQ:1888636

后台-插件-广告管理-内容页尾部广告(手机)
关注我们

扫一扫关注我们,了解最新精彩内容

搜索