上传音频文件
思路
1、自定义Upload
重点:<input ref={inputRef} type="file" accept={accept} onClick={e => e.stopPropagation()} onChange={uploadFile} multiple={multiple}/>
使用input标签设置type是file,将input元素通过forwardRef暴露给父组件,使父组件可以通过useImperativeHandle透传的resetValue方法在外部控制input的value值,
2、自定义AudioUpload
通过区分iOS和Android设置不同的accept来解决格式的兼容性问题
上传流程:文件格式校验——音频时长校验——获取upload token——上传文件——获取到ossUrl
3、使用 AudioUpload组件
.mp3, .wav, .m4a 和 audio/*
.mp3, .wav, 和 .m4a 是具体的音频文件格式
audio/* 是一个 MIME 类型,它表示所有音频文件类型
- MP3 比较流行,有损压缩的音频格式
- wav 无损未压缩的,文件较大
- m4a 通常用于Apple设备
- audio/*支持大多数音频
总结:需要处理特定格式的音频文件用前者;希望支持多种音频格式用后者;
1、自定义Upload
Upload/index.module.css
.tongyi-upload {outline: 0;
}
Upload/index.tsx
import React, { ReactNode, forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
import classnames from 'classnames';
import styles from './index.module.css';interface UploadChangeParam {file: File;fileList: File[];event?: { percent: number };
}interface UploadProps {className?: string;style?: React.CSSProperties;accept?: string;multiple?: boolean;withCredentials?: boolean;children: ReactNode;// 限制大小maxSize?: number;// onChange?: (info: UploadChangeParam) => void;customRequest: (info: { file: File }) => Promise<void>;onError?: (e: any) => void;
}export default forwardRef((props: UploadProps, ref: any) => {const { className, accept, maxSize, multiple = false, customRequest, onError, children } = props;const inputRef = useRef<HTMLInputElement>(null);const uploadFile = (e: React.ChangeEvent<HTMLInputElement>) => {const { files } = e.target;if (!files) {return;}const originFiles = [...files] as File[];originFiles.forEach((file: File) => {if (maxSize && file.size > maxSize) {onError && onError({code: 'FILE_EXCEEDS_SIZE'});return;}return customRequest && customRequest({file});});};useImperativeHandle(ref, () => {return {resetValue: () => {if (inputRef.current) {inputRef.current.value = '';}}}});return (<divclassName={classnames(styles['tongyi-upload'], className)}onClick={() => inputRef.current?.click()}style={props.style}><inputtype="file"ref={inputRef}style={{ display: 'none' }}accept={accept}onClick={e => e.stopPropagation()}onChange={uploadFile}multiple={multiple}/>{children}</div>)
})
2、自定义AudioUpload
AudioUpload.tsx
import { getUploadToken, uploadFile } from '@/services/file';
import Upload from './Upload';
import { getDeviceType } from '@/utils';
import { getAudioDuration } from '@/utils/audioFile';
import React, {forwardRef,ReactNode,useImperativeHandle,useRef,
} from 'react';interface UploadTokenData {accessId: string;policy: string;signature: string;dir: string;host: string;expire: number;bucketName: string;key: string;
}interface UploadProps {className?: string;accept?: string;multiple?: boolean;withCredentials?: boolean;style?: React.CSSProperties;children: ReactNode;beforeUpload?: () => void;onChange?: (ossUrl: string) => void;onError?: (err: any) => void;
}// 文件大小限制
const maxSize = 10 * 1024 * 1024;
const audioFileType = ['mp3', 'aac', 'wav', 'flac', 'ogg', 'm4a'];export default forwardRef((props: UploadProps, ref: any) => {const uploadStatusRef = useRef<any>({});const uploadRef = useRef<any>({});const proxyFn = (fn: () => Promise<any>) => {if (uploadStatusRef.current.status !== 'cancel') {return fn();}return Promise.reject('cancel');};const customRequest = async (info: { file: File }) => {uploadStatusRef.current.status = 'ready';if (!info.file?.type.startsWith('audio/')) {props.onError &&props.onError({code: 'FILE_TYPE_ERROR',message: '',});return Promise.reject('FILE_TYPE_ERROR');}const duration: number = await getAudioDuration(info.file);console.log('获取到的时长', duration);if (duration < 10 || duration > 30) {props.onError &&props.onError({code: 'FILE_DURATION_ERROR',message: '',});return Promise.reject('FILE_DURATION_ERROR');}props.beforeUpload && props.beforeUpload();console.log('文件信息=========', info.file);return (// proxyFn(() => getAudioUploadToken(info.file.name))proxyFn(() => getUploadToken()).then((data: UploadTokenData) => {console.log('getAudioUploadToken的结果', data);return proxyFn(() => uploadFile(data, info.file)).then((uploadRes) => {console.log('uploadFile成功了', uploadRes);props.onChange && props.onChange(uploadRes?.ossUrl);}).catch((e) => {console.log('uploadFile报错了===========', e);props.onError &&props.onError({code: 'UNKNOW',message: e.errorMsg,});});}).catch((e) => {console.log('e=========', e);const { errorMsg } = e;if (e !== 'cancel') {props.onError &&props.onError({code: 'UPLOAD_ERROR',});}}));};useImperativeHandle(ref,() => ({cancel: () => {uploadStatusRef.current.status = 'cancel';uploadRef.current.resetValue();clearTimeout(uploadStatusRef.current.clock);},}),[],);const accept = getDeviceType() ? '.mp3, .wav, .m4a' : 'audio/*';return (<UploadmaxSize={maxSize}accept={accept}{...props}ref={uploadRef}customRequest={customRequest}>{props.children}</Upload>);
});
3、使用 AudioUpload组件
// 上传组件
const uploadRef = useRef<any>();
// 上传状态
const[uploadStatus, setUploadStatus] = useState<string>('default');
// 上传定时器
const uploadTimer = useRef<any>();
// 合成进度
const [percent, setPercent] = useState<number>(0);/*** 开始上传*/const startUpload = () => {console.log('上传中');setUploadStatus('processing');setPercent(0);const fn = () => {const i = Math.ceil(Math.random() * 3);uploadTimer.current = setTimeout(() => {fn();}, 1 * 1000);setPercent((pre) => {let current = pre + i;if (current >= 100) {current = 100;clearTimeout(uploadTimer.current);}return current;});};clearTimeout(uploadTimer.current);uploadTimer.current = setTimeout(() => {fn();}, 1 * 1000);};
/*** 上传失败* @param e*/const onError = (e: any) => {console.log('上传失败了');onChange('');setUploadStatus('error');clearTimeout(uploadTimer.current);uploadRef.current.cancel();setPercent(0);let msg = '';switch (e.code) {case 'FILE_DURATION_ERROR':msg = '请上传10-30秒音频文件';break;case 'FILE_EXCEEDS_SIZE':msg = '请上传10M以下的文件';break;case 'FILE_TYPE_ERROR':msg = '抱歉,请上传音频类型的文件';break;case 'SEC_RESULT':msg = e.message || `${e.code}抱歉,出错了,请换一个文件试试!`;break;case 'UPLOAD_ERROR':msg = '抱歉,上传失败,请重新上传';break;default:msg = `${e.code}抱歉,出错了,请换一个文件试试!`;break;}Toast.show({type: 'error',content: msg,});};/*** 上传成功*/const onUploaded = (ossUrl: string) => {console.log('上传成功获取到ossUrl', ossUrl);clearTimeout(uploadTimer.current);setUploadStatus('success');// 上传成功之后调用接口合成数字声音。。。onMergeSound(ossUrl);};
return (<AudioUploadonError={onError}onChange={onUploaded}beforeUpload={startUpload}ref={uploadRef}><div>上传</div></AudioUpload>
)
4、音频时长校验
/*** 异步获取音频文件的时长* @param file 音频文件* @returns 返回音频的时长(秒)*/
export const getAudioDuration = async (file) => {try {const audio = new Audio(URL.createObjectURL(file));await new Promise((resolve) => (audio.onloadedmetadata = resolve));const { duration } = audio;return duration;} catch (error) {console.error('获取音频时长时发生错误:', error);return 0;}
};
5、上传文件
export const uploadFile = (data: UploadTokenData, file: File) => {console.log('uploadFile开始了', data, '====', file);const bodyFormData = new FormData();const url = `${data.host}/${data.dir}${file.name}`;bodyFormData.append('OSSAccessKeyId', data.accessId);bodyFormData.append('policy', data.policy);bodyFormData.append('signature', data.signature);bodyFormData.append('key', `${data.dir}${file.name}`);bodyFormData.append('dir', data.dir);bodyFormData.append('success_action_status', '200');bodyFormData.append('file', file);console.log('uploadFile上传的url: ', url);return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();xhr.onerror = function error(e) {console.log('upload error', e);reject(e);};xhr.onload = async () => {// allow success when 2xx status see https://github.com/react-component/upload/issues/34if (xhr.status < 200 || xhr.status >= 300) {reject('上传异常');}console.log('upload success');resolve({...data,ossUrl: url,});};xhr.open('post', data.host, true);xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');xhr.send(bodyFormData);});
};
相关文章:
上传音频文件
思路 1、自定义Upload 重点:<input ref{inputRef} type"file" accept{accept} onClick{e > e.stopPropagation()} onChange{uploadFile} multiple{multiple}/> 使用input标签设置type是file,将input元素通过forwardRef暴露给父组件&…...
Linux之jdk离线安装
下载地址 一、linux安装jdk8 1、上传,解压 cd /usr/local/java tar -zxvf jdk-10.0.2_linux-x64_bin.tar.gz2、修改配置 vim /etc/profile #在/etc/profile文件后面加上如下配置 export JAVA_HOME/usr/local/java/jdk-10.0.2 export JRE_HOME/usr/local/java/jd…...
JVM结构、架构与生命周期总结
【1】JVM结构 不同厂商的JVM产品 : 厂商JVMOracle-SUNHotspotOracleJRocketIBMJ9 JVM阿里Taobao JVM HotSpot VM是目前市面上高性能虚拟机的代表作之一。它采用解释器与即时编译器并存的架构。 在今天,Java程序的运行性能早已脱胎换骨,已…...
Flink-StarRocks详解:第四部分StarRocks分区管理,数据压缩(第54天)
文章目录 前言2.3.3 管理分区2.3.3.1 增加分区2.3.3.2 删除分区2.3.3.3 恢复分区2.3.3.4 查看分区 2.3.4 设置分桶2.3.4.1 随机分桶(自 v3.1)2.3.4.2 哈希分桶2.3.4.2.1 优点2.3.4.2.2 如何选择分桶键2.3.4.2.3 注意事项 2.3.4.3 确定分桶数量 2.3.5 最佳…...
为什么有时候银行贷款审核会查大数据信用?
在申请银行贷款时,不少人会疑惑为何银行会深入审查申请人的大数据信用信息。这背后,其实是银行风险控制与精准决策的体现。 首先,大数据信用信用能全面反映申请人的信用状况 它不仅仅局限于传统的征信报告,还涵盖了消费行为、社交…...
LoRa无线通讯,让光伏机器人实现无“线”管理
光伏清洁机器人,作为光伏电站运维的新兴关键设备,已跃升为继组件、支架、光伏逆变器之后的第四大核心组件,正逐步成为光伏电站的标准配置。鉴于光伏电站普遍坐落于偏远无人区或地形复杂之地,光伏清洁机器人必须具备远程操控能力、…...
买流量卡要注意什么,这些冷知识你一定要懂!
买流量卡要注意什么?别总盯着价格看,还有一些隐形的冷知识得了解一下,今天这篇文章就是要告诉你一些流量卡中隐藏的冷知识。 一、首先,那些月租9元、19元的流量卡,大概率都是短期卡,虽然他们的资费便宜&a…...
【嵌入式】STM3212864点阵屏使用SimpleGUI单色屏接口库——(2)精简字库
一 开源库简介与移植 最近一个项目需要用12864屏幕呈现一组较为复杂的菜单界面,本着不重复造轮子的原则找到了SimpleGUI开源库。 开源地址:SimpleGUI: 一个面向单色显示屏的开源GUI接口库。 SimpleGUI是一款针对单色显示屏设计的接口库。相比于传统的GUI…...
《计算机网络》(第8版)第1章 概述 复习笔记
第 1 章 概述 一、计算机网络在信息时代中的作用 计算机网络的两个重要功能: 1 .连通性 指互联网上的用户之间是相互连通的。 2 .共享(资源共享) 资源共享可以是信息共享、软件共享,也可以是硬件共享。此…...
银行数据质量保障体系建设实践
引言 在数字化转型浪潮中,数据中台成为企业实现数据驱动决策的关键支撑。它不仅整合了企业内外部的数据资源,还通过数据共享与复用,提升了运营效率和业务创新能力。然而,随着数据量的激增和数据来源的多样化,如何确保…...
笔记小结:《利用Python进行数据分析》二进制数据格式存储与web交互
提示:此节内容仅作了解即可 目录 二进制数据格式 使用HDF5 读取Microsoft Excel文件 二进制数据格式 实现数据的高效二进制格式存储最简单的办法之一是使用Python内置的pickle序列化。 Python 的 pickle 模块是一个用于序列化和反序列化 Python 对象结构的模块…...
电脑桌面图标变白了?3个方法20秒钟轻松解
电脑桌面图标变白了?3个方法20秒钟轻松解 ⚠️电脑桌面图标变白了,3种方法轻松解决 🚸方法一和方法二属于治标不治本的解决方法,但操作较为简单,在不同情况下有不成功的可能,方法三相对复杂一些,…...
数据治理,管什么?
元数据(Metadata):通俗地说就是描述数据的数据,比如数据的名称、属性、分类、字段信息、大小、标签等等。要做好数据的管理,元数据起到了举足轻重的作用。 参考数据(Reference Data)࿱…...
【前端】JavaScript入门及实战121-125
文章目录 121 滚轮事件122 键盘事件123 键盘移动div124 BOM125 History 121 滚轮事件 <!DOCTYPE html> <html> <head> <title></title> <meta charset "utf-8"> <style type"text/css">#box1 {width: 100px;h…...
pytest测试框架之http协议接口测试
1 接口测试 日常测试中接口测试是一项重要的工作,尤其是http协议的接口测试更加普遍,比如一些常用的测试框架或者工具(robotframework框架,testng框架,postman等)都支持http接口的测试,而这节内容主要介绍…...
FFmpeg源码:av_gcd函数分析
一、引言 公约数,是一个能同时整除几个整数的数。如果一个整数同时是几个整数的约数,称这个整数为它们的“公约数”;公约数中最大的称为最大公约数。对任意的若干个正整数,1总是它们的公约数。 公约数与公倍数相反,就…...
springboot物流寄查系统-计算机毕业设计源码95192
目 录 1 绪论 1.1 研究背景 1.2选题背景 1.3论文结构与章节安排 2 springboot物流寄查系统系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 法律可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非功能性分析 2.3 系统用例分析 2…...
【秋招笔试】24-07-27-OPPO-秋招笔试题(算法岗)
🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 编程一对一辅导 ✨ 本系列打算持续跟新 秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 💡 第一题贪心模拟…...
AUTOSAR实战教程 - 模式管理BswM与其他各模块的交互
近日驻厂某OEM,幸得大块的个人时间, 把BswM这一块的内容从ETAS/ISOLAR工具配置到代码实现做了一个全方位的CT. 2024,希望孜孜内卷的汽车人升职加薪! 博主近期写的一首小诗,也一并送给大家,懂的都懂: 在看不到阳光的冬天/ 我染了风寒/ 白天点灯/ 晚上吃药/ 躺在被窝里才敢…...
经典非比较排序—计数排序的Java实现方式
目录 1.具体思路: 2.代码实现: 3.代码分析 4.示例测试: 测试源码: 测试结果: 计数排序,又被称为鸽巢原理,属于桶排序的一种,其本质是通过哈希映射思想,设定计数数组输入以…...
大疆L1点云数据导出后,用CloudCompare做可视化与简单分析的完整流程
大疆L1点云数据从导出到分析:CloudCompare实战全流程指南 当你从DJI Terra中导出L1激光雷达的LAS文件时,真正的数据价值挖掘才刚刚开始。作为测绘工程师或三维建模从业者,如何将这些原始点云转化为可操作的洞察?本文将带你用开源神…...
避坑指南:Xilinx PCIe IP的lane反序问题与GT时钟约束的隐藏陷阱
Xilinx PCIe IP实战:破解Lane反序与GT时钟约束的五大核心难题 当你在Vivado中首次生成PCIe IP核时,可能会惊讶地发现硬件实际的lane顺序与代码中的定义完全相反。这不是bug,而是Xilinx默认的设计特性。更棘手的是,GT参考时钟的自动…...
QDKTAI实战面试题50问之41-50
一、逐题详细解析(41-50题)第41题:如何设计一个有效的AI内容审核系统?1. 考察重点对大模型“生成式而非判别式”核心特性的理解识别AI审核场景中的关键冲突点(长文本处理、语气/风格干扰)针对性解决方案的设…...
硬件工程师转向嵌入式软件开发的十大技巧
嵌入式系统设计:硬件工程师转向软件开发的十大实用技巧1. 引言嵌入式系统设计是硬件与软件紧密结合的领域,硬件工程师在扩展技能到软件开发时,需要建立新的思维模式和工作方法。本文总结了硬件工程师转向软件设计时需要掌握的十大关键技巧&am…...
Qwen1.5-1.8B GPTQ生成技术博客大纲与初稿:以“操作系统内存管理”为例
Qwen1.5-1.8B GPTQ生成技术博客大纲与初稿:以“操作系统内存管理”为例 1. 引言:当AI成为技术写作的“副驾驶” 最近在折腾一些技术分享,想写一篇关于操作系统内存管理的文章。这话题吧,说深了容易劝退,说浅了又没意…...
塑胶件防裂实践:3D检测亲测有效
行业痛点分析在精密制造领域,塑胶件开裂是长期困扰工程师的顽疾。传统检测手段,如卡尺、三坐标测量机(CMM)或二维影像测量,存在明显局限。它们难以对复杂曲面、内部应力集中区域进行非接触式、全尺寸的量化评估&#x…...
页游党必看!传奇、篮球、策略全都有,点击即玩
对于喜欢玩网页游戏的朋友来说,找一个靠谱、福利多、游戏全的平台太重要了!不用下载、点击即玩,还能安心挂机不担心跑路,这样的平台才是真刚需~ 今天就给大家安利一个深耕页游十余载的老牌平台——602游戏平台&#x…...
Wemod-Patcher:开源工具实现WeMod功能增强的完整方案
Wemod-Patcher:开源工具实现WeMod功能增强的完整方案 【免费下载链接】Wemod-Patcher WeMod patcher allows you to get some WeMod Pro features absolutely free 项目地址: https://gitcode.com/gh_mirrors/we/Wemod-Patcher 在游戏体验优化领域࿰…...
深入解析UniApp中的package.json:从基础配置到高级技巧
1. 初识UniApp中的package.json 第一次接触UniApp项目时,我盯着package.json文件看了半天,心想这不就是个管理npm包依赖的配置文件吗?直到踩了几个坑才发现,UniApp对这个文件做了特殊扩展,让它成为了项目配置的中枢神经…...
终极指南:Shenyu网关集成Polaris服务治理平台的完整教程
终极指南:Shenyu网关集成Polaris服务治理平台的完整教程 Shenyu网关作为基于Spring Cloud的高性能API网关,与Polaris服务治理平台的集成能够为企业级微服务架构提供强大的流量控制和动态配置能力。本教程将详细讲解如何从零开始配置Shenyu网关与Polaris…...
