利用js和audio标签读取音频文件并计算音频时长


一、场景:系统需要动态修改声音文件并播放声音

1.尝试

  • (1)import方式直接引入音频文件
    尝试过await import(“路径”)的方式,这种方式可以动态引入一个绝对路径或相对路径的内容,但是必须在编译前就确定好此路径,然而我又不想通过修改文件的方式去实现这个需求,只想修改不同的路径,也就是传入一个变量而非一个明确路径

  • (2)利用node.js读取文件并对文件内容进行改造,这种方法经证实可行,方法见 2

2.通过查询网上的内容实现了具体解决方案

  • (1)原理

    • 各种媒体数据可以以 base64 编码方式直接嵌入网页中
  • (2)实现步骤

    • 利用node.js的fs.createReadStream动态读取指定地址的文件,并用buffer对数据流进行拼接、转码,转为base64的格式,通过拼接串的方式生成audio所需的src
    • 直接在界面创建audio标签/或者利用js动态创建"AUDIO"标签,将标签的src赋值,使用play()方法播放音频
  • (3)代码

        //引入的node模块
        const fs = require('fs');
        import { Buffer } from 'buffer';
    
        //使用fs模块读取本地文件,文件路径为url
        let rs = fs.createReadStream(url, {
            flags: "r",
            encoding: null,
            fd: null,
            autoClose: true,
            start: 0,
            highWaterMark:8 * 1024
        })
    
        let chunks=[]
    
        rs.on("data",(chunk)=>{
            chunks.push(chunk)
        })
    
        rs.on("end",()=>{//当读取完成后对数据进行处理
            let bs=null
            bs = Buffer.concat(chunks).toString("base64")
            if(bs){
                var audio = document.createElement("AUDIO");
        //此处filetype是文件类型,因为需求是需要支持mp3/wav
                let mysrc= 'data:audio/'+filetype+';base64,' + bs;
                audio.src = mysrc
                audio.volume = 1.0
                audio.play()
                console.log("播放声音")
            }
        })
    
  • (4)注意
    createReadStream()中的highWaterMark不进行设置的话可能会造成无法监听到end事件的问题。

3.参考

①借鉴的实现过程《nodejs读取本地mp3、mp4等媒体文件并播放》
②各种媒体数据以 base64 编码方式直接嵌入网页中的写法
③node.js相关文档

二、场景:查看音频文件的时长

在场景一的基础上,需要动态修改文件地址,读取本地文件,并查看地址指向本地文件的时长

1.尝试

  • 通过node.js中fs模块的stat方法可以获取文件信息
    但是只能获取到文件的基本信息,比如创建、更新、访问时间,文件名等
  • 第三方解析音频文件信息的库 node-id3
    但是这个库解析出来的内容有可能不全且有乱码,不能适用于全部音频文件
  • html中DOM Audio 对象
    可以通过getElementById()访问<audio>元素获得,这个Audio对象有个属性duration就是表示的时长,所以有了 2 中的解决方案

2.解决方案

  • 步骤

    • 在场景一的解决方案基础上,即先解析文件内容,将文件内容改造后传给audio元素的src属性
    • 然后利用getElementById()访问<audio>元素获得DOM对象,读取这个对象的duration属性即可
  • 注意
    解析文件并更新src的操作后不能立刻执行读取duration属性的操作,可能会读取到NAN,需要设置setTimeOut(),等src更新完成后再获取duration
    例如:
    jsAndAudio1

    jsAndAudio2

3.参考

①HTML DOM Audio 对象

附测试地址和代码

https://www.w3school.com.cn/tiy/t.asp?f=jsref_audio_create

    <!DOCTYPE html>
    <html>
        <body>

        <h3>演示如何查看音频元素时长</h3>
        <p>请点击按钮该元素将播放 .ogg 文件格式的声音,并查看音频时长。</p>
        <button onclick="myFunction()">试一下</button>
        <p id="demo"></p>
        <audio id="myaudio"><audio>
        <script>
            function myFunction()
            {
              var x = document.getElementById("myaudio");
              x.setAttribute("src", "/i/horse.mp3");
              x.setAttribute("controls", "controls");
              
              //document.getElementById("demo").innerHTML = "音频时长为:"+x.duration;
              
              setTimeout(()=>{
                document.getElementById("demo").innerHTML = "音频时长为:"+x.duration;
              },100)
              
            }
        </script>
        </body>
    </html>
