'How to process files in FastAPI from multiple clients without saving the files to disk
I am using FastAPI to create an API that receives small audio files from a mobile app. In this API I do processing of the signal and I am able to return a response after classifying that sound. The final goal is to send the classification back to the user.
Here's what I am doing so far:
@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
In my_classification_module(), I have this :
X, sr = librosa.load(sound_file)
I want to avoid creating a file to be classified with librosa. I would like to do this with a temporary file, without using memory unecessarily and to avoid the overlap of files when the app is used by multiple users.
Solution 1:[1]
If your function supports a file-like object, you could use the .file attribute of UploadFile, e.g., file.file (which is a SpooledTemporaryFile instance), or if your function requires the file in bytes format, use the .read() async method (see the documentation). If you wish to keep your route defined with def instead of async def (have a look at this answer for more info on def vs async def), you could use the .read() method of the file-like object directly, e.g., file.file.read().
Update - How to resolve File contains data in an unknown format error
Make sure the audio file is not corrupted. If, let's say, you saved it and opened it with a media player, would the sound file play?
Make sure you have the latest version of
librosamodule installed.Try installing
ffmpegand adding it to the system path, as suggested here.As described here and in the documentation,
librosa.load()can take a file-like object as an alternative to a file path - thus, usingfile.fileorfile.file._fileshould normally be fine (as per the documentation,_fileattribute is either anio.BytesIOorio.TextIOWrapperobject...).However, as described in the documentation here and here, as well as in this github discussion, you could also use the soundfile module to read audio from file-like objects. Example:
import soundfile as sf data, samplerate = sf.read(file.file)You could also write the file
contentsof the uploaded file to a BytesIO stream, and then pass it to eithersf.read()orlibrosa.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()Another option would be to save the file
contentsto aNamedTemporaryFile, which "has a visible name in the file system" that "can be used to open the file". Once you are done with it, you can manually delete it using theremove()orunlink()method.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 finally: temp.close() os.unlink(temp.name)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
