图片编辑器中实现文件上传的三种方式和二进制流及文件头校验文件类型

背景

最近在 vue-design-editor 开源项目中实现 psd 等多种文件格式上传解析成模板过程中, 发现搞定设计文件上传没有使用 input 实现文件上传, 所以我研究了一下相关技术, 总结了以下三种文件上传方法

wecom-temp-1ca1ac3f69f021a80526d22768d8b64b.png

  1. input 文件选择
  2. window.showOpenFilePicker 和 window.showDirectoryPicker 文件选择和文件夹选择
  3. 拖拽上传

为什么我不使用 file 里面的 type 字段直接当文件类型判断, 下图就是 psd 文件类型

企业微信截图_60833287-f70b-4969-9840-fa034aa44d52.png

大家发现是不是其实可以通过 type 字段进行判断, 但是告诉大家这是我把 png 图片直接修改了后缀名为 psd, 所以直接通过 type 进行文件类型校验是不准确的, 会有隐患, 如果我们通过二进制流及文件头校验就非常准确了

文件上传

input 文件上传

如果大家实现文件上传第一想到的应该是 input 文件上传, 这里就不多赘述了

<input type="file" accept=".png, .jpeg, .jpg, .psd" />

缺点就是样式很丑, 定制样式麻烦, 所以实现点击文件上传功能没有直接上手实现

window.showOpenFilePicker 和 window.showDirectoryPicker

支持唤起文件和文件夹, 属于浏览器全局方法,直接调用即可

showSaveFilePicker 保持文件功能

如果有以上三个 api 就可以在浏览器中实现代码编辑器功能

代码实现如下

const arrFileHandle = await window.showOpenFilePicker({types: [{accept: {"image/*": [".psd", ".png", ".gif", ".jpeg", ".jpg", ".webp"],},},],// 可以选择多个图片multiple: false,
});
// 遍历选择的文件
for (const fileHandle of arrFileHandle) {// 获取文件内容const fileData = await fileHandle.getFile();fileList.value.push(fileData);
}

不过使用该 api 有限制

  1. 需要 https 环境,如果是本地 localhost 不受此限制。
  2. 不能在 iframe 内使用,因为被认为不安全

所以我在使用该 api 过程中也有兜底逻辑

如果不支持也能点击唤起, 模拟 input 点击唤起

const input = document.createElement("input");
input.type = "file";
input.multiple = "multiple";
input.accept = ".png, .jpeg, .jpg, .psd";
input.click();
input.addEventListener("change", (file) => {});

拖拽上传

搞定设计支持拖拽上传文件, 所以也支持拖拽上传

企业微信截图_d6c53985-acc8-4d30-81c0-4275eb7bb531.png

import { tryOnMounted, useEventListener } from "@vueuse/core";
const dragArea = ref();
tryOnMounted(() => {useEventListener(dragArea.value, "dragover", onDragOver);useEventListener(dragArea.value, "dragleave", onDragLeave);useEventListener(dragArea.value, "drop", onDrop);
});
function onDragOver(e: Event) {e.preventDefault();// 拖拽区域样式提示// 也可以通过变量的形式控制domdragArea.value.classList.add("dragover");isDrag.value = true;
}function onDragLeave(e: Event) {e.preventDefault();dragArea.value.classList.remove("dragover");isDrag.value = false;
}async function onDrop(e: any) {e.preventDefault();dragArea.value.classList.remove("dragover");isDrag.value = false;const files = e.dataTransfer.files;
}

比 input 上传的优点是支持上传文件夹

二进制流及文件头校验文件类型

其他校验方法

在背景中也提过通过文件 type 字段校验类型的缺陷

还有一个方法也是有和 type 校验类型一样的缺陷

企业微信截图_aa8f8339-01cc-422f-abfe-7fe90bd1bf96.png

就是使用文件后缀来判断

const file = e.files[0];
//获取最后一个.的位置
const index = file.name.lastIndexOf(".");
//获取后缀
const ext = file.name.substr(index + 1);
console.log(ext);

文件后缀名

概念我们了解一下

文件扩展名是文件让电脑识别它的识别器,文件本身的格式是内在的,扩展名是外在的,一般情况下,他们是相互对应的,但如果扩展名被操作或修改,就不能与文件本身的格式对应,就会遇到打不开,打开乱码或无法显示,无法识别等情况。

解决

同样可以将其他类型的文件上传至服务器,或者文件压根就没有后缀,那又要怎么判断呢?因此前端需要使用一个更加合理的方式。

所以使用二进制及文件头的形式来校验文件格式

虽然文件后缀可以手动改,因此可以直接通过读取文件的二进制来判断。
通常来说固定类型的文件头都是相同的,比如说 jpeg 的文件头是 FF D8 FF E0。

枚举类型相关的文件头

