Je suis en train de mettre en œuvre une application Web et je veux que les utilisateurs enregistrent de l'audio, puis je veux un bouton d'envoi pour POST le fichier mp3 enregistré sur le serveur.
La route principale de mon serveur (Flask) '/'
attend la demande POST:
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == "GET":
return render_template('index.html', request="GET")
else:
print request.files
print request.form
print request.form['file']
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename):
handle_file(file)
return render_template('index.html', request="POST")
Voici mon code JS:
Il y a deux problèmes principaux ici:
1) Lorsque je télécharge le fichier mp3 après l'enregistrement, il ne peut pas être ouvert par un lecteur multimédia. Il semble que je fasse quelque chose de mal en enregistrant simplement l'audio.
2) Lorsque je print request.form
sur mon serveur après avoir reçu la demande POST, je ne reçois que ceci:
ImmutableMultiDict([('file', u'')])
Et print request.form['file']
renvoie une ligne vide.
Pourquoi cela arrive-t-il? Quelque chose ne va pas avec la demande POST.
Enfin, je veux pouvoir décoder la chaîne que je publie pour la reconvertir au format mp3. Comment je fais ça?
Note : Rien de tout cela ne doit rester le même. La tâche consiste à enregistrer de l’audio puis à le POST sur le serveur. S'il existe un moyen plus efficace de le faire, tous les conseils sont les bienvenus. De plus, je me moque de savoir si le fichier sera au format wav ou mp3.
Remarque: cette réponse ne traite que les implémentations actuelles à la fois de chrome et de Firefox. Tout cela est sujet à changement à tout moment.
Je ne sais pas si quelque chose ne va pas dans votre code côté serveur, mais n'envoyez pas de données binaires sous forme de chaîne. Utilisez plutôt un FormData pour l'envoyer en plusieurs parties (vous gagnerez 30% de données + intégrité).
En outre, il semble que dans votre code MediaRecorder, vous finalisiez le fichier à chaque événement dataavailable
. Ce n'est généralement pas ce que vous voulez.
Actuellement, aucun navigateur ne prend en charge l’enregistrement natif au format mp3.
var mimes = ['mpeg', 'mpeg3', 'x-mpeg3', 'mp3', 'x-mpeg']
console.log(mimes.some(m=>MediaRecorder.isTypeSupported('audio/'+m)));
Donc, si vous voulez utiliser MediaRecorder, vous devrez vous adapter avec le codec opus
, encapsulé soit dans webm
pour chrome, ou ogg
pour FF:
var enc = ['ogg', 'webm'];
var mime = "";
enc.forEach(e => {
if (!mime && MediaRecorder.isTypeSupported(`audio/${e};codecs="opus"`)) {
mime = `audio/${e};codecs="opus"`;
}
});
console.log(mime);
Alors maintenant que nous avons le bon type MimeType, nous pouvons simplement dire au navigateur de l’utiliser:
var enc = ['ogg', 'webm'];
var extension = "",
mime = '';
enc.forEach(e => !extension &&
(mime = `audio/${e};codecs="opus"`) &&
MediaRecorder.isTypeSupported(mime) &&
(extension = e));
navigator.mediaDevices.getUserMedia({
audio: true
})
.then(stream => {
const chunks = [];
const rec = new MediaRecorder(stream, {
mimeType: mime // use the mimeType we've found
});
// this is not where we build the file, but where we store the chunks
rec.ondataavailable = e => chunks.Push(e.data);
rec.onstop = e => {
// stop our gUM stream
stream.getTracks().forEach(t => t.stop());
// NOW create the file
let blob = new Blob(chunks, {
type: mime
});
// we could now send this blob :
// let form = new FormData();
// form.append('file', blob, 'filename.'+extension;
// ... declare xhr
// xhr.send(form);
// but we'll just fetch it for the demo :
let url = URL.createObjectURL(blob);
let au = new Audio(url);
au.controls = true;
document.body.appendChild(au);
au.play();
// and create an downloadable link from it :
let a = document.createElement('a');
a.href = url;
a.download = 'filename.' + extension;
a.innerHTML = 'download';
document.body.appendChild(a);
};
rec.start();
setTimeout(() => rec.stop(), 3000);
});
Ou nous pourrions aussi laisser le navigateur tout faire par défaut. Ce ne serait un problème que pour l'extension de fichier ...
Désormais, si vous préférez wav
à opus
, vous pouvez laisser le MediaRecorder s'en aller, utiliser simplement WebAudioAPI, lui transmettre votre flux gUM et enregistrer les données à partir de là. Ici, je vais utiliser la bibliothèque recorder.js pour plus de simplicité.
navigator.mediaDevices.getUserMedia({
audio: true
})
.then(stream => {
const aCtx = new AudioContext();
const streamSource = aCtx.createMediaStreamSource(stream);
var rec = new Recorder(streamSource);
rec.record();
setTimeout(() => {
stream.getTracks().forEach(t => t.stop());
rec.stop()
rec.exportWAV((blob) => {
// now we could send this blob with an FormData too
const url = URL.createObjectURL(blob);
let au = new Audio(url);
au.controls = true;
document.body.appendChild(au);
au.play();
let a = document.createElement('a');
a.href = url;
a.innerHTML = 'download';
a.download = 'filename.wav';
document.body.appendChild(a);
});
}, 3000);
})
<script src="https://rawgit.com/mattdiamond/Recorderjs/master/dist/recorder.js"></script>
Et si vous voulez vraiment un mp3, je suppose que vous pouvez utiliser l’une des bibliothèques de javascript lame disponibles sur le Web.
Essayez de convertir le blob audio en Base64 et envoyez la chaîne base64 sur le serveur.
function submit(blob) {
var reader = new window.FileReader();
reader.readAsDataURL(blob);
reader.onloadend = function() {
var fd = new FormData();
base64data = reader.result;
fd.append('file', base64data, 'audio.mp3');
$.ajax({
type: 'POST',
url: '/',
data: fd,
cache: false,
processData: false,
contentType: false,
enctype: 'multipart/form-data'
}).done(function(data) {
console.log(data);
});
}
}
Maintenant, convertissez la chaîne base64 en un flux binaire sur votre serveur.
Pour plus d’informations sur le décodage de Base64 en python, consultez ce post. Décodage de données Python base64