title: How to save WAV Blob to MongoDB, retrieve and serve correctly with Node?
body: <p>I've found many posts dealing with saving binary files using the Mongoose Buffer SchemaType. However, most of them deal with image files, and I haven't been able to get them to work with a WAV audio file.</p>
  
  <p>I'm using Recorder.js to save audio recordings from the built-in microphone. I use Recorder.js' exportWAV function to get a BLOB from the finished recording, then read the blob with FileReader and send it to the Node/Express backend where it is then saved to the DB. I've checked using the Mongo CLI and there is data being saved to the relevant field (starting with BinData(0,"UklGR.lotsofdatahere..="). When I try to get the recording by sentence id, the server responds with an appropriately-MIME-typed .wav file that is unplayable.</p>
  
  <p>It seems that I'm missing something in the way that the files are encoded and decoded for storage in MongoDB. When reading the blob spit out by Recorder.js, it looks like it's already base64 encoded. So that's why I tried loading it as a base64 Buffer before saving to Mongo, and then decoding from a base64 buffer on output. What am I missing here? How can I fix these encoding issues? Thanks!</p>
  
  <p>Note: I don't necessarily need GridFS because these files are well under 16MB. Although, if it's a lot faster to stream files from GridFS, maybe I should switch to that solution. However, I'd like to figure out what's wrong with this approach first. </p>
  
  <p>Here's the relevant code from the Angular frontend:
  </p>
  
  <pre class="lang-js prettyprint-override"><code>$scope.start = function() {
    $scope.rec.record();
  }
  
  $scope.export = function() {
    $scope.rec.stop();
  
    $scope.rec.exportWAV(function blobCallback(blob) {
      $scope.rec.clear();
  
      var reader = new FileReader();
      reader.onload = function(event) {
        $.ajax({
          type: 'POST',
          url: '/saveRecording',
          data: {
            audio: event.target.result,
            text: $scope.text,
            timestamp: new Date()
          }
        }).done(function(data) {
          console.log(data);
        });
      }
      reader.readAsDataURL(blob);
    });
  }
  </code></pre>
  
  <p>The Express routes:</p>
  
  <pre class="lang-js prettyprint-override"><code>router.post('/saveRecording', function(request, response, next) {
    var sentence = new Sentence();
    sentence.audio = new Buffer(request.body.audio, 'base64');
    sentence.timestamp = request.body.timestamp;
    sentence.text = request.body.text;
  
  
    // Save sentence to DB with Mongoose
    sentence.save(function(error, sentence) {
      if (error) {
        return next(error);
      }
  
      // If no error, send added sentence back to the client.
      response.json(sentence);
    });
  });
  
  router.get('/getRecording/:sentenceId', function(request, response, next) {
    Sentence.findById(request.params.sentenceId,
      function dbCallback (error, sentence) {
        if (error) {
          return next(error);
        }
        if (!sentence) {
          return next(new Error('Can\'t find sentence'));
        }
  
        var base64Audio = new Buffer(sentence.audio, 'base64');
        response.writeHead(200, {
          'Content-Type': 'audio/x-wav',
          'Content-Length': base64Audio.length
        });
  
        response.write(base64Audio);
        response.end();
      });
  });
  </code></pre>
  
  <p>The Mongoose Schema for Sentences:</p>
  
  <pre class="lang-js prettyprint-override"><code>var SentenceSchema = new mongoose.Schema({
    text: String,
    audio: Buffer,
    timestamp: Date
  });
  </code></pre>