web-dev-qa-db-fra.com

POST Données audio HTML5 sur le serveur

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:

Jsfiddle

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.

11
pavlos163

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: 

violon pour chrome

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é.

violon pour chrome

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.

3
Kaiido

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

0
Nadir Laskar