¿Cómo obtener un archivo o blob de una URL de objeto?

127

Estoy permitiendo que el usuario cargue imágenes en una página mediante arrastrar y soltar y otros métodos. Cuando se cae una imagen, estoy usandoURL.createObjectURL para convertir a una URL de objeto para mostrar la imagen. No estoy revocando la URL, ya que la reutilizo.

Entonces, cuando llega el momento de crear un FormDataobjeto para que pueda permitir que carguen un formulario con una de esas imágenes, ¿hay alguna forma de que luego pueda revertir esa URL de objeto en Blobo Fileasí puedo agregarlo a un FormData¿objeto?

BrianFreud
fuente
no te preocupes por los dos comentarios anteriores: todo lo que necesitas hacer es enviar un XMLHttpRequestcorreo electrónico a la URL del blob.
gengkev
2
¿Por qué no simplemente almacenar los objetos del archivo original en algún lugar, luego usar la URL del objeto para mostrarlos y en el envío del formulario usar los archivos originales?
user764754
@ user764754 porque al intentar obtener la URL del archivo que está cargando el usuario aparece como "C: \ fakepath"
Malcolm Salvador

Respuestas:

86

Solución moderna:

let blob = await fetch(url).then(r => r.blob());

La url puede ser una url de objeto o una url normal.


fuente
3
Agradable. Me alegra ver que ES5 hace que cosas como esta sean más simples.
BrianFreud
11
Y si desea obtener directamente un archivo de la promesa, puede generar un archivo de la siguiente manera. let file = await fetch(url).then(r => r.blob()).then(blobFile => new File([blobFile], "fileNameGoesHere", { type: "image/png" })
dbakiu
3
Desafortunadamente, esta solución no me funciona en Chrome. El navegador no puede cargar esta URL.
waldgeist
Waldgeist, ¿lo envolvió en createObjectUrl ()?
Matt Fletcher
73

Como gengkev alude en su comentario anterior, parece que la mejor / única forma de hacerlo es con una llamada asíncrona xhr2:

var xhr = new XMLHttpRequest();
xhr.open('GET', 'blob:http%3A//your.blob.url.here', true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
  if (this.status == 200) {
    var myBlob = this.response;
    // myBlob is now the blob that the object URL pointed to.
  }
};
xhr.send();

Actualización (2018): para situaciones en las que ES5 se puede usar de forma segura, Joe tiene una respuesta más simple basada en ES5 a continuación.

BrianFreud
fuente
19
¿Cuándo tendrías un objectURL que no sea local para el dominio y alcance actuales?
BrianFreud
44
Lo hice igual que arriba, pero obtuve 404 no encontrado con el xhr. ¿que esta pasando?
albert yu
Tenga en cuenta que algunos navegadores (lea el IE antiguo ...), si necesita manejarlos, consulte la publicación stackoverflow.com/questions/17657184/…
mraxus
44
@BrianFreud "¿Cuándo tendrías un objectURL que no es local para el dominio y alcance actuales?" En mi caso, estoy tratando de darle a un blob de Amazon S3 un nombre de archivo diferente, que actualmente es un "guid" en el S3 sin extensión. Entonces, en mi caso estoy usando una llamada entre dominios.
Wagner Bertolini Junior
66
"Las URL de objeto son URL que apuntan a archivos en el disco". ObjectURLs por definición solo puede ser local.
BrianFreud
12

Tal vez alguien encuentre esto útil cuando trabaje con React / Node / Axios. Utilicé esto para mi función de carga de imágenes react-dropzoneen la nube en la interfaz de usuario.

    axios({
        method: 'get',
        url: file[0].preview, // blob url eg. blob:http://127.0.0.1:8000/e89c5d87-a634-4540-974c-30dc476825cc
        responseType: 'blob'
    }).then(function(response){
         var reader = new FileReader();
         reader.readAsDataURL(response.data); 
         reader.onloadend = function() {
             var base64data = reader.result;
             self.props.onMainImageDrop(base64data)
         }

    })
Spedy
fuente
1
¿Funciona esto para solicitudes de dominio cruzado? Los videos de Twitter tienen URL blob. Necesito poder reunir el objeto blob al que apunta la URL blob. Estoy usando fetch api en el navegador, lo que me está dando este error - Refused to connect to 'blob:https://twitter.com/9e00aec3-6729-42fb-b5a7-01f50be302fa' because it violates the following Content Security Policy directive: "connect-src . ¿Podría darme una pista de lo que podría estar haciendo mal / no obteniendo?
gdadsriver
Pude usar esta línea para obtener el resultado con el blob como propiedad de datos: const result = await axios.get (url, {responseType: "blob"});
Noah Stahl
4

Usando fetch por ejemplo como a continuación:

 fetch(<"yoururl">, {
    method: 'GET',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + <your access token if need>
    },
       })
.then((response) => response.blob())
.then((blob) => {
// 2. Create blob link to download
 const url = window.URL.createObjectURL(new Blob([blob]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `sample.xlsx`);
 // 3. Append to html page
 document.body.appendChild(link);
 // 4. Force download
 link.click();
 // 5. Clean up and remove the link
 link.parentNode.removeChild(link);
})

Puede pegar en la consola de Chrome para probar. el archivo con descarga con 'sample.xlsx' ¡Espero que pueda ayudar!

usuario3843418
fuente
Creo que el OP preguntó sobre la conversión de una URL de blob en un archivo / blob real, en lugar de al revés.
Abhishek Ghosh
3

Desafortunadamente, la respuesta de @ BrianFreud no se ajusta a mis necesidades, tenía una necesidad un poco diferente, y sé que esa no es la respuesta a la pregunta de @ BrianFreud, pero la dejo aquí porque muchas personas llegaron aquí con mi misma necesidad. Necesitaba algo como '¿Cómo obtener un archivo o blob de una URL?', Y la respuesta correcta actual no se ajusta a mis necesidades porque no es de dominio cruzado.

Tengo un sitio web que consume imágenes de Amazon S3 / Azure Storage, y allí almaceno objetos con identificadores únicos:

muestra: http: //****.blob.core.windows.net/systemimages/bf142dc9-0185-4aee-a3f4-1e5e95a09bcf

Algunas de estas imágenes deben descargarse desde la interfaz de nuestro sistema. Para evitar pasar este tráfico a través de mi servidor HTTP, ya que estos objetos no requieren seguridad para acceder (excepto mediante el filtrado de dominios), decidí hacer una solicitud directa en el navegador del usuario y utilizar el procesamiento local para dar al archivo un nombre real y extensión.

Para lograrlo, he usado este gran artículo de Henry Algus: http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/

1. Primer paso: agregar soporte binario a jquery

/**
*
* jquery.binarytransport.js
*
* @description. jQuery ajax transport for making binary data type requests.
* @version 1.0 
* @author Henry Algus <[email protected]>
*
*/

// use this transport for "binary" data type
$.ajaxTransport("+binary", function (options, originalOptions, jqXHR) {
    // check for conditions and support for blob / arraybuffer response type
    if (window.FormData && ((options.dataType && (options.dataType == 'binary')) || (options.data && ((window.ArrayBuffer && options.data instanceof ArrayBuffer) || (window.Blob && options.data instanceof Blob))))) {
        return {
            // create new XMLHttpRequest
            send: function (headers, callback) {
                // setup all variables
                var xhr = new XMLHttpRequest(),
        url = options.url,
        type = options.type,
        async = options.async || true,
        // blob or arraybuffer. Default is blob
        dataType = options.responseType || "blob",
        data = options.data || null,
        username = options.username || null,
        password = options.password || null;

                xhr.addEventListener('load', function () {
                    var data = {};
                    data[options.dataType] = xhr.response;
                    // make callback and send data
                    callback(xhr.status, xhr.statusText, data, xhr.getAllResponseHeaders());
                });

                xhr.open(type, url, async, username, password);

                // setup custom headers
                for (var i in headers) {
                    xhr.setRequestHeader(i, headers[i]);
                }

                xhr.responseType = dataType;
                xhr.send(data);
            },
            abort: function () {
                jqXHR.abort();
            }
        };
    }
});

2. Segundo paso: realice una solicitud con este tipo de transporte.

function downloadArt(url)
{
    $.ajax(url, {
        dataType: "binary",
        processData: false
    }).done(function (data) {
        // just my logic to name/create files
        var filename = url.substr(url.lastIndexOf('/') + 1) + '.png';
        var blob = new Blob([data], { type: 'image/png' });

        saveAs(blob, filename);
    });
}

Ahora puedes usar el Blob creado como quieras, en mi caso quiero guardarlo en el disco.

3. Opcional: guarde el archivo en la computadora del usuario con FileSaver

He usado FileSaver.js para guardar en el disco el archivo descargado, si necesita hacerlo, use esta biblioteca de JavaScript:

https://github.com/eligrey/FileSaver.js/

Espero que esto ayude a otros con necesidades más específicas.

Wagner Bertolini Junior
fuente
1
Los ObjectURL son por definición locales. Las URL de objeto son URL que apuntan a archivos en el disco. Dado que no existe tal cosa como un objectURL de dominio cruzado, está combinando dos conceptos diferentes, por lo tanto, su problema es usar la solución dada.
BrianFreud
1
@BrianFreud Lo entendí que no es exactamente la respuesta de esta pregunta. Pero sigo pensando que es una buena respuesta para irme desde que llegué aquí buscando la respuesta de una pequeña pregunta diferente '¿Cómo obtener un archivo o un blob desde una URL?'. Si verifica su propia respuesta, hay 10 votos a favor en 'No funciona en caso de solicitudes de dominio cruzado. '. Así que más de 10 personas llegaron aquí buscando eso. Entonces decidí dejarlo aquí.
Wagner Bertolini Junior
El problema es que su respuesta a sabiendas no responde a esta pregunta. Una URL normal y una URL de objeto son dos cosas completamente diferentes. Con respecto al número de votos a favor en "No funciona en caso de solicitudes de dominio cruzado", ¿no le parece mejor explicar por qué eso es literalmente imposible aquí y señalar un lugar que muestre la respuesta para URL normales, en lugar de eso? que confundir las cosas aquí al combinar URL normales y URL de objetos?
BrianFreud
@BrianFreud no dude en sugerir una edición en mi respuesta. Estudiaré sobre el tema, tal vez no entendí qué es un "objectURL" frente a un "objeto URL" ... El inglés no es mi lengua materna. Haré una investigación sobre el tema para dar una mejor respuesta. Pero creo que hay otros que vienen aquí buscando algo diferente. No busqué "objectURL" para llegar aquí, ese era mi punto antes. Pero yo también te tengo.
Wagner Bertolini Junior
2

Si muestra el archivo en un lienzo de todos modos, también puede convertir el contenido del lienzo en un objeto blob.

canvas.toBlob(function(my_file){
  //.toBlob is only implemented in > FF18 but there is a polyfill 
  //for other browsers https://github.com/blueimp/JavaScript-Canvas-to-Blob
  var myBlob = (my_file);
})
Thilo
fuente
2
Tenga en cuenta que eso no le devuelve el mismo archivo original; está haciendo un nuevo archivo de imagen sobre la marcha. Cuando lo probé por última vez hace un par de años, descubrí que al menos en Chrome, el nuevo archivo de imagen no está comprimido en absoluto: tenía 10k jpgs convirtiéndose en 2.5 mb jpgs.
BrianFreud