feat: 上传视频/图片处理
This commit is contained in:
@ -113,22 +113,155 @@ export function genRandomId() {
|
||||
|
||||
export function formatFileSize(bytes: number): string {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
|
||||
|
||||
const k = 1024;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
export function getVideoDuration(file: any) {
|
||||
// 修改函数以同时获取视频时长和首帧
|
||||
export function getVideoInfo(file: File): Promise<{ duration: number; firstFrame: string }> {
|
||||
return new Promise((resolve) => {
|
||||
const video = document.createElement('video');
|
||||
video.preload = 'metadata';
|
||||
video.onloadedmetadata = function() {
|
||||
window.URL.revokeObjectURL(video.src);
|
||||
resolve(video.duration);
|
||||
video.crossOrigin = 'anonymous'; // 避免跨域问题
|
||||
video.muted = true; // 静音,避免意外播放声音
|
||||
video.style.position = 'fixed'; // 确保视频元素在DOM中
|
||||
video.style.top = '-1000px'; // 但不可见
|
||||
document.body.appendChild(video); // 添加到DOM
|
||||
|
||||
let hasResolved = false;
|
||||
|
||||
// 先获取元数据(时长)
|
||||
video.onloadedmetadata = function () {
|
||||
// 视频时长
|
||||
const duration = video.duration;
|
||||
|
||||
// 尝试将视频定位到非常小的时间点,确保有帧可捕获
|
||||
if (duration > 0) {
|
||||
video.currentTime = Math.min(0.1, duration / 2);
|
||||
}
|
||||
};
|
||||
|
||||
// 当视频定位完成后尝试捕获首帧
|
||||
video.onseeked = function () {
|
||||
if (hasResolved) return;
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = video.videoWidth;
|
||||
canvas.height = video.videoHeight;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (ctx) {
|
||||
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
|
||||
// 清理
|
||||
window.URL.revokeObjectURL(video.src);
|
||||
document.body.removeChild(video);
|
||||
|
||||
// 返回结果
|
||||
hasResolved = true;
|
||||
resolve({
|
||||
duration: video.duration,
|
||||
firstFrame: canvas.toDataURL('image/jpeg', 0.9) // 提高质量
|
||||
});
|
||||
};
|
||||
|
||||
// 作为备选方案,监听loadeddata事件
|
||||
video.onloadeddata = function () {
|
||||
if (hasResolved) return;
|
||||
|
||||
// 尝试捕获帧
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = video.videoWidth;
|
||||
canvas.height = video.videoHeight;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (ctx) {
|
||||
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
|
||||
// 检查是否捕获到有效帧(非全黑)
|
||||
const imageData = ctx?.getImageData(0, 0, canvas.width, canvas.height);
|
||||
if (imageData) {
|
||||
let isAllBlack = true;
|
||||
for (let i = 0; i < imageData.data.length; i += 4) {
|
||||
if (imageData.data[i] > 10 || imageData.data[i+1] > 10 || imageData.data[i+2] > 10) {
|
||||
isAllBlack = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAllBlack) {
|
||||
// 清理
|
||||
window.URL.revokeObjectURL(video.src);
|
||||
document.body.removeChild(video);
|
||||
|
||||
// 返回结果
|
||||
hasResolved = true;
|
||||
resolve({
|
||||
duration: video.duration,
|
||||
firstFrame: canvas.toDataURL('image/jpeg', 0.9)
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是全黑帧,尝试定位到0.1秒
|
||||
if (video.duration > 0) {
|
||||
video.currentTime = 0.1;
|
||||
}
|
||||
};
|
||||
|
||||
// 设置视频源以触发加载
|
||||
video.src = URL.createObjectURL(file);
|
||||
|
||||
// 设置超时,防止长时间无响应
|
||||
setTimeout(() => {
|
||||
if (!hasResolved) {
|
||||
document.body.removeChild(video);
|
||||
resolve({
|
||||
duration: 0,
|
||||
firstFrame: ''
|
||||
});
|
||||
}
|
||||
}, 5000); // 5秒超时
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const formatDuration = (seconds: number) => {
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
const remainingSecondsAfterHours = seconds % 3600;
|
||||
const minutes = Math.floor(remainingSecondsAfterHours / 60);
|
||||
const remainingSeconds = Math.floor(remainingSecondsAfterHours % 60);
|
||||
|
||||
if (hours > 0) {
|
||||
if (remainingSecondsAfterHours === 0) {
|
||||
return `${hours}小时`;
|
||||
}
|
||||
if (remainingSeconds === 0) {
|
||||
return `${hours}小时${minutes}分`;
|
||||
}
|
||||
return `${hours}小时${minutes}分${remainingSeconds}秒`;
|
||||
} else if (minutes > 0) {
|
||||
if (remainingSeconds === 0) {
|
||||
return `${minutes}分`;
|
||||
}
|
||||
return `${minutes}分${remainingSeconds}秒`;
|
||||
} else {
|
||||
return `${remainingSeconds}秒`;
|
||||
}
|
||||
};
|
||||
|
||||
export const formatUploadSpeed = (bytesPerSecond: number): string => {
|
||||
if (bytesPerSecond < 1024) {
|
||||
return `${bytesPerSecond.toFixed(2)} B/s`;
|
||||
} else if (bytesPerSecond < 1024 * 1024) {
|
||||
return `${(bytesPerSecond / 1024).toFixed(2)} KB/s`;
|
||||
} else {
|
||||
return `${(bytesPerSecond / (1024 * 1024)).toFixed(2)} MB/s`;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user