<think>好的,用户的问题是关于如何通过JavaScript实现文件上传在输出音频时显示音频时长。我需要结合之前提供的引用内容,特别是引用[2]、[3]、[4]中的方法,来构建一个示例代码。 首先,用户上传文件需要使用HTML的input元素,类型为file,允许音频格式。接下来,通过File API获取用户上传的文件对象。然后,使用FileReader读取文件内容,或者直接创建一个对象URL作为音频元素的来源。 根据引用[3],直接设置audio.src为文件对象URL可能会导致duration暂时无法获取,出现NaN或Infinity的问题。解决方案是使用audio.addEventListener('loadedmetadata')事件,或者在metadata加载完成后读取duration。不过根据引用[3]的经验,可能需要设置currentTime来触发duration的正确获取,但可能需要结合异步等待。 引用[2]中提到,使用Deferred对象或Promise来处理异步获取duration的问题,因为可能需要等待一段时间才能获取到正确的值。因此,在代码中可能需要使用Promise定时器来轮询检查duration是否有效。 引用[4]提到使用FileReaderAudioContext来获取音频时长,但可能比较复杂。相比之下,使用audio元素事件监听可能更简单。 综合这些信息,步骤应该是: 1. 创建input元素让用户上传音频文件。 2. 监听input的change事件,获取选中的文件。 3. 创建Audio元素,将src设置为文件的URL。 4. 监听loadedmetadata事件,当元数据加载完成后,检查duration。如果仍不可用,采用引用[3]中的方法,循环检查设置currentTime直到duration有效。 5. 显示获取到的duration。 需要注意的是,直接使用loadedmetadata事件可能在某些浏览器或情况下无法立即获取duration,所以需要备选方案,比如循环检查或设置currentTime。 现在需要将这些步骤转化为代码,处理可能的异步问题。例如,使用async/await结合setTimeout来等待duration的有效值。同时,引用[2]中的例子使用了requestAnimationFrame或setTimeout,所以代码中可以采用类似的方法。 最后,将代码整合,确保在用户上传文件后,正确显示时长。同时,处理可能出现的错误,比如文件类型不正确或无法读取的情况。 可能还需要考虑兼容性问题,比如使用URL.createObjectURL来处理文件引用,在完成后释放内存。此外,界面部分需要显示上传状态结果,比如时长显示的位置。 总结,示例代码应包括HTML的input,JavaScript的事件处理,Audio对象的创建,异步获取duration的逻辑,以及结果展示。参考引用中的方法,确保代码可靠性兼容性。</think>### 分步实现文件上传获取音频时长 #### 步骤1: 创建文件上传组件 ```html <input type="file" id="audioFile" accept="audio/*"> <div id="durationDisplay"></div> ``` #### 步骤2: 监听文件上传事件 ```javascript document.getElementById('audioFile').addEventListener('change', function(e) { const file = e.target.files[0]; if (!file) return; getAudioDuration(file).then(duration => { document.getElementById('durationDisplay').textContent = `音频时长: ${Math.round(duration)}秒`; }).catch(error => { console.error("获取失败:", error); }); }); ``` #### 步骤3: 核心函数实现 ```javascript function getAudioDuration(file) { return new Promise((resolve, reject) => { // 创建临时音频对象 const audio = new Audio(); audio.src = URL.createObjectURL(file); // 方法1: 直接监听元数据加载 audio.addEventListener('loadedmetadata', () => { if (!isNaN(audio.duration)) { URL.revokeObjectURL(audio.src); // 释放内存 resolve(audio.duration); } }); // 方法2: 兼容性处理(针对某些浏览器) const checkDuration = async () => { try { while (isNaN(audio.duration) || audio.duration === Infinity) { await new Promise(resolve => setTimeout(resolve, 200)); audio.currentTime = 10000000 * Math.random(); // 触发时长计算[^3] } resolve(audio.duration); } catch (error) { reject(error); } finally { URL.revokeObjectURL(audio.src); } }; // 双重检测机制 setTimeout(() => { if (isNaN(audio.duration)) checkDuration(); }, 1000); }); } ``` #### 技术要点说明 1. **对象URL处理** 通过`URL.createObjectURL()`创建临时引用链接,使用后需调用`revokeObjectURL()`释放内存[^3] 2. **异步检测机制** 采用`setTimeout``currentTime`赋值操作强制浏览器解析音频文件,解决某些浏览器无法立即获取时长的问题[^2][^3] 3. **双重验证模式** 同时使用`loadedmetadata`事件监听主动轮询检测,确保最大兼容性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值