Como o javascript pode fazer upload de um blob?

107

Tenho um blob de dados nesta estrutura:

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

Na verdade, são dados de som gravados usando o recente Chrome getUerMedia()e Recorder.js

Como posso fazer upload desse blob para o servidor usando o método de postagem do jquery? Eu tentei isso sem sorte:

   $.post('http://localhost/upload.php', { fname: "test.wav", data: soundBlob }, 
    function(responseText) {
           console.log(responseText);
    });
Yang
fonte
1
Você pode querer pensar em usar binaryJS para isso. Se você estiver transmitindo dados, isso pode funcionar.
toxicate20
Essa resposta também é muito detalhada. stackoverflow.com/a/8758614/1331430
Fabrício Matté 11/11/12

Respostas:

123

Tente isto

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);
});

Você precisa usar a API FormData e definir o jQuery.ajax's processDatae contentTypecomo false.

Fabrício Matté
fonte
1
Você sabe como fazer isso sem AJAX também?
William Entriken
@FullDecent O que você quer dizer? Para solicitar que o usuário baixe um arquivo usando a API de arquivo? Ou apenas para armazenar o conteúdo do blob?
Fabrício Matté
4
Para fazer basicamente$('input[type=file]').value=blob
William Entriken
14
Os requisitos de segurança evitam a configuração programática dos valores de entrada do arquivo: stackoverflow.com/questions/1696877/…
yeeking
9
Observe que um Blob tem um nome de arquivo genérico quando enviado ao servidor, ao contrário de um Arquivo. Mas você pode especificar o nome do arquivo Blob em FormData: stackoverflow.com/questions/6664967/…
Sebastien Lorber
20

Na verdade, você não precisa FormDataenviar um Blobpara o servidor a partir de JavaScript (e a Filetambém é um Blob).

Exemplo jQuery:

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
});

Exemplo de Vanilla JavaScript:

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);

Concedido, se você estiver substituindo um formulário HTML tradicional com várias partes por uma implementação "AJAX" (ou seja, seu back-end consome dados do formulário com várias partes), você deseja usar o FormDataobjeto conforme descrito em outra resposta.

Fonte: Novos truques em XMLHttpRequest2 | HTML5 Rocks

Dmitry Pashkevich
fonte
17

Não consegui fazer o exemplo acima funcionar com blobs e queria saber o que exatamente está em upload.php. Então aqui está:

(testado apenas no 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);

}

O conteúdo de 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);
?>
uivando
fonte
Tenho certeza de que você pode alterar a linha data: fd,na ajaxchamada de função para data: blob,.
Kenny Evitt
11

Consegui fazer o exemplo @yeeking funcionar não usando FormData, mas usando um objeto javascript para transferir o blob. Funciona com um blob de som criado usando recorder.js. Testado no Chrome versão 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);
}

Conteúdo de 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
fonte
7
Cuidado com o arquivo php - se você permitir que o cliente HTTP defina o nome do arquivo, eles podem usá-lo para carregar conteúdo malicioso em um arquivo e diretório de sua escolha. (contanto que o Apache possa escrever lá)
yeeking
9

Atualização de 2019

Isso atualiza as respostas com a API Fetch mais recente e não precisa do jQuery.

Aviso de isenção de responsabilidade: não funciona no IE, Opera Mini e navegadores mais antigos. Veja caniuse .

Busca Básica

Pode ser tão simples como:

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

Buscar com tratamento de erros

Depois de adicionar o tratamento de erros, pode ficar assim:

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);
            });

Código PHP

Este é o código do lado do servidor em 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
fonte
2

Tentei todas as soluções acima e, além disso, aquelas em respostas relacionadas também. Soluções incluindo, mas não se limitando a, passar o blob manualmente para uma propriedade de arquivo HTMLInputElement, chamando todos os métodos readAs * em FileReader, usando uma instância de File como segundo argumento para uma chamada FormData.append, tentando obter os dados de blob como uma string obtendo os valores em URL.createObjectURL (myBlob) que resultaram desagradáveis ​​e travaram minha máquina.

Agora, se por acaso você tentar esses ou mais e ainda descobrir que não consegue fazer upload do seu blob, isso pode significar que o problema está no servidor. No meu caso, meu blob excedeu o limite de http://www.php.net/manual/en/ini.core.php#ini.upload-max-filesize e post_max_size no PHP.INI, então o arquivo estava saindo do front end formulário, mas sendo rejeitado pelo servidor. Você pode aumentar este valor diretamente no PHP.INI ou via .htaccess

Eu quero respostas
fonte