上传前检测媒体文件[MP4]

673 阅读4分钟

前言

现在前端上传文件大多都是通过 “云存储” 提供的SDK配合有写入资源权限的临时Token的账号信息来实现前端直接将文件直传 OSS 云存储空间的。然后把上传后生成的文件在OSS桶的绝对路径地址提交给后端同事即可。过程相对早期通过 FormData 表单提交通过后端上传文件的形式简化方便了许多。

上传到云存储的媒体文件和资源类的文件大多都可以采用直链的形式进行访问,文件类型和对应的常见文件格式有以下类型:

  • 音频文件(.mp3、.wav、.aac、.ogg等)
  • 视频文件(.mp4、.mov、.rmvb、.avi等)
  • 图片文件(.jpeg、.bmp、.gif、.png、.tiff、.raw、.svg等)
  • 文档文件(.ppt、.pptx、.doc、.docx、.xls、.xlsx、.pdf、.txt等)
  • 其他类型文件(压缩包、软件包、安装包等对应格式的文件)

不同类型的文件大多会在上传文件之前对不同类型的文件在存储空间内建对应的文件夹进行相应的文件归类,以便于日后对文档的查找和对文件的归类整理。

场景

上传文件之前一般都会对文件做文件类型的校验,通过检验文件的类型来决定本次上传的文件对应的是存储空间的哪一个文件类型的目录(:部分存储空间在外侧直接采用时间格式的形式当作上传文件的目录,这种方式方便按时间的维度来管理文件,但是不便于文件的归类)早期做开发上传文件的时候,大多没有文档归类的概念,都是一股脑的按照时间目录的形式上传文件,那时候的文件类型比较单一,大多是图片和视频为主。现在开发工作中涉及到的文件种类比较多,按文件分类的形式管理,相对来说过比较清晰明了。

此次主要对上传文件的媒体文件进行上传前的校验,主要是视频文件(MP4)和音频文件(MP3、WAV);大多时候采取对上传文件的文件后缀名File对象file.type 来进行校验;但是这种方式有一种弊端,就是拿其它类型的文件强行修改后缀名的形式来上传,这时候虽然文件可以上传,但是存在一些弊端,一是上传之后无法在线预览和在线播放;二是后端在对上传的媒体文件进行转码处理和加水印,此时就会造成转码失败的情况。所以为了严格控制上传时对媒体类型的文件的校验,就把文件校验这个步骤放在了前端。

首先来打印上传前的文件的信息

image.png

图一:正常MP4文件打印出的文件信息

image.png

图二:打印的正常视频文件的校验的媒体文件信息

属性名字属性简介
duration视频文件的时长信息
mime视频文件codec信息
audioTracks视频文件中的音频相关信息
videoTracks视频文件中视频相关的信息
isProgressive文件是否可以渐进式播放
isFragmented文件是否已碎片化
fragment_duration以时间刻度为单位给出文件碎片部分的持续时间
hasIOD文件是否包含 MPEG-4 初始对象描述符
track_width轨道标题中指示的轨道数量和宽度
track_height轨道标题中指示的轨道编号、高度
sample_rate数字,媒体标题中指示的采样率
channel_count数字,媒体标题中指示的通道数
sample_size媒体标头中指示的未压缩音频样本的数量、大小(以位为单位)

上述表格中的信息是基于 “mp4box” 插件打印的信息

将xlsx文件修改后缀名转换成mp4文件形式打印的信息

image.png

图三:此处图片为将非媒体文件通过修改后缀名的形式生成的mp4文件

注: 对比图二图四,打印信息对比分析,修改后缀名的文件无法打印出“tracks”、“audioTracks”、“videoTracks”和“mime”等相关信息

代码

import MP4Box from "mp4box";

/**
 * @description 检测媒体文件
 * @param {Object} file 待检测文件对象
 * @return {Boolean} true:不合规、false:合规
 **/
export const checkMediaFile = (file) => {
	return new Promise((resolve) => {
		if (!["audio/mpeg", "audio/wav", "video/mp4"].includes(file.type)) return resolve(true);

		if (file.type === "video/mp4") {
			// 检测视频
			const mp4boxFile = MP4Box.createFile();
			mp4boxFile.onReady = (info) => {
				console.log("info", info);
				const MIME = info?.mime ?? null;
				let flag = true;
				if (MIME) flag = MIME.includes("acv1") || MIME.includes("mp4a") ? false : true;
				resolve(flag);
			};
			// 读取媒体文件流
			const mediaFile = new FileReader();
			mediaFile.readAsArrayBuffer(file);
			// 媒体文件加载完成
			mediaFile.addEventListener("loadend", (evt) => {
				console.log("loadend", evt);
				let stream = evt.target.result;
				stream.fileStart = 0;
				mp4boxFile.appendBuffer(stream);
				mp4boxFile.flush();
				resolve(true);
			});
		}
	});
};

上述代码需要先引入 mp4box 插件

npm install mp4box
yarn add mp4box

总结

此处主要校验的是上传的媒体文件中的mp4格式的文件,通过打印的信息来判断是否是mp4视频源文件,可以通过tracks和mime来判断视频源的编码格式 codec 的值是否是H264编码,现在视频的主流的编码格式还是H264,可以通过 codec 的值来判断当前文件是否是自己想要的文件。