How can javascript upload a blob?
Asked Answered
K

6

136

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);
    });
Krems answered 11/11, 2012 at 17:6 Comment(2)
You might want to think about using binaryJS for this. If you are streaming data, that might do the trick.Indre
This answer is very detailed as well. https://mcmap.net/q/40413/-how-can-i-upload-files-asynchronously-with-jqueryDanettedaney
D
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);
});
Drinkwater answered 11/11, 2012 at 17:17 Comment(8)
Do you know how to do this without AJAX too?Gord
@FullDecent What do you mean? To prompt the user to download a file using the File API? Or to just store the blob content?Danettedaney
To do basically $('input[type=file]').value=blobGord
The files property returns a 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. =]Danettedaney
Security requirements prevent programmatic setting of file input values: #1697377Baste
@Baste 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).Danettedaney
This worked with asp.net-mvc. The controller method signature will look like ActionResult Upload(string fname, HttpPostedFileBase data). Browser is Chrome 36.Biparietal
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: #6665467Phosphorus
P
59

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);
?>
Pietrek answered 6/5, 2019 at 14:4 Comment(3)
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.Saury
@StephenDuffy the answers are 2 years apartChannel
Works well, but might open some security holes. It's a really good training however to understand why so.Herophilus
P
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

Patron answered 23/11, 2015 at 23:11 Comment(0)
B
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);
?>
Baste answered 15/8, 2013 at 14:24 Comment(1)
I'm pretty sure you can change the line data: fd, in the ajax function call to data: blob,.Stouthearted
N
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);
?>
Nedra answered 13/2, 2014 at 5:55 Comment(1)
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)Baste
N
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

Nickerson answered 17/10, 2017 at 5:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.