You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

289 lines
10 KiB
HTML

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width,initial-scale=1"
/>
<title>录音与播放功能</title>
<style>
body {
font-family: 'Segoe UI', sans-serif;
background-color: #f5f7fa;
margin: 0;
padding: 20px;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.container {
background-color: #fff;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 30px;
width: 100%;
max-width: 500px;
text-align: center;
}
h1 {
color: #2c3e50;
margin-bottom: 20px;
}
.controls {
margin-bottom: 20px;
}
button {
background-color: #3498db;
color: #fff;
border: none;
padding: 12px 24px;
font-size: 16px;
border-radius: 8px;
cursor: pointer;
margin: 0 8px;
transition: background-color 0.3s;
}
button:disabled {
background-color: #bdc3c7;
cursor: not-allowed;
}
button:hover {
background-color: #2980b9;
}
.timer {
font-size: 18px;
color: #e74c3c;
margin: 10px 0;
font-weight: 700;
}
.file-info {
margin-top: 10px;
color: #7f8c8d;
font-size: 14px;
font-weight: 400;
}
.upload-status {
margin-top: 10px;
font-size: 14px;
font-weight: 400;
}
.audio-player {
margin-top: 20px;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
audio {
width: 100%;
height: 50px;
border: none;
background-color: #f0f0f0;
}
.status {
margin-top: 10px;
color: #7f8c8d;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h1>录音与播放</h1>
<div class="controls">
<button id="startBtn">开始录音</button>
<button
id="stopBtn"
disabled="disabled"
>
结束录音
</button>
<button
id="uploadBtn"
disabled="disabled"
>
上传到服务器
</button>
</div>
<div
class="timer"
id="timer"
>
00:00
</div>
<div
class="file-info"
id="fileInfo"
>
文件大小0 KB
</div>
<div
class="upload-status"
id="uploadStatus"
></div>
<div
class="status"
id="status"
>
准备就绪
</div>
<div class="audio-player">
<audio
id="audioPlayer"
controls
></audio>
</div>
</div>
<script>
// 获取 DOM 元素
const startBtn = document.getElementById('startBtn');
const stopBtn = document.getElementById('stopBtn');
const uploadBtn = document.getElementById('uploadBtn');
const timerEl = document.getElementById('timer');
const fileInfoEl = document.getElementById('fileInfo');
const uploadStatusEl = document.getElementById('uploadStatus');
const statusEl = document.getElementById('status');
const audioPlayer = document.getElementById('audioPlayer');
let mediaRecorder;
let audioChunks = [];
let recordingTime = 0;
const MAX_RECORDING_TIME = 60; // 最大60秒
const AUTO_STOP_TIME = 10; // 超过10秒自动停止
let timerInterval;
let audioBlob = null; // 存储录音结果,用于上传
// 模拟上传接口地址(可替换为真实后端地址)
const UPLOAD_URL = 'https://httpbin.org/post'; // 测试用接口,可上传文件
// 开始录音
startBtn.addEventListener('click', async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder = new MediaRecorder(stream, {
mimeType: 'audio/webm;codecs=opus',
});
audioChunks = [];
mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) {
audioChunks.push(event.data);
}
};
mediaRecorder.onstop = () => {
// 生成 Blob
audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
const audioUrl = URL.createObjectURL(audioBlob);
audioPlayer.src = audioUrl;
// 计算文件大小KB
const fileSizeKB = (audioBlob.size / 1024).toFixed(2);
fileInfoEl.textContent = `文件大小:${fileSizeKB} KB`;
statusEl.textContent = `录音完成,大小:${fileSizeKB} KB可播放`;
uploadBtn.disabled = false; // 启用上传按钮
stopBtn.disabled = true;
startBtn.disabled = false;
clearInterval(timerInterval);
timerEl.textContent = '00:00';
};
mediaRecorder.start();
startBtn.disabled = true;
stopBtn.disabled = false;
statusEl.textContent = '正在录音...';
// 启动计时器
recordingTime = 0;
timerInterval = setInterval(() => {
recordingTime++;
timerEl.textContent = formatTime(recordingTime);
// ✅ 超过10秒自动停止录音
if (recordingTime >= AUTO_STOP_TIME) {
statusEl.textContent = `⚠️ 已超过 ${AUTO_STOP_TIME} 秒,自动停止录音`;
stopRecording();
}
// 也检查最大时长(防止意外)
if (recordingTime >= MAX_RECORDING_TIME) {
statusEl.textContent = `⚠️ 已达到最大录音时长 ${MAX_RECORDING_TIME} 秒,自动停止`;
stopRecording();
}
}, 1000);
} catch (err) {
alert('无法访问麦克风:' + err.message);
console.error('录音失败:', err);
}
});
// 结束录音
stopBtn.addEventListener('click', stopRecording);
function stopRecording() {
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
mediaRecorder.stop();
}
if (mediaRecorder && mediaRecorder.stream) {
mediaRecorder.stream.getTracks().forEach((track) => track.stop());
}
}
// 上传录音文件
uploadBtn.addEventListener('click', async () => {
if (!audioBlob) {
alert('没有可上传的录音文件');
return;
}
uploadStatusEl.textContent = '正在上传...';
uploadBtn.disabled = true;
const formData = new FormData();
formData.append('audio', audioBlob, 'recording.webm'); // 文件名自定义
try {
const response = await fetch(UPLOAD_URL, {
method: 'POST',
body: formData,
});
if (response.ok) {
const result = await response.json();
uploadStatusEl.textContent = '✅ 上传成功!';
console.log('上传成功:', result);
} else {
throw new Error(`HTTP ${response.status}`);
}
} catch (err) {
uploadStatusEl.textContent = '❌ 上传失败:' + err.message;
console.error('上传失败:', err);
} finally {
uploadBtn.disabled = false;
}
});
// 格式化时间:秒转为 mm:ss
function formatTime(seconds) {
const mins = Math.floor(seconds / 60)
.toString()
.padStart(2, '0');
const secs = (seconds % 60).toString().padStart(2, '0');
return `${mins}:${secs}`;
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', () => {
stopBtn.disabled = true;
uploadBtn.disabled = true;
statusEl.textContent = '点击“开始录音”开始';
});
</script>
</body>
</html>