我有这样的实现,后端传输音频文件。我想要一种能够快速加载和播放音频文件的方法:一切都按预期工作,但似乎存在不稳定的行为,需要很长时间才能加载到我的网页上:
我是 React Js 新手。
import React, { useState,useEffect, useRef } from 'react';
import { Icon } from '@iconify/react';
import { podcastIcons } from '@/db/data';
import axios from 'axios';
import WaveSurfer from 'wavesurfer.js';
const PlayFile: React.FC = () => {
const [linkName, setLinkName] = useState<string>(''); // State for link name
const [audioURL, setAudioURL] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
const waveformRef = useRef<HTMLDivElement | null>(null); // Reference to the waveform container
const waveSurfer = useRef<WaveSurfer | null>(null); // Reference to the WaveSurfer instance
const handleSubmit = async (event: React.FormEvent) => {
event.preventDefault();
const formData = new FormData();
formData.append('link_name', linkName);
try {
const response = await axios.post('http://localhost:8000/submitform/', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
const audioBlob = new Blob([response.data], { type: 'audio/mpeg' });
const audioUrl = URL.createObjectURL(audioBlob);
setAudioURL(audioUrl); // Set audio UR
// Initialize Wavesurfer after the audio is set
console.log('checking waveform');
if (waveformRef.current && waveSurfer.current === null) {
waveSurfer.current = WaveSurfer.create({
container: waveformRef.current,
waveColor: '#ddd',
progressColor: '#333',
cursorColor: '#333',
height: 64,
barWidth: 1,
responsive: true,
// backend: 'MediaElement'
});
}
// Load the audio file into Wavesurfer
console.log('after checking ...');
if (waveSurfer.current && audioUrl) {
console.log('Waveform loaded with audio URL:', audioUrl);
waveSurfer.current.load(audioUrl);
}
// } catch (error) {
// console.error('Error generating the podcast:', error);
// }
} catch (error) {
console.error('Error submitting the form', error);
}finally {
setIsLoading(false);
}
};
);
return (
<form onSubmit={handleSubmit} className=" bg-gradient-to-r from-emerald-100 to-lime-200 flex text-black flex-col lg:flex-row lg:space-x-8 p-6 font-sans py-12 px-6">
</div>
{/* Link Name Input Field */}
<div className="mb-6">
<label className="font-semibold mb-2">Document Link</label>
<input
type="Document link"
value={linkName}
onChange={(e) => setLinkName(e.target.value)}
className="border rounded w-full py-2 px-3"
placeholder="Link to Document"
/>
</div>
<button className="bg-black text-white py-3 px-8 rounded w-full">
Create podcast
</button>
{/* Show the audio player and waveform if the audio URL is available */}
{audioURL && (
<div className="mt-4">
<div className="waveform" ref={waveformRef}></div> {/* Waveform container */}
<div className="mt-2 flex gap-4">
<button onClick={() => waveSurfer.current?.playPause()} className="bg-green-500 text-white py-2 px-4 rounded">
{waveSurfer.current?.isPlaying() ? 'Pause' : 'Play'}
</button>
<button onClick={() => waveSurfer.current?.stop()} className="bg-red-500 text-white py-2 px-4 rounded">
Stop
</button>
</div>
</div>
)}
</div>
</form>
);
};
export default PlayFile;
我已经尝试过上面的代码,它有时有效,有时则无效。我正在寻找优化代码并获得以可扩展的方式处理前端音频渲染的实践经验。说明最佳实践的示例将受到高度赞赏。 使用peaks.js、howler和其他针对性能进行优化的类似库的解决方案也受到欢迎。
在 React 应用程序中采用 Howler.js 具有许多优势,可以直接解决您当前实施中面临的挑战:
通过利用 Howler.js,您可以在 React 应用程序中创建可扩展、高效且用户友好的音频播放功能,解决不稳定的加载行为并提高整体性能。
试试这个代码:
如果您稍后决定合并波形可视化等高级功能,您可以将专用库(例如 WaveSurfer.js)与 Howler.js 一起集成,从而保持模块化且高效的架构。
import React, { useState, useRef, useEffect } from 'react';
import axios from 'axios';
import { Howl } from 'howler';
const PlayFileWithHowler = () => {
const [linkName, setLinkName] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [howl, setHowl] = useState(null);
const [isPlaying, setIsPlaying] = useState(false);
const handleSubmit = async (event) => {
event.preventDefault();
setIsLoading(true);
const formData = new FormData();
formData.append('link_name', linkName);
try {
const response = await axios.post('http://localhost:8000/submitform/', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
responseType: 'blob', // Ensure the response is a blob
});
const audioBlob = new Blob([response.data], { type: 'audio/mpeg' });
const audioUrl = URL.createObjectURL(audioBlob);
const newHowl = new Howl({
src: [audioUrl],
html5: true, // Use HTML5 Audio to enable streaming large files
onend: () => setIsPlaying(false),
onplay: () => setIsPlaying(true),
onpause: () => setIsPlaying(false),
onstop: () => setIsPlaying(false),
onloaderror: (id, error) => console.error('Load error:', error),
onplayerror: (id, error) => console.error('Play error:', error),
});
setHowl(newHowl);
} catch (error) {
console.error('Error submitting the form', error);
// Optionally, set an error state here to display to the user
} finally {
setIsLoading(false);
}
};
const handlePlayPause = () => {
if (howl) {
howl.playing() ? howl.pause() : howl.play();
}
};
const handleStop = () => {
if (howl) {
howl.stop();
}
};
// Cleanup Howl on unmount
useEffect(() => {
return () => {
if (howl) {
howl.unload();
}
};
}, [howl]);
return (
<form onSubmit={handleSubmit} className="bg-gradient-to-r from-emerald-100 to-lime-200 flex flex-col lg:flex-row lg:space-x-8 p-6 font-sans py-12 px-6">
{/* Link Name Input Field */}
<div className="mb-6 flex-1">
<label className="font-semibold mb-2 block">Document Link</label>
<input
type="text"
value={linkName}
onChange={(e) => setLinkName(e.target.value)}
className="border rounded w-full py-2 px-3"
placeholder="Link to Document"
required
/>
</div>
<div className="flex items-end">
<button
type="submit"
className="bg-black text-white py-3 px-8 rounded w-full"
disabled={isLoading}
>
{isLoading ? 'Loading...' : 'Create Podcast'}
</button>
</div>
{/* Show the audio controls if Howl is initialized */}
{howl && (
<div className="mt-4 w-full">
<div className="flex gap-4">
<button
type="button"
onClick={handlePlayPause}
className="bg-green-500 text-white py-2 px-4 rounded"
>
{isPlaying ? 'Pause' : 'Play'}
</button>
<button
type="button"
onClick={handleStop}
className="bg-red-500 text-white py-2 px-4 rounded"
>
Stop
</button>
</div>
</div>
)}
</form>
);
};
export default PlayFileWithHowler;