131

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);
    });
Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
Yang
  • 9,794
  • 15
  • 44
  • 52

6 Answers6

146

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);
});
yaya
  • 7,675
  • 1
  • 39
  • 38
Fabrício Matté
  • 69,329
  • 26
  • 129
  • 166
  • 3
    Do you know how to do this without AJAX too? – William Entriken Aug 07 '13 at 02:05
  • 1
    @FullDecent What do you mean? To prompt the user to download a file using the File API? Or to just store the blob content? – Fabrício Matté Aug 07 '13 at 02:07
  • 4
    To do basically `$('input[type=file]').value=blob` – William Entriken Aug 09 '13 at 21:36
  • The `files` property returns a [`FileList`](https://developer.mozilla.org/en-US/docs/Web/API/FileList), hence there's apparently no standard way to push a file into an `input[type=file]`'s Files collection. But you can make a question and I may try to look for some workaround. `=]` – Fabrício Matté Aug 09 '13 at 21:45
  • 16
    Security requirements prevent programmatic setting of file input values: http://stackoverflow.com/questions/1696877/how-to-set-a-value-to-a-file-input-in-html – yeeking Aug 15 '13 at 12:43
  • @yeeking Sometimes these security guidelines are softened, however I guess you're right - not only setting the value doesn't work, cloning a file input doesn't carry its value either and there's no File API method to programmatically add files to file inputs (yet). – Fabrício Matté Aug 15 '13 at 12:57
  • This worked with [tag:asp.net-mvc]. The controller method signature will look like `ActionResult Upload(string fname, HttpPostedFileBase data)`. Browser is Chrome 36. – Michael Aug 07 '14 at 16:16
  • 9
    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: http://stackoverflow.com/questions/6664967/how-to-give-a-blob-uploaded-as-formdata-a-file-name – Sebastien Lorber Feb 13 '15 at 14:34
56

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);
?>
chatnoir
  • 2,185
  • 1
  • 15
  • 17
  • 1
    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. – Stephen Duffy Oct 12 '20 at 20:03
  • 1
    @StephenDuffy the answers are 2 years apart – PauAI Jan 04 '22 at 08:43
  • Works well, but might open some security holes. It's a really good training however to understand why so. – NVRM Sep 18 '22 at 16:30
23

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

Xedret
  • 1,823
  • 18
  • 25
Dmitry Pashkevich
  • 13,431
  • 9
  • 55
  • 73
19

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);
?>
yeeking
  • 958
  • 8
  • 11
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);
?>
Soumen Basak
  • 251
  • 3
  • 7
  • 9
    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) – yeeking Dec 15 '14 at 22:35
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

I Want Answers
  • 371
  • 3
  • 15