1.选择相同视频未触发change事件
<input type="file" name="file" id="file" />
<script>
// 在适当时机克隆input元素后移除原来的input
$("#file").after($("#file").clone().val("")).remove();
</script>
参考:INPUT[type=file]的change事件不触发问题_风神修罗使的博客-CSDN博客
2.监听change事件判断文件类型
function bindChangeEvent() {
$(document)
.off("change")
.on("change", "#file", function () {
var fileObj = $(this);
if (fileObj.val().length < 1) return;
var file = fileObj[0].files["0"];
if (!file) return;
const type = file.type;
const supportedTypes = ["video", "image", "rar", "zip"];
if (type && !supportedTypes.some((e) => type.includes(e))) {
alert("暂不支持上传");
return;
}
// rar文件类型的type为空,此时根据文件名后缀判断
if (!type && !/\.rar$/.test(file.name)) {
alert("暂不支持上传");
return;
}
if (type.includes("image/")) {
uploadPic(file); // todo: 上传图片
} else {
uploadFile(file, type); // todo: 上传文件
}
});
}
3.判断文件大小
function isSopportSize(file, size) {
if (file.size.toFixed(2) > size * 1024 * 1024) {
alert(`请选择小于${size}MB的文件`);
return false;
}
return true
}
4.上传Key为mov格式文件到COS无法播放,特殊处理改为mp4
if (/\.(mov|MOV)$/.test(file.name)) {
fileName = file.name.replace(/\.(mov|MOV)$/, '.mp4')
}
5.cos 实例调用 COS 请求
// cos 实例调用 COS 请求
cos.uploadFile(
{
Bucket,
Region,
Body: file,
SliceSize: 1024 * 1024 * 5, // 分块上传会多次调用 new COS中传入的getAuthorization
ContentType: type, // 添加上传的文件类型,COS会根据该类型设置Content-Type
Key: fileName,
...
})
6.获取视频时长的3种方法
使用HTML5的File API和Video API:
<input type="file" id="videoInput" accept="video/*" />
<script>
var videoInput = document.getElementById("videoInput");
videoInput.addEventListener("change", function () {
var file = this.files[0];
var video = document.createElement("video");
var url = URL.createObjectURL(file);
video.src = url;
video.addEventListener("loadedmetadata", function () {
console.log("视频时长:", video.duration);
});
});
// var audioElement = new Audio(URL.createObjectURL(file));
// audioElement.muted = true;
// audioElement.play().then(() => audioElement.pause());
// audioElement.addEventListener(
// "loadedmetadata",
// function () {
// console.log("视频时长:", audioElement.duration);
// audioElement.muted = false;
// }
// );
</script>
- 使用第三方库如`video.js`进行视频播放和获取时长:
<input type="file" id="videoInput" accept="video/*" />
<script src="https://vjs.zencdn.net/7.14.3/video.js"></script>
<link href="https://vjs.zencdn.net/7.14.3/video-js.css" rel="stylesheet" />
<script>
var videoInput = document.getElementById("videoInput");
videoInput.addEventListener("change", function () {
var file = this.files[0];
var video = document.createElement("video");
var player = videojs(video);
player.on("loadedmetadata", function () {
console.log("视频时长:", player.duration());
});
var reader = new FileReader();
reader.onload = function (e) {
player.src({
src: e.target.result,
type: "video/mp4",
});
};
reader.readAsDataURL(file);
});
</script>
- 使用ffmpeg.js进行视频解码和获取时长(需要将视频文件上传到服务器进行解码):
<input type="file" id="videoInput" accept="video/*" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/ffmpeg.js/1.8.4/ffmpeg.min.js"></script>
<script>
var videoInput = document.getElementById("videoInput");
videoInput.addEventListener("change", function () {
var file = this.files[0];
var video = document.createElement("video");
var url = URL.createObjectURL(file);
video.src = url;
video.addEventListener("loadedmetadata", function () {
// 使用ffmpeg.js进行视频解码
ffmpeg.FS("writeFile", "input.mp4", new Uint8Array(file));
ffmpeg.run("-i input.mp4", "-hide_banner", function (output) {
var regex = /duration (\d+:\d+:\d+)/;
var duration = output.match(regex)[1];
console.log("视频时长:", duration);
});
});
});
</script>
注意:
- 以上实现方式可能需要使用`http(s)`协议进行访问,因为某些浏览器限制了`file://`协议下的一些操作。另外,第三方库`video.js`和`ffmpeg.js`需要在支持`HTML5`的浏览器中使用。
-
微信浏览器h5 loadedmetadata 兼容性问题参考微信浏览器h5 loadedmetadata 兼容性问题_weixin_45191052的博客-CSDN博客
7.时长和文件单位工具函数
// 文件单位转换
filesizeConvert(size) {
var data = ''
if (size < 0.1 * 1024) {
data = size.toFixed(2) + 'B'
} else if (size < 0.1 * 1024 * 1024) {
data = (size / 1024).toFixed(2) + 'KB'
} else if (size < 0.1 * 1024 * 1024 * 1024) {
data = (size / (1024 * 1024)).toFixed(2) + 'MB'
} else {
data = (size / (1024 * 1024 * 1024)).toFixed(2) + 'GB'
}
var sizestr = data + ''
var len = sizestr.indexOf('.')
var dec = sizestr.substr(len + 1, 2)
if (dec == '00') {
return sizestr.substring(0, len) + sizestr.substr(len + 3, 2)
}
return sizestr
},
// 视频格式化
formatDuraton(time) {
if (time > -1) {
var hour = Math.floor(time / 3600)
var min = Math.floor(time / 60) % 60
var sec = time % 60
if (hour < 10) {
time = '0' + hour + ':'
} else {
time = hour + ':'
}
if (min < 10) {
time += '0'
}
time += min + ':'
if (sec < 10) {
time += '0'
}
time += sec
}
return time
},
8.视频和进度条模板
<style>
/* 上传圆形进度条 */
.upload-loading {
width: 46px;
height: 46px;
padding: 20px;
position: relative;
overflow: hidden;
border-radius: 50%;
line-height: 46px;
text-align: center;
font-size: 10px;
color: #fff;
}
.upload-loading strong {
font-weight: normal;
}
.upload-loading canvas {
position: absolute;
top: 20px;
left: 20px;
}
</style>
<script>
function setVideoHtml(message) {
const { url, isFirst, task_Id, isSuccess, duration } = message;
const isWechat = isWechat();
const isAndroid = isAndroid();
let cosUrl = `https://${url}`;
cosUrl = isAndroid ? cosUrl : cosUrl + "#t=0.1";
const mySrc = `${isFirst ? url : cosUrl}`;
const isShowControls = isFirst || !isSuccess;
return `<div data-taskId=${task_Id} class="video-main">
<video controlsList="nodownload" disablePictureInPicture id="videoPlayer" initial-time="0.01" preload="metadata" ${
isAndroid || isWechat
? `x5-video-orientation="portrait" x5-video-player-fullscreen="true" x5-video-player-type="h5" poster="${mySrc}?vframe/jpg/offset/1"`
: ""
}
style="border-radius:8px;vertical-align:middle;max-width:158px;max-height:158px;${
isAndroid || isWechat ? "min-height:90px;" : "width:auto;height:auto"
}">
<source src="${url}" type="video/mp4">
<source src="${url}" type="video/webm">
<source src="${url}" type="video/ogg">
您的浏览器不支持 HTML5 video 标签。
</video>
<span>${duration}</span>
${
isFirst
? `<div class="loading middle">${setCircleLoadingHtml(
task_Id
)}</div>`
: ""
}
</div>`.replace(/\n/gm, "");
}
function setCircleLoadingHtml(task_Id) {
return `<div class="upload-loading">
<strong id="number">0%</strong>
<canvas width="46" height="46" id="ccb-${task_Id}"></canvas>
<canvas width="46" height="46" id="ccc-${task_Id}"></canvas>
</div>`.replace(/\n/gm, "");
}
</script>
9.绘制上传进度的进度条
// 绘制进度条
function loadingCanvas(taskId) {
var c1 = document.getElementById(`ccb-${taskId}`) // 进度条
var ctx1 = c1.getContext('2d')
ctx1.strokeStyle = 'rgb(123,165,236)'
ctx1.lineWidth = 4
var c = document.getElementById(`ccc-${taskId}`) // 进度条背景
var ctx = c.getContext('2d')
ctx.beginPath()
ctx.lineWidth = 4
ctx.strokeStyle = 'rgba(182,197,223,.5)'
ctx.arc(23, 23, 20, 0, 2 * Math.PI)
ctx.stroke()
return ctx1
}
// 进度条加载中
function loadingPercent(percent, ctx1, data_taskId) {
ctx1.beginPath() // 开始画图
ctx1.clearRect(0, 0, 210, 210) // 清除画布
$(`${data_taskId} #number`).text(percent.toFixed(0) + '%')
ctx1.arc(23, 23, 20, -0.5 * Math.PI, (1.5 * Math.PI * percent) / 100)
ctx1.stroke()
}
10.全屏播放
function bindVideo (data_taskId) {
$(`${data_taskId} .play`).show()
$(`${data_taskId} video`).attr({ controls: false })
const elvideo = $(`${data_taskId} video`)[0]
$(`${data_taskId} .play`)
.off('click')
.on('click', (e) => {
$(`${data_taskId} video`).attr({ controls: true })
$(`${data_taskId} .play`).hide()
$(`${data_taskId} span`).hide()
if (isAndroid()) {
requestFullScreen() // 安卓视频播放不自动全屏,特殊处理
}
if (isMinigram()) {
elvideo.muted = true // 设置静音
$(`${data_taskId} video`).attr('autoplay', true)
$(`${data_taskId} video`).load()
} else {
elvideo.play()
}
})
// 视频全屏按钮点击事件
elvideo.addEventListener('fullscreenchange', () => {
var fullScreenElement = document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement
// var orientation = window.orientation
if (fullScreenElement) {
// 全屏时的处理逻辑
// console.log('进入全屏')
// console.log('全屏元素:', fullScreenElement)
//要获取全屏横竖屏方向,您可以使用window.orientation属性。该属性返回显示屏幕方向的数值,其中0表示竖屏,90表示逆时针旋转90度,-90表示顺时针旋转90度。
// console.log('屏幕方向:', orientation)
} else {
// 退出全屏时的处理逻辑
// console.log('退出全屏')
elvideo.pause()
$(`${data_taskId} video`).attr({ controls: false })
$(`${data_taskId} video`).attr('autoplay', false)
$(`${data_taskId} .play`).show()
$(`${data_taskId} span`).show()
}
})
// 调用requestFullscreen方法实现全屏
function requestFullScreen() {
if (elvideo.requestFullscreen) {
elvideo.requestFullscreen()
} else if (elvideo.mozRequestFullScreen) {
elvideo.mozRequestFullScreen() //FireFox
} else if (elvideo.webkitRequestFullscreen) {
elvideo.webkitRequestFullscreen() //Chrome等
} else if (elvideo.msRequestFullscreen) {
elvideo.msRequestFullscreen() //IE11
}
}
},
11获取视频第一帧
// 获取video元素
var video = document.getElementById('video');
// 创建canvas元素
var canvas = document.createElement('canvas');
// 设置canvas的宽度和高度
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
// 获取canvas的上下文
var ctx = canvas.getContext('2d');
// 将video的第一帧画到canvas上
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
// 获取第一帧的图像数据
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
<video id="myVideo" width="320" height="240" controls>
<source src="movie.mp4" type="video/mp4">
</video>
<script>
var video = document.getElementById("myVideo");
video.currentTime = 0;
</script>
12强制禁用全屏按钮
在H5的video标签中,可以通过设置x5-video-player-fullscreen
属性为false
来禁用全屏按钮,避免出现方向冲突问题
<video src="video.mp4" controls x5-video-player-fullscreen="false"></video>