Воспроизведение аудио из большого двоичного объекта в React

#reactjs #audio #html5-audio

#reactjs #Аудио #html5-аудио

Вопрос:

Я получаю некоторое аудио из серверной части (в формате .wav) и хотел бы воспроизвести его во интерфейсе react.

В старой реализации использовались файлы в общей папке и такой тег:

 <audio ref={audioPlayer} src={new Blob(output.data)} preload="metadata" onEnded={onEnded} onLoadedMetadata={onLoadedMetadata}/>
 

Как я могу использовать двоичные данные из моего запроса вместо источника здесь, или есть какой-либо другой простой способ воспроизведения аудиофайла из памяти?

Ответ №1:

Вы можете создать URL-адрес объекта из двоичных данных в формате, подобном blob, для источника аудиоэлемента.

Вот прокомментированный пример, включающий в себя удобный хук:

 <div id="root"></div><script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script><script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script><script src="https://unpkg.com/@babel/standalone@7.16.4/babel.min.js"></script>
<script type="text/babel" data-type="module" data-presets="react">

const {useEffect, useMemo, useState} = React;

/**
 * This is just for the demo.
 * You seem to already have the binary data for the blob.
 */
function useBlob () {
  const [blob, setBlob] = useState();
  const [error, setError] = useState();

  useEffect(() => {
    (async () => {
      try {
        // A random doorbell audio sample I found on GitHub
        const url = 'https://raw.githubusercontent.com/prof3ssorSt3v3/media-sample-files/65dbf140bdf0e66e8373fccff580ac0ba043f9c4/doorbell.mp3';
        const response = await fetch(url);
        if (!response.ok) throw new Error(`Response not OK (${response.status})`);
        setBlob(await response.blob());
      }
      catch (ex) {
        setError(ex instanceof Error ? ex : new Error(String(ex)));
      }
    })();
  }, []);

  return {blob, error};
}

/**
 * Get an object URL for the current blob. Will revoke old URL if blob changes.
 * https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
 */
function useObjectUrl (blob) {
  const url = useMemo(() => URL.createObjectURL(blob), [blob]);
  useEffect(() => () => URL.revokeObjectURL(url), [blob]);
  return url;
}

// Use the hook and render the audio element
function AudioPlayer ({blob}) {
  const src = useObjectUrl(blob);
  return <audio controls {...{src}} />;
}

function Example () {
  const {blob, error} = useBlob();
  return (
    <div>
      <h2>Audio player using binary data</h2>
      {
        blob ? <AudioPlayer {...{blob}} />
          : error ? <div>There was an error fetching the audio file: {String(error)}</div>
          : <div>Loading audio...</div>
      }
    </div>
  );
}

ReactDOM.render(<Example />, document.getElementById('root'));

</script> 

Комментарии:

1. Это отличное начало и делает именно то, что я ищу! Однако я чувствую, что с моим Blob что-то не так. Как я могу убедиться, что он правильно отформатирован, чтобы его можно было интерпретировать как аудио? На данный момент я установил тип: «audi / wav», но при открытии файла через ObjectURL он пуст.

2. @masus04 В тексте вашего вопроса указано, что у вас уже есть большой двоичный объект, так что это вопрос, отличный от того, который вы задали. Если вы хотите задать другой вопрос по SO и дать ссылку на него в комментарии здесь, я был бы рад взглянуть. Если этот ответ отвечает на ваш вопрос о том, как использовать blob-объект с аудиоэлементом в React, не стесняйтесь пометить его как таковой.

3. Я просто пытался выяснить, был ли у меня уже есть большой двоичный объект правильным. Если у вас есть какое-либо представление об этом, это было бы оценено. Во всяком случае, теперь это работает для меня, большое вам спасибо за ваш ответ!

4. Рад, что у вас все получилось @masus04

Ответ №2:

Спасибо @jsejcksn за подробный ответ!

Краткая версия будет выглядеть следующим образом:

  1. Убедитесь, что вы правильно извлекаете большой двоичный объект, например, указав {responseType: ‘blob’} с помощью axios
  2. Оберните большой двоичный объект в ObjectURL
     url = URL.createObjectURL(blob)
     
  3. Передайте URL-адрес в тег audio как src
     <audio src={url} />
     
  4. Если URL-адрес больше не требуется, освободите ресурсы следующим образом:
     URL.revokeObjectURL(url)
     

Редактировать: добавлен комментарий @jsejcksn

Комментарии:

1. Каждый раз, когда вы создаете URL-адрес объекта, он создает копию большого двоичного объекта в памяти, поэтому обязательно используйте URL.revokeObjectURL(yourObjectURL) , когда закончите с URL-адресом, иначе это утечка памяти.