当前位置: 首页 > news >正文

上传音频文件

思路

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 重点&#xff1a;<input ref{inputRef} type"file" accept{accept} onClick{e > e.stopPropagation()} onChange{uploadFile} multiple{multiple}/> 使用input标签设置type是file&#xff0c;将input元素通过forwardRef暴露给父组件&…...

Linux之jdk离线安装

下载地址 一、linux安装jdk8 1、上传&#xff0c;解压 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产品 &#xff1a; 厂商JVMOracle-SUNHotspotOracleJRocketIBMJ9 JVM阿里Taobao JVM HotSpot VM是目前市面上高性能虚拟机的代表作之一。它采用解释器与即时编译器并存的架构。 在今天&#xff0c;Java程序的运行性能早已脱胎换骨&#xff0c;已…...

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 随机分桶&#xff08;自 v3.1&#xff09;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 最佳…...

为什么有时候银行贷款审核会查大数据信用?

在申请银行贷款时&#xff0c;不少人会疑惑为何银行会深入审查申请人的大数据信用信息。这背后&#xff0c;其实是银行风险控制与精准决策的体现。 首先&#xff0c;大数据信用信用能全面反映申请人的信用状况 它不仅仅局限于传统的征信报告&#xff0c;还涵盖了消费行为、社交…...

LoRa无线通讯,让光伏机器人实现无“线”管理

光伏清洁机器人&#xff0c;作为光伏电站运维的新兴关键设备&#xff0c;已跃升为继组件、支架、光伏逆变器之后的第四大核心组件&#xff0c;正逐步成为光伏电站的标准配置。鉴于光伏电站普遍坐落于偏远无人区或地形复杂之地&#xff0c;光伏清洁机器人必须具备远程操控能力、…...

买流量卡要注意什么,这些冷知识你一定要懂!

买流量卡要注意什么&#xff1f;别总盯着价格看&#xff0c;还有一些隐形的冷知识得了解一下&#xff0c;今天这篇文章就是要告诉你一些流量卡中隐藏的冷知识。 ​一、首先&#xff0c;那些月租9元、19元的流量卡&#xff0c;大概率都是短期卡&#xff0c;虽然他们的资费便宜&a…...

【嵌入式】STM3212864点阵屏使用SimpleGUI单色屏接口库——(2)精简字库

一 开源库简介与移植 最近一个项目需要用12864屏幕呈现一组较为复杂的菜单界面&#xff0c;本着不重复造轮子的原则找到了SimpleGUI开源库。 开源地址&#xff1a;SimpleGUI: 一个面向单色显示屏的开源GUI接口库。 SimpleGUI是一款针对单色显示屏设计的接口库。相比于传统的GUI…...

《计算机网络》(第8版)第1章 概述 复习笔记

第 1 章 概述 一、计算机网络在信息时代中的作用 计算机网络的两个重要功能&#xff1a; 1 &#xff0e;连通性 指互联网上的用户之间是相互连通的。 2 &#xff0e;共享&#xff08;资源共享&#xff09; 资源共享可以是信息共享、软件共享&#xff0c;也可以是硬件共享。此…...

银行数据质量保障体系建设实践

引言 在数字化转型浪潮中&#xff0c;数据中台成为企业实现数据驱动决策的关键支撑。它不仅整合了企业内外部的数据资源&#xff0c;还通过数据共享与复用&#xff0c;提升了运营效率和业务创新能力。然而&#xff0c;随着数据量的激增和数据来源的多样化&#xff0c;如何确保…...

笔记小结:《利用Python进行数据分析》二进制数据格式存储与web交互

提示&#xff1a;此节内容仅作了解即可 目录 二进制数据格式 使用HDF5 读取Microsoft Excel文件 二进制数据格式 实现数据的高效二进制格式存储最简单的办法之一是使用Python内置的pickle序列化。 Python 的 pickle 模块是一个用于序列化和反序列化 Python 对象结构的模块…...

电脑桌面图标变白了?3个方法20秒钟轻松解

电脑桌面图标变白了&#xff1f;3个方法20秒钟轻松解 ⚠️电脑桌面图标变白了&#xff0c;3种方法轻松解决 &#x1f6b8;方法一和方法二属于治标不治本的解决方法&#xff0c;但操作较为简单&#xff0c;在不同情况下有不成功的可能&#xff0c;方法三相对复杂一些&#xff0c…...

数据治理,管什么?

元数据&#xff08;Metadata&#xff09;&#xff1a;通俗地说就是描述数据的数据&#xff0c;比如数据的名称、属性、分类、字段信息、大小、标签等等。要做好数据的管理&#xff0c;元数据起到了举足轻重的作用。 参考数据&#xff08;Reference Data&#xff09;&#xff1…...

【前端】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 接口测试 日常测试中接口测试是一项重要的工作&#xff0c;尤其是http协议的接口测试更加普遍,比如一些常用的测试框架或者工具&#xff08;robotframework框架&#xff0c;testng框架&#xff0c;postman等&#xff09;都支持http接口的测试&#xff0c;而这节内容主要介绍…...

FFmpeg源码:av_gcd函数分析

一、引言 公约数&#xff0c;是一个能同时整除几个整数的数。如果一个整数同时是几个整数的约数&#xff0c;称这个整数为它们的“公约数”&#xff1b;公约数中最大的称为最大公约数。对任意的若干个正整数&#xff0c;1总是它们的公约数。 公约数与公倍数相反&#xff0c;就…...

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.具体思路&#xff1a; 2.代码实现&#xff1a; 3.代码分析 4.示例测试&#xff1a; 测试源码&#xff1a; 测试结果&#xff1a; 计数排序&#xff0c;又被称为鸽巢原理&#xff0c;属于桶排序的一种&#xff0c;其本质是通过哈希映射思想&#xff0c;设定计数数组输入以…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

蓝桥杯 2024 15届国赛 A组 儿童节快乐

P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡&#xff0c;轻快的音乐在耳边持续回荡&#xff0c;小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下&#xff0c;六一来了。 今天是六一儿童节&#xff0c;小蓝老师为了让大家在节…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

06 Deep learning神经网络编程基础 激活函数 --吴恩达

深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...