我正在使用 FastAPI 创建一个 API,用于从移动应用程序接收小音频文件。在此 API 中,我对信号进行处理,并且能够在对声音进行分类后返回响应。最终目标是将分类发送回用户。
这是我到目前为止正在做的事情:
@app.post("/predict")
def predict(file: UploadFile = File(...)): # Upload the wav audio sent from the mobile app user
with open(name_file, "wb") as buffer:
shutil.copyfileobj(file.file, buffer) #creating a file with the received audio data
...
prev= test.my_classification_module(name_file) #some processing and the goal response in PREV variable
在
my_classification_module()
,我有这个:
X, sr = librosa.load(sound_file)
我想避免创建一个用
librosa
进行分类的文件。我想使用临时文件来执行此操作,而不会不必要地使用内存,并避免多个用户使用应用程序时文件重叠。
如果您的函数支持 file-like 对象,您可以使用
.file
的 UploadFile
属性,例如 file.file
(这是一个 SpooledTemporaryFile
实例),或者如果您的函数需要该文件在 bytes
格式中,使用 .read()
async 方法(请参阅 文档)。如果您希望保留使用 def
而不是 async def
定义的路线(有关 def
与 async def
的更多信息,请查看 this answer),您可以使用
的
.read()
方法直接类文件对象,例如file.file.read()
。
File contains data in an unknown format
错误确保音频文件未损坏。比方说,如果您保存它并使用媒体播放器打开它,声音文件会播放吗?
确保您安装了最新版本的
librosa
模块。
尝试安装
ffmpeg
并将其添加到系统路径,如建议这里。
如此处和中所述 documentation,
librosa.load()
可以采用 file-like 对象作为文件路径的替代 - 因此,使用 file.file
或 file.file._file
通常应该没问题(根据 documentation, _file
属性是 io.BytesIO
或 io.TextIOWrapper
对象...)。
但是,正如文档here和here以及github讨论中所述,您还可以使用soundfile模块从file-like对象读取音频。示例:
import soundfile as sf
data, samplerate = sf.read(file.file)
您还可以将上传文件的文件
contents
写入BytesIO流,然后将其传递给sf.read()
或librosa.load()
:
from io import BytesIO
contents = file.file.read()
buffer = BytesIO(contents)
data, samplerate = librosa.load(buffer) # ussing librosa module
#data, samplerate = sf.read(buffer) # using soundfile module
buffer.close()
另一种选择是将文件
contents
保存到NamedTemporaryFile
,它“在文件系统中具有可见的名称”,“可用于打开文件”。完成后,您可以使用 remove()
或 unlink()
方法手动删除它。
from FastAPI import HTTPException
from tempfile import NamedTemporaryFile
import os
contents = file.file.read()
temp = NamedTemporaryFile(delete=False)
try:
with temp as f:
f.write(contents);
data, samplerate = librosa.load(temp.name) # ussing librosa module
#data, samplerate = sf.read(temp.name) # using soundfile module
except Exception:
raise HTTPException(status_code=500, detail='Something went wrong')
finally:
#temp.close() # the `with` statement above takes care of closing the file
os.remove(temp.name)