分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 前端开发

前端上传数据-图片和视频格式校验

发布时间:2023-09-06 02:32责任编辑:赖小花关键词:前端

上一篇用 promise 嵌套实现了按 excel 行顺序上传数据,这篇要解决的问题是图片和视频格式校验,图片主要有 jpg png gif 视频 mp4

由于用户选择的资源可能并不是真正的多媒体文件,使用 js 的 file.type 方法获取的文件类型可能不准确,比如将 .xlsx 改为 .jpg, file.type 得到的类型是image/jpeg

客户端拉取资源时,图片和视频的分辨率也一并获取,而上传由前端控制,所以上传时对资源要进行比较准确的判断。

我的判断策略:

  1. 判断文件后缀,若不是 jpg/png/gif/mp4 中的一种,则报错
  2. 对 jpg/png/gif 的文件,读取二进制头信息,满足任一格式则返回相应格式,否则为非法格式
  3. 获取图片和视频的分辨率,获取成功则是真的成功,否则还是报错

后缀名校验

// 获取图片的 width heightgetImgSize(file) { ?const imgFileType = [‘image/jpeg‘, ‘image/png‘, ‘image/gif‘] ?const filetype = file.type ?const suffix = filetype.substring(filetype.lastIndexOf(‘/‘)+1) ?// 返回一个 promise ??return new Promise((resolve, reject) => { ???let reader = new FileReader() ???reader.onload = function(e){ ?????const data = e.target.result ?????const img = new Image() ?????img.onload = function(){ ???????resolve({width: img.width, height: img.height, ext: suffix }) ?????} ?????img.onerror = function(){ ???????reject(`[${file.name}]解析失败,可能图片格式不正确`) ?????} ?????img.src = data ???} ???reader.readAsDataURL(file) ?})},// 获取视频的 width heightgetVideoSize(file) { ?const videoType = [‘video/mp4‘,] ?const filetype = file.type ?const suffix = filetype.substring(filetype.lastIndexOf(‘/‘)+1) ?// 返回一个 promise ????return new Promise((resolve, reject) => { ?????const url = window.URL.createObjectURL(file) ?????const video = document.createElement(‘video‘) ?????video.onloadedmetadata = evt => { ???????// Revoke when you don‘t need the url any more to release any reference ???????window.URL.revokeObjectURL(url) ???????resolve({width: video.videoWidth, height: video.videoHeight, ext: suffix }) ?????} ?????video.onerror = evt => { ???????reject(`[${file.name}]解析失败,可能视频文件格式不正确`) ?????} ?????video.src = url ?????video.load() ?})},

二进制头信息

依据 ISO 标准, jpg 文件的前2个字节为 0xFF, 0xD8
png 前8个字节 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A
gif 有"GIF87a" 和 "GIF89a",依据前6个字节判断

  • GIF87a 0x47, 0x49, 0x46, 0x38, 0x37, 0x61
  • GIF89a 0x47, 0x49, 0x46, 0x38, 0x39, 0x61

js 提供了 getUint8 以便读取字节码,只需要传入偏移量即可

校验代码

const JPEG_SOI = [0xFF, 0xD8]const JPEG_EOI = [0xFF, 0xD9]// png的文件头就是png图片的前8个字节,其值为[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]const PNG_HEADER = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]// GIF files start with a fixed-length header ("GIF87a" or "GIF89a") giving the versionconst GIF89A_HEADER = [0x47, 0x49, 0x46, 0x38, 0x39, 0x61]const GIF87A_HEADER = [0x47, 0x49, 0x46, 0x38, 0x37, 0x61]// 是否小端序const isLittleEndian = (function() { ?var buffer = new ArrayBuffer(2); ?new DataView(buffer).setInt16(0, 256, true) ?return new Int16Array(buffer)[0] === 256})()// byte数组元素是否相等function isArrayEqual(a, b){ ?for(let i=0; i<a.length; i++){ ???if(a[i] !== b[i]){ ?????return false ???} ?} ?return true}export function getImageTypeByHeadContent(file){ ?// file 实际上是一个 Blob 对象 ?// 读取 Blob 对象的前8个字节 ?const fileHeader = file.slice(0, 8) ?return new Promise((resolve, reject) => { ???let reader = new FileReader() ???reader.onload = function(e){ ?????const data = e.target.result ?????const header = new DataView(data) ?????let bytesArr = [] ?????for(let i=0; i<header.byteLength; i++){ ???????bytesArr.push(header.getUint8(i, isLittleEndian)) ?????} ?????if(isArrayEqual(JPEG_SOI, bytesArr.slice(0,2))){ ???????resolve(‘jpg‘) ?????}else if(isArrayEqual(PNG_HEADER, bytesArr)){ ???????resolve(‘png‘) ?????}else if(isArrayEqual(GIF89A_HEADER, bytesArr.slice(0,6)) || ???????????????isArrayEqual(GIF87A_HEADER, bytesArr.slice(0,6)) ?????????????){ ???????resolve(‘gif‘) ?????}else{ ???????reject() ?????} ???} ???reader.readAsArrayBuffer(fileHeader) ?})}

那么多媒体文件的校验函数

// 如果传入的类型与实际不符,则不上传,防止图片类型上传视频,或视频类型上传图片getMediaSize(file, validtype){ ?const _this = this ?return new Promise((resolve, reject) => { ???if(! _this.hasGotSizeObj.hasOwnProperty(file.name)){ ?????if(file.type.startsWith(‘image‘) && validtype === ‘image‘){ ???????_this.getImgSize(file) ???????.then(data => { ?????????// 从文件头信息无法识别图片类型时,以后缀名为图片类型 ?????????getImageTypeByHeadContent(file) ?????????.then(type => { ???????????data.ext = type ???????????_this.hasGotSizeObj[file.name] = {extra: data} ???????????resolve(data) ?????????}) ?????????.catch(()=>{ ???????????_this.hasGotSizeObj[file.name] = {extra: data} ???????????resolve(data) ?????????}) ???????}) ???????.catch(err => { ?????????reject(err) ???????}) ?????}else if(file.type.startsWith(‘video‘) && validtype === ‘video‘){ ???????_this.getVideoSize(file) ???????.then(data => { ?????????_this.hasGotSizeObj[file.name] = {extra: data} ?????????resolve(data) ???????}).catch(err => { ?????????reject(err) ???????}) ?????}else{ ???????reject(`不允许的文件类型: ${file.type}`) ?????} ???}else{ ?????resolve(_this.hasGotSizeObj[file.name].extra) ???} ?})},

前端上传数据-图片和视频格式校验

原文地址:https://www.cnblogs.com/wbjxxzx/p/10342156.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved