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

上一篇用 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 height
getImgSize(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 height
getVideoSize(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 version
const 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

时间: 2024-10-10 04:16:37

前端上传数据-图片和视频格式校验的相关文章

el-upload怎么拿到上传的图片的base64格式

这里只是本地上传,拿图片的base64,并不向后台直接上传,拿到base64后手动上传 上传前效果: 上传后效果: .vue <el-form-item label="礼品封面"> <el-upload class="avatar-uploader" action="" :show-file-list="false" :auto-upload="false" :on-change=&quo

js图片上传验证图片格式和大小尺寸

<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <script type="text/javascript"> function getImageSize(obj){ var _file=document.getElementById("file

IOS 网络开发NSURLSession(四)UploadTask(上传数据+图片)

原创blog,转载请注明出处 blog.csdn.net/hello_hwc 前言: UploadTask继承自DataTask.不难理解,因为UploadTask只不过在Http请求的时候,把数据放到Http Body中.所以,用UploadTask来做的事情,通常直接用DataTask也可以实现.不过,能使用封装好的API会省去很多事情,何乐而不为呢? Demo下载链接 http://download.csdn.net/detail/hello_hwc/8557791 Demo里包括了三种T

上传base64图片到七牛云前端遇到的坑

介意前端普通引入七牛云SDk上传图片到七牛云需要多个js,所以才有了base64的上传方式,简化操作,(懒.) 七牛云官方文档如下 https://developer.qiniu.com/kodo/kb/1326/how-to-upload-photos-to-seven-niuyun-base64-code 我们前端理所当然就复制黏贴了..然后问题就来了 看地址 var url = "http://upload.qiniu.com/putb64/20264"; //非华东空间需要根据

CKeditor七牛云JS SDK前端上传插件修改

七牛云官方有放出JS SDK,没有我想使用的CKeditor前端上传插件,所以结合七牛官方的Javascript SDK对CKeditor做了一些修改使它能够直接上传到七牛云,又同时保留了上传到本地服务的接口. 优点和缺点1.在前端上传到七牛云,不消耗服务器带宽和流量.空间.2.保留了CKeditor上传到自己服务器的能力.3.支持拖拽和剪切板黏贴图片上传(因为是保存为png格式,建议只黏贴色彩单调的图片,要不然图片会很大,浪费流量).4.拖拽和剪切板黏贴图片.不支持4M以上的文件,因为没有分块

[Pulgin] 前端上传组件Plupload使用指南

我之前写过一篇文章<文件上传利器SWFUpload使用指南>,里面介绍了上传组件SWFUpload的使用方法,但现在随着html5技术的逐渐推广和普及,再去使用以flash为上传手段的SWFUpload显然就有点过时了,毕竟html5原生的就给我们提供了文件上传的API.Plupload是一款由著名的web编辑器TinyMCE团队开发的上传组件,简单易用且功能强大,我们完全可以使用Plupload来代替以前的SWFUpload. Plupload有以下功能和特点: 1.拥有多种上传方式:HTM

前端上传组件Plupload使用指南

我之前写过一篇文章<文件上传利器SWFUpload使用指南>,里面介绍了上传组件SWFUpload的使用方法,但现在随着html5技术的逐渐推广和普及,再去使用以flash为上传手段的SWFUpload显然就有点过时了,毕竟html5原生的就给我们提供了文件上传的API.Plupload是一款由著名的web编辑器TinyMCE团队开发的上传组件,简单易用且功能强大,我们完全可以使用Plupload来代替以前的SWFUpload. Plupload有以下功能和特点: 1.拥有多种上传方式:HTM

【转】前端上传组件Plupload使用指南

http://www.cnblogs.com/2050/p/3913184.html Plupload有以下功能和特点: 1.拥有多种上传方式:HTML5.flash.silverlight以及传统的<input type=”file” />.Plupload会自动侦测当前的环境,选择最合适的上传方式,并且会优先使用HTML5的方式.所以你完全不用去操心当前的浏览器支持哪些上传方式,Plupload会自动为你选择最合适的方式. 2.支持以拖拽的方式来选取要上传的文件 3.支持在前端压缩图片,即

【转】前端上传组件Plupload

[转自博客园-无双] html5原生的给我们提供了文件上传的API,Plupload是一款由著名的web编辑器TinyMCE团队开发的上传组件,简单易用且功能强大,我们完全可以使用Plupload来代替以前的SWFUpload. Plupload有以下功能和特点: 1.拥有多种上传方式:HTML5.flash.silverlight以及传统的<input type=”file” />.Plupload会自动侦测当前的环境,选择最合适的上传方式,并且会优先使用HTML5的方式.所以你完全不用去操