const signatureList = [{mime: "video/mp4",ext: "mp4",offset: 4,signature: [0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6f, 0x6d],},{mime: "video/mp4",ext: "mp4",offset: 4,signature: [0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34],},{mime: "image/jpeg",ext: "jpeg",signature: [0xff, 0xd8, 0xff],},{mime: "image/png",ext: "png",signature: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],},{mime: "image/gif",ext: "gif",signature: [0x47, 0x49, 0x46, 0x38, 0x37, 0x61],},{mime: "image/gif",ext: "gif",signature: [0x47, 0x49, 0x46, 0x38, 0x39, 0x61],},{mime: "image/vnd.adobe.photoshop",ext: "psd",signature: [0x38, 0x42, 0x50, 0x53],},
];

FileReader 读取文件的二进制,之后判断二进制的前几位是否跟符合相应类型文件的文件头。

/*** @description 校验给出的字节数据是否符合某种MIME Type的signature* @param {Array} bufferss 字节数据* @param {Object} typeItem 校验项 { signature, offset  }*/
const check = (bufferss: Buffer, { signature, offset = 0 }: any) => {for (let i = 0, len = signature.length; i < len; i++) {// 传入字节数据与文件signature不匹配// 需考虑有offset的情况以及signature中有值为undefined的情况if (bufferss[i + offset] !== signature[i] && signature[i] !== undefined) return false;}return true;
};/*** @description 获取文件二进制数据* @param {File} file 文件对象实例* @param {Object} options 配置项,指定读取的起止范围*/
const getArrayBuffer = (file: File, { start, end }: any) => {return new Promise((reslove, reject) => {try {const reader = new FileReader();reader.onload = (e: any) => {const buffers = new Uint8Array(e.target.result);reslove(buffers);};reader.onerror = (err) => reject(err);reader.onabort = (err) => reject(err);reader.readAsArrayBuffer(file.slice(start, end));} catch (err) {reject(err);}});
};/*** @description 获取文件的真实类型* @param {File} file 文件对象实例* @param {Object} options 配置项,指定读取的起止范围*/
const getFileType = (file: File, options = { start: 0, end: 32 }) =>getArrayBuffer(file, options).then((buffers: any) => {// 找出签名列表中定义好的类型,并返回for (let i = 0, len = signatureList.length; i < len; i++) {if (check(buffers, signatureList[i])) {const { mime, ext } = signatureList[i];return { mime, ext };}}// 未找到则返回file对象中的信息return { mime: file.type, ext: "" };}).catch((err) => err);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/759347.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android Selinux详解[七]--如何给可执行程序bin加标签

经过前面几篇文章的介绍&#xff0c;你应该对Selinux有一定的了解了&#xff0c;现在我们就来实战一下。 你可能会在工作的过程遇到要给可执行程序bin加标签的需求&#xff0c;以下来讲解一下怎么给bin加标签 1. 一个bin通常是通过adb shell bin名字拉起来的&#xff0c;拉起…

WPF 中 样式触发器机制 Style.Triggers

在 WPF (Windows Presentation Foundation) 中&#xff0c;Style.Triggers 是一种样式触发器机制&#xff0c;它允许你在特定条件下改变控件的外观或行为。Style.Triggers 元素是 Style 元素的子元素&#xff0c;用于定义触发器集合。 每个触发器都关联到一个条件&#xff0c;…

何恺明重提十年之争——模型表现好是源于能力提升还是捕获数据集偏见

2011年,知名学者Antonio Torralba和Alyosha Efros提出了“数据集偏差之战”&#xff0c;他们发现机器学习模型很容易“过拟合”到特定的数据集上&#xff0c;导致在其他数据集上表现不佳。过去十年&#xff0c;随着深度学习革命的到来&#xff0c;建立多样化、大规模、全面且尽…

应急响应-Web2

应急响应-Web2 1.攻击者的IP地址&#xff08;两个&#xff09;&#xff1f; 192.168.126.135 192.168.126.129 通过phpstudy查看日志&#xff0c;发现192.168.126.135这个IP一直在404访问 &#xff0c; 并且在日志的最后几条一直在访问system.php &#xff0c;从这可以推断 …

机器学习_聚类(Clustering)

文章目录 简介K-均值算法(K_Means) 简介 你经常跟哪些人联系&#xff0c;而这些人又经常给哪些人发邮件&#xff0c;由此找到关系密切的人群。因此&#xff0c;这可能需要另一个聚类算法&#xff0c;你希望用它发现社交网络中关系密切的朋友。 K-均值算法(K_Means) K-均值是…

查看angular版本的问题The Angular CLI requires a minimum Node.js version of v18.13.

angular版本与node.js版本不匹配的问题 下载安装angular 查看版本&#xff0c;发现不匹配 安装指定版本即可 查看版本并运行

stm32f103c8t6学习笔记(学习B站up江科大自化协)-ADC

ADC简介 ADC&#xff0c;英文全称是Analog to Digital Convert&#xff0c;意为模拟数字转换器&#xff0c;简称模数转换器&#xff0c;或者叫AD转换器&#xff0c;STM32主要是数字电路&#xff0c;数字电路只有高低电平&#xff0c;没有几V电压的概念&#xff0c;如果想读取电…

Android 优化 - 数据结构

一、概念 数据结构&#xff1a;数据存储在内存中的顺序和位置关系&#xff0c;选择合适的数据结构能提高内存的利用率。 线性结构链表结构树形结构 二、线性结构 结构优点缺点数组数据呈线性排列&#xff0c;初始化时就要指定长度且无法更改&#xff0c;会开辟一块连续的内…

使用ansible批量修改操作系统管理员账号密码

一、ansible server端配置 1、对于Linux主机配置免密登录ssh-copy-id -i ~/.ssh/id_rsa.pub rootremote_ip 2、在/etc/ansible/hosts文件中添加相应主机IP 3、对于Windows主机需要在/etc/ansible/hosts文件中进行以下配置 192.168.83.132 ansible_ssh_useradministrator an…

神经网络(深度学习,计算机视觉,得分函数,损失函数,前向传播,反向传播,激活函数)

目录 一、神经网络简介 二、深度学习要解决的问题 三、深度学习的应用 四、计算机视觉 五、计算机视觉面临的挑战 六、得分函数 七、损失函数 八、前向传播 九、反向传播 十、神经元的个数对结果的影响 十一、正则化与激活函数 一、神经网络简介 神经网络是一种有监督…

在vue项目中封装并使用WebSocket(2)

创建一个websocket组件 <script> export default {name: "index",props: {wsUrl: {type: String,require: true,},},data() {return {socket: "",};},watch: {wsUrl: {immediate: true,handler() {this.init();},},},methods: {init() {console.log…

RocketMq 顺序消费、分区消息、延迟发送消息、Topic、tag分类 实战 (延迟发送消息) (四)

延迟发送消息生产者配置 如下所示&#xff1a;Bean注解向Spring容器注入一个名字叫delayOrderProducerBean、类型为OrderProducerBean 的对象&#xff08;下文需要用到&#xff09; Configuration public class DelayProducerClient {Autowiredprivate RocketMqDelayPropertie…

打开打包好的.APK文件,使用Android Studio

1. 没有android studio的 下载安装 Android 开发者 | Android Developers 2. 打开file下面的 “Profile or debug apk” 选择想要打开的.apk文件 3. 打开AndroidManifest.xml就可以看到想要看到版本号等基本信息

晶圆制造过程中常用载具的类型

晶圆载具用于硅片生产、晶圆制造以及工厂之间晶圆的储存、传送、运输以及防护。晶圆载具种类很多,如FOUP用于晶圆制造工厂中晶圆的传送;FOSB用于硅片生产与晶圆制造工厂之间的运输;CASSETTE载具可用于工序间运送以及配合工艺使用。 OPEN CASSETTE OPEN CASSETTE主要在晶圆…

VB.NET 中的属性(Properties)和字段(Fields)有什么区别?请举例说明。

VB.NET 中的属性&#xff08;Properties&#xff09;和字段&#xff08;Fields&#xff09;有什么区别&#xff1f;请举例说明。 在VB.NET中&#xff0c;属性&#xff08;Properties&#xff09;和字段&#xff08;Fields&#xff09;是用于封装数据的两种主要方式。它们之间的…

vue3 导航守卫

Vue 3 的导航守卫与 Vue 2 相比&#xff0c;在 API 和使用方式上并没有太大的变化&#xff0c;但 Vue 3 的响应式系统和 Composition API 为导航守卫提供了更多的灵活性。 以下是 Vue 3 中常见的几种导航守卫&#xff1a; 1. 全局前置守卫 全局前置守卫在路由跳转前执行。你可…

智慧公园:AI智能分析网关V4城市公园视频智能监管方案

一、背景分析 随着天气渐渐转暖&#xff0c;城市公园的花卉也逐渐盛开&#xff0c;春暖花开时节&#xff0c;前往公园赏花游玩的城市居民也渐渐多起来&#xff0c;因此安全问题也成为相关监管部门的重要管理任务之一。随着科技的不断进步&#xff0c;智能监控技术已经成为现代…

小程序SSL证书

小程序已成为我们日常生活中不可或缺的一部分。为了让用户的每一次点击、每一次交互都安全无忧&#xff0c;我们引入了专业级的SSL证书服务&#xff0c;为您的小程序构筑坚不可摧的信息安全壁垒。 SSL证书&#xff0c;如同一把无形的加密锁&#xff0c;它将小程序中的所有数据流…

.net 8 使用学习小记

单例模式 public class Singleton {private static Singleton instance null;private static readonly object syncRoot new object();private Singleton() { }public static Singleton Instance{get{if (instance null){lock (syncRoot){if (instance null){instance ne…

使用vue的element组件上传excel文件

在Vue.js项目中&#xff0c;如果你想要上传Excel文件&#xff0c;并且正在使用Element UI组件库&#xff0c;你可以利用 ​<el-upload>​组件来实现上传功能。以下是一个简单的例子&#xff0c;演示了如何使用Element UI上传Excel文件。 首先&#xff0c;在你的Vue组件模…