137

I have a blob data in this structure:

Blob {type: "audio/wav", size: 655404, slice: function}
size: 655404
type: "audio/wav"
__proto__: Blob

It's actually sound data recorded using the recent Chrome getUerMedia() and Recorder.js

How can I upload this blob to the server using jquery's post method? I've tried this without any luck:

   $.post('http://localhost/upload.php', { fname: "test.wav", data: soundBlob }, 
    function(responseText) {
           console.log(responseText);
    });
2
  • 1
    You might want to think about using binaryJS for this. If you are streaming data, that might do the trick. Commented Nov 11, 2012 at 17:18
  • This answer is very detailed as well. stackoverflow.com/a/8758614/1331430 Commented Nov 11, 2012 at 17:22

6 Answers 6

147

You can use the FormData API.

If you're using jquery.ajax, you need to set processData: false and contentType: false.

var fd = new FormData();
fd.append('fname', 'test.wav');
fd.append('data', soundBlob);
$.ajax({
    type: 'POST',
    url: '/upload.php',
    data: fd,
    processData: false,
    contentType: false
}).done(function(data) {
       console.log(data);
});
Sign up to request clarification or add additional context in comments.

8 Comments

Do you know how to do this without AJAX too?
@FullDecent What do you mean? To prompt the user to download a file using the File API? Or to just store the blob content?
To do basically $('input[type=file]').value=blob
Security requirements prevent programmatic setting of file input values: stackoverflow.com/questions/1696877/…
Note that a Blob has a generic filename when sent to the server, unlike a File. But you can speficy the Blob filename in FormData: stackoverflow.com/questions/6664967/…
|
65

2019 Update

This updates the answers with the latest Fetch API and doesn't need jQuery.

Disclaimer: doesn't work on IE, Opera Mini and older browsers. See caniuse.

Basic Fetch

It could be as simple as:

  fetch(`https://example.com/upload.php`, {method:"POST", body:blobData})
                .then(response => console.log(response.text()))

Fetch with Error Handling

After adding error handling, it could look like:

fetch(`https://example.com/upload.php`, {method:"POST", body:blobData})
            .then(response => {
                if (response.ok) return response;
                else throw Error(`Server returned ${response.status}: ${response.statusText}`)
            })
            .then(response => console.log(response.text()))
            .catch(err => {
                alert(err);
            });

PHP Code

This is the server-side code in upload.php.

<?php    
    // gets entire POST body
    $data = file_get_contents('php://input');
    // write the data out to the file
    $fp = fopen("path/to/file", "wb");

    fwrite($fp, $data);
    fclose($fp);
?>

3 Comments

The top answer employs jquery and is more complex than even the error handling version here. It has 126 votes. This, on the other hand has 11 (now 12 votes) and works with js natively and is very short. I wish I had 120 votes to put this solution where it belongs.
@StephenDuffy the answers are 2 years apart
Works well, but might open some security holes. It's a really good training however to understand why so.
24

You actually don't have to use FormData to send a Blob to the server from JavaScript (and a File is also a Blob).

jQuery example:

var file = $('#fileInput').get(0).files.item(0); // instance of File
$.ajax({
  type: 'POST',
  url: 'upload.php',
  data: file,
  contentType: 'application/my-binary-type', // set accordingly
  processData: false
});

Vanilla JavaScript example:

var file = $('#fileInput').get(0).files.item(0); // instance of File
var xhr = new XMLHttpRequest();
xhr.open('POST', '/upload.php', true);
xhr.onload = function(e) { ... };
xhr.send(file);

Granted, if you are replacing a traditional HTML multipart form with an "AJAX" implementation (that is, your back-end consumes multipart form data), you want to use the FormData object as described in another answer.

Source: New Tricks in XMLHttpRequest2 | HTML5 Rocks

Comments

20

I could not get the above example to work with blobs and I wanted to know what exactly is in upload.php. So here you go:

(tested only in Chrome 28.0.1500.95)

// javascript function that uploads a blob to upload.php
function uploadBlob(){
    // create a blob here for testing
    var blob = new Blob(["i am a blob"]);
    //var blob = yourAudioBlobCapturedFromWebAudioAPI;// for example   
    var reader = new FileReader();
    // this function is triggered once a call to readAsDataURL returns
    reader.onload = function(event){
        var fd = new FormData();
        fd.append('fname', 'test.txt');
        fd.append('data', event.target.result);
        $.ajax({
            type: 'POST',
            url: 'upload.php',
            data: fd,
            processData: false,
            contentType: false
        }).done(function(data) {
            // print the output from the upload.php script
            console.log(data);
        });
    };      
    // trigger the read from the reader...
    reader.readAsDataURL(blob);

}

The contents of upload.php:

<?
// pull the raw binary data from the POST array
$data = substr($_POST['data'], strpos($_POST['data'], ",") + 1);
// decode it
$decodedData = base64_decode($data);
// print out the raw data, 
echo ($decodedData);
$filename = "test.txt";
// write the data out to the file
$fp = fopen($filename, 'wb');
fwrite($fp, $decodedData);
fclose($fp);
?>

1 Comment

I'm pretty sure you can change the line data: fd, in the ajax function call to data: blob,.
12

I was able to get @yeeking example to work by not using FormData but using javascript object to transfer the blob. Works with a sound blob created using recorder.js. Tested in Chrome version 32.0.1700.107

function uploadAudio( blob ) {
  var reader = new FileReader();
  reader.onload = function(event){
    var fd = {};
    fd["fname"] = "test.wav";
    fd["data"] = event.target.result;
    $.ajax({
      type: 'POST',
      url: 'upload.php',
      data: fd,
      dataType: 'text'
    }).done(function(data) {
        console.log(data);
    });
  };
  reader.readAsDataURL(blob);
}

Contents of upload.php

<?
// pull the raw binary data from the POST array
$data = substr($_POST['data'], strpos($_POST['data'], ",") + 1);
// decode it
$decodedData = base64_decode($data);
// print out the raw data,
$filename = $_POST['fname'];
echo $filename;
// write the data out to the file
$fp = fopen($filename, 'wb');
fwrite($fp, $decodedData);
fclose($fp);
?>

1 Comment

Careful in the php file - if you allow the HTTP client to set the filename, they could use it to upload malicious content into a file and directory of their choosing. (so long as Apache can write there)
2

I tried all the solutions above and in addition, those in related answers as well. Solutions including but not limited to passing the blob manually to a HTMLInputElement's file property, calling all the readAs* methods on FileReader, using a File instance as second argument for a FormData.append call, trying to get the blob data as a string by getting the values at URL.createObjectURL(myBlob) which turned out nasty and crashed my machine.

Now, if you happen to attempt those or more and still find you're unable to upload your blob, it could mean the problem is server-side. In my case, my blob exceeded the http://www.php.net/manual/en/ini.core.php#ini.upload-max-filesize and post_max_size limit in PHP.INI so the file was leaving the front end form but getting rejected by the server. You could either increase this value directly in PHP.INI or via .htaccess

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.