Fix voiceprint upload temp file collisions

This commit is contained in:
Xulilong 2026-07-16 10:39:10 +08:00
parent 83d863daf7
commit 41baca051c
2 changed files with 83 additions and 27 deletions

View File

@ -5,6 +5,7 @@ import os
import tempfile
import mimetypes
import subprocess
import uuid
from pathlib import Path
from datetime import datetime
@ -95,6 +96,41 @@ def convert_to_wav(input_path):
raise Exception(f"音频转换错误: {str(e)}")
def _safe_audio_extension(uploaded_file):
filename = secure_filename(uploaded_file.filename or "")
suffix = Path(filename).suffix.lower()
if suffix:
return suffix
content_type = (uploaded_file.content_type or "").lower()
if "webm" in content_type:
return ".webm"
if "wav" in content_type or "wave" in content_type:
return ".wav"
if "mpeg" in content_type or "mp3" in content_type:
return ".mp3"
if "ogg" in content_type:
return ".ogg"
if "mp4" in content_type or "m4a" in content_type:
return ".m4a"
return ".webm"
def _make_temp_audio_path(upload_dir, uploaded_file):
ext = _safe_audio_extension(uploaded_file)
filename = secure_filename(
f"temp_{datetime.now().strftime('%Y%m%d%H%M%S%f')}_{uuid.uuid4().hex}{ext}"
)
return os.path.join(upload_dir, filename)
def _make_final_voiceprint_path(upload_dir, name):
filename = secure_filename(
f"{name}_{datetime.now().strftime('%Y%m%d%H%M%S%f')}_{uuid.uuid4().hex[:8]}.wav"
)
return os.path.join(upload_dir, filename)
@voiceprint_bp.route('/api/voiceprints', methods=['GET'])
@login_required
def get_voiceprints():
@ -141,8 +177,7 @@ def create_voiceprint():
os.makedirs(voiceprint_dir, exist_ok=True)
# 保存原始音频文件到临时位置
temp_filename = secure_filename(f"temp_{datetime.now().strftime('%Y%m%d%H%M%S')}_{file.filename}")
temp_filepath = os.path.join(voiceprint_dir, temp_filename)
temp_filepath = _make_temp_audio_path(voiceprint_dir, file)
file.save(temp_filepath)
original_file_path = temp_filepath
@ -150,8 +185,7 @@ def create_voiceprint():
converted_file_path = convert_to_wav(temp_filepath)
# 保存转换后的文件
final_filename = secure_filename(f"{name}_{datetime.now().strftime('%Y%m%d%H%M%S')}.wav")
final_filepath = os.path.join(voiceprint_dir, final_filename)
final_filepath = _make_final_voiceprint_path(voiceprint_dir, name)
# 如果转换后的文件不是原始文件,则移动
if converted_file_path != temp_filepath:
@ -242,8 +276,7 @@ def update_voiceprint(voiceprint_id):
os.makedirs(voiceprint_dir, exist_ok=True)
# 保存原始音频文件到临时位置
temp_filename = secure_filename(f"temp_{datetime.now().strftime('%Y%m%d%H%M%S')}_{file.filename}")
temp_filepath = os.path.join(voiceprint_dir, temp_filename)
temp_filepath = _make_temp_audio_path(voiceprint_dir, file)
file.save(temp_filepath)
original_file_path = temp_filepath
@ -251,8 +284,7 @@ def update_voiceprint(voiceprint_id):
converted_file_path = convert_to_wav(temp_filepath)
# 保存转换后的文件
final_filename = secure_filename(f"{name}_{datetime.now().strftime('%Y%m%d%H%M%S')}.wav")
final_filepath = os.path.join(voiceprint_dir, final_filename)
final_filepath = _make_final_voiceprint_path(voiceprint_dir, name)
# 如果转换后的文件不是原始文件,则移动
if converted_file_path != temp_filepath:

View File

@ -1109,7 +1109,7 @@
style="background-color: #fff;border-radius: 4px !important;"></textarea> -->
<div class="modal-footer">
<button class="btn" onclick="closeModal()">取消</button>
<button class="btn btn-primary" onclick="saveVoice()"
<button id="saveVoiceBtn" class="btn btn-primary" onclick="saveVoice()"
style="color: #fff;">保存</button>
</div>
</div>
@ -1794,6 +1794,7 @@
let mediaRecorder;
let audioChunks = [];
let isRecording = false;
let isSavingVoice = false;
let modetypes = "add"
let modechooseIndex = 0
// 打开添加/编辑弹框
@ -1852,6 +1853,9 @@ async function synchronize() {
}
// 保存声纹信息
async function saveVoice() {
if (isSavingVoice) {
return;
}
const name = document.getElementById('voiceName').value.trim();
// const desc
// = document.getElementById('voiceDesc').value.trim();
@ -1881,7 +1885,15 @@ console.log("audioBlob:", audioBlob);
const formData = new FormData();
formData.append("name", name);
formData.append("voice_data", audioBlob);
const uploadFileName = fileInput && fileInput.name ? fileInput.name : `voiceprint_${Date.now()}.webm`;
formData.append("voice_data", audioBlob, uploadFileName);
isSavingVoice = true;
const saveBtn = document.getElementById('saveVoiceBtn');
if (saveBtn) {
saveBtn.disabled = true;
saveBtn.dataset.originalText = saveBtn.textContent;
saveBtn.textContent = '保存中...';
}
if (modetypes == 'add') {
try {
@ -1903,6 +1915,12 @@ console.log("audioBlob:", audioBlob);
// alert("上传失败:" + err.message);
showMessage(`${err.message}`, 'error');
} finally {
isSavingVoice = false;
if (saveBtn) {
saveBtn.disabled = false;
saveBtn.textContent = saveBtn.dataset.originalText || '保存';
}
}
} else {
try {
@ -1927,6 +1945,12 @@ console.log("audioBlob:", audioBlob);
// console.error("上传出错:", err);
// alert("上传失败:" + err.message);
showMessage(`${err.message}`, 'error');
} finally {
isSavingVoice = false;
if (saveBtn) {
saveBtn.disabled = false;
saveBtn.textContent = saveBtn.dataset.originalText || '保存';
}
}
}
}