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

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.


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


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

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

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

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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.