¿Cómo puede javascript cargar un blob?

107

Tengo un blob de datos en esta estructura:

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

En realidad, son datos de sonido grabados utilizando Chrome getUerMedia()y Recorder.js recientes.

¿Cómo puedo cargar este blob en el servidor usando el método de publicación de jquery? Intenté esto sin suerte:

   $.post('http://localhost/upload.php', { fname: "test.wav", data: soundBlob }, 
    function(responseText) {
           console.log(responseText);
    });
Yang
fuente
1
Es posible que desee pensar en usar binaryJS para esto. Si está transmitiendo datos, eso podría ser el truco.
toxicate20 de
Esta respuesta también es muy detallada. stackoverflow.com/a/8758614/1331430
Fabrício Matté

Respuestas:

123

Prueba esto

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

Es necesario utilizar la API FormData y establecer el jQuery.ajax's processDatay contentTypea false.

Fabrício Matté
fuente
1
¿También sabes cómo hacer esto sin AJAX?
William Entriken
@FullDecent ¿Qué quieres decir? ¿Para pedirle al usuario que descargue un archivo usando la API de archivos? ¿O simplemente almacenar el contenido de blobs?
Fabrício Matté
4
Para hacer básicamente$('input[type=file]').value=blob
William Entriken
14
Los requisitos de seguridad evitan la configuración programática de los valores de entrada de archivos: stackoverflow.com/questions/1696877/…
yeeking
9
Tenga en cuenta que un blob tiene un nombre de archivo genérico cuando se envía al servidor, a diferencia de un archivo. Pero puede especificar el nombre del archivo Blob en FormData: stackoverflow.com/questions/6664967/…
Sebastien Lorber
20

En realidad, no tiene que usar FormDatapara enviar un Blobal servidor desde JavaScript (y a Filetambién es a Blob).

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

Ejemplo de JavaScript de vainilla:

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

Por supuesto, si está reemplazando un formulario de varias partes HTML tradicional con una implementación "AJAX" (es decir, su back-end consume datos de formulario de varias partes), desea usar el FormDataobjeto como se describe en otra respuesta.

Fuente: Nuevos trucos en XMLHttpRequest2 | HTML5 Rocks

Dmitry Pashkevich
fuente
17

No pude hacer que el ejemplo anterior funcionara con blobs y quería saber qué hay exactamente en upload.php. Así que aquí tienes:

(probado solo en 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);

}

El contenido 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);
?>
yeeking
fuente
Estoy bastante seguro de que puede cambiar la línea data: fd,en la ajaxllamada de función a data: blob,.
Kenny Evitt
11

Pude hacer que el ejemplo de @yeeking funcionara sin usar FormData sino con el objeto javascript para transferir el blob. Funciona con un blob de sonido creado con recorder.js. Probado en Chrome versión 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);
}

Contenido 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
fuente
7
Cuidado con el archivo php: si permite que el cliente HTTP establezca el nombre del archivo, podría usarlo para cargar contenido malicioso en un archivo y directorio de su elección. (siempre que Apache pueda escribir allí)
yeeking
9

Actualización 2019

Esto actualiza las respuestas con la última Fetch API y no necesita jQuery.

Descargo de responsabilidad: no funciona en IE, Opera Mini y navegadores más antiguos. Ver caniuse .

Búsqueda básica

Podría ser tan simple como:

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

Obtener con manejo de errores

Después de agregar el manejo de errores, podría verse así:

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 es el código del lado del servidor en 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
fuente
2

Probé todas las soluciones anteriores y, además, las de las respuestas relacionadas. Soluciones que incluyen, entre otras, pasar el blob manualmente a la propiedad de archivo de HTMLInputElement, llamar a todos los métodos readAs * en FileReader, usar una instancia de archivo como segundo argumento para una llamada FormData.append, intentar obtener los datos del blob como una cadena obteniendo los valores en URL.createObjectURL (myBlob) que resultaron desagradables y bloquearon mi máquina.

Ahora, si intenta esos o más y aún descubre que no puede cargar su blob, podría significar que el problema está en el lado del servidor. En mi caso, mi blob superó el límite de http://www.php.net/manual/en/ini.core.php#ini.upload-max-filesize y post_max_size en PHP.INI, por lo que el archivo estaba saliendo de la interfaz formulario pero siendo rechazado por el servidor. Puede aumentar este valor directamente en PHP.INI o mediante .htaccess

Quiero respuestas
fuente