在 React 中播放来自 Blob 的音频
Play audio from Blob in React
我从后端接收到一些音频(.wav 格式)并想在 React 前端播放它。
旧的实现使用 public 文件夹中的文件和这样的标签:
<audio ref={audioPlayer} src={new Blob(output.data)} preload="metadata" onEnded={onEnded} onLoadedMetadata={onLoadedMetadata}/>
我如何使用请求中的二进制数据而不是此处的源,或者是否有任何其他简单的方法从内存中播放音频文件?
您可以为音频元素源创建类似 object URL from binary data in a 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>
感谢@jsejcksn 的详尽回答!
简短版本如下:
确保正确检索 Blob,例如通过使用 axios
指定 {responseType: 'blob'}
将 Blob 包装成 ObjectURL
url = URL.createObjectURL(blob)
将 url 作为 src
传递给音频标签
<audio src={url} />
如果url不再需要,释放资源如下:
URL.revokeObjectURL(url)
编辑:添加了@jsejcksn 的评论
我从后端接收到一些音频(.wav 格式)并想在 React 前端播放它。
旧的实现使用 public 文件夹中的文件和这样的标签:
<audio ref={audioPlayer} src={new Blob(output.data)} preload="metadata" onEnded={onEnded} onLoadedMetadata={onLoadedMetadata}/>
我如何使用请求中的二进制数据而不是此处的源,或者是否有任何其他简单的方法从内存中播放音频文件?
您可以为音频元素源创建类似 object URL from binary data in a 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>
感谢@jsejcksn 的详尽回答!
简短版本如下:
确保正确检索 Blob,例如通过使用 axios
指定 {responseType: 'blob'}将 Blob 包装成 ObjectURL
url = URL.createObjectURL(blob)
将 url 作为 src
传递给音频标签<audio src={url} />
如果url不再需要,释放资源如下:
URL.revokeObjectURL(url)
编辑:添加了@jsejcksn 的评论