返回

React 加载 MP3 到 URL:FileReader 和 Blob URL 实践

javascript

React 中加载 MP3 文件至 URL

音频播放功能在 Web 应用中十分常见,其中一个常见需求就是允许用户上传 MP3 文件并通过 URL 进行播放。 开发者经常遇到一个问题,即如何将用户通过文件输入控件选择的 MP3 文件转换为可以直接在 <audio> 标签中使用的 URL。 本文将探讨这个问题并提供解决方案。

挑战与原因分析

当使用 <input type="file"> 上传 MP3 文件时,默认情况下,该文件不是以 URL 的形式提供给 JavaScript 代码的。 FileReader 对象确实可以将文件读取为 DataURL,但是该 URL 需要等待文件加载完成后才能获取。 而示例中的代码在文件尚未加载完成就尝试获取 reader.result 结果,导致获取空字符串。这是主要的问题。 另外, onloadend 事件中的 e.target.result 可以用来构造 audio 对象进行播放。但开发者目标是将这个 URL 用于其他组件中。

解决方案 1:使用 FileReader 正确获取 Data URL

核心思想是使用 onload 事件回调来获取文件的 DataURL 并存储下来,该事件确保读取操作已完成。 修改代码逻辑,确保在读取完成之后再访问读取的结果。

操作步骤:

  1. 修改 readURL 函数。
  2. onload 回调中设置 sourceAux,以便能够正确获取 Data URL

代码示例:

import React, { useState } from 'react';

function AudioUploader() {
    const [sourceAux, setSourceAux] = useState('');
  
    const readURL = (input) => {
        if(input.target.files && input.target.files[0]){
          let reader = new FileReader();
    
          reader.onload = function(e){
              // 使用 useState 来存储 Data URL
              setSourceAux(e.target.result);
              console.log(e.target.result); 
          };
          reader.readAsDataURL(input.target.files[0]);
        }
    };
  
    return (
      <div>
          <input id="auInput" type="file" accept="audio/*" onChange={readURL}/>
        
          {/* 播放器使用sourceAux, 这里仅为示意 */}
           {sourceAux && (
             <audio controls src={sourceAux}>
              Your browser does not support the audio tag.
              </audio>
          )} 
          
      </div>
    );
  }
  
  export default AudioUploader;

上述代码通过useState保存 Data URL,使得可以响应状态变化更新组件,并且提供了简单的 audio 标签演示用法。

解决方案 2:使用 URL.createObjectURL() 创建 Blob URL

另一种更高效的方案是使用 URL.createObjectURL(),它可以基于文件的 Blob 对象创建一个 URL,且不会像 Data URL 那样把整个文件内容编码进字符串中。 这通常对性能更有利。 Blob URL 可以更有效地加载媒体资源,且易于管理和释放资源。

操作步骤:

  1. 使用 URL.createObjectURL() 创建 Blob URL。
  2. 在组件卸载时使用 URL.revokeObjectURL() 释放 Blob URL 的内存。
    这样做是为了防止内存泄漏。

代码示例:

import React, { useState, useEffect } from 'react';

function AudioUploader() {
    const [sourceAux, setSourceAux] = useState('');
    const [blobUrl, setBlobUrl] = useState(null);

  
    const readURL = (input) => {
      if(input.target.files && input.target.files[0]) {
          const file = input.target.files[0];
        // 创建blob URL
          const newBlobUrl = URL.createObjectURL(file);
          setBlobUrl(newBlobUrl)
          setSourceAux(newBlobUrl);
      }
    };


  useEffect(()=>{
        // 清理函数:在组件卸载时,移除blob URL,释放内存
        return () => {
            if (blobUrl) {
              URL.revokeObjectURL(blobUrl);
              console.log('Blob url cleaned:',blobUrl)
            }
          };

    },[blobUrl])
    

    return (
      <div>
          <input id="auInput" type="file" accept="audio/*" onChange={readURL}/>
           
           {/* 播放器使用sourceAux, 这里仅为示意 */}
            {sourceAux && (
                <audio controls src={sourceAux}>
                    Your browser does not support the audio tag.
                 </audio>
            )} 
      </div>
    );
}
export default AudioUploader;

这段代码在 readURL 函数中使用 URL.createObjectURL 生成 Blob URL。并且使用 useEffect 组件在卸载时清除这个 URL,以此来管理内存,是一个较佳实践。

额外安全提示

当允许用户上传音频文件时,需要格外小心。 对于用户上传的文件,验证其文件类型,并进行一定安全检测是合理的安全实践。 请使用可信任的音频播放库。 在前端加载音频文件之前,后端处理是一种更安全的模式,特别是在对用户上传内容有较高安全要求的应用中。 后端在接收上传的音频后,应该检查文件的 MIME 类型并扫描文件是否包含恶意内容。