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

Resuelto BrianFreud asked hace 12 años • 10 respuestas

Permito que el usuario cargue imágenes en una página mediante arrastrar y soltar y otros métodos. Cuando se suelta una imagen, la uso URL.createObjectURLpara convertirla a una URL de objeto para mostrar la imagen. No revoco la URL, ya que la reutilizo.

Entonces, cuando llega el momento de crear un FormDataobjeto, para poder permitirles cargar un formulario con una de esas imágenes, ¿hay alguna manera de revertir la URL del objeto nuevamente en una Blobo Filealgo así y luego puedo agregarla? a un FormDataobjeto?

BrianFreud avatar Aug 09 '12 10:08 BrianFreud
Aceptado

Solución moderna:

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

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

 avatar Sep 19 '2018 16:09

Como alude gengkev 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 utilizar de forma segura, Joe tiene una respuesta más simple basada en ES5 a continuación.

BrianFreud avatar Aug 10 '2012 12:08 BrianFreud

Quizás alguien encuentre esto útil cuando trabaje con React/Node/Axios. Utilicé esto para mi función de carga de imágenes de Cloudinary en react-dropzonela 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 avatar Feb 06 '2018 11:02 spedy

El problema de recuperar la URL del blob nuevamente es que esto creará una copia completa de los datos del blob y, por lo tanto, en lugar de tenerla solo una vez en la memoria, la tendrá dos veces. Con Blobs grandes, esto puede arruinar el uso de memoria con bastante rapidez.

Es bastante desafortunado que File API no nos brinde acceso a los Blobs actualmente vinculados; ciertamente pensaron que los autores web deberían almacenar ese Blob ellos mismos en el momento de la creación de todos modos, lo cual es cierto:

Lo mejor aquí es almacenar el objeto que utilizó al crear la URL blob://.

Si tiene miedo de que esto impida que el Blob sea recolectado como basura, tiene razón, pero también lo hace la URL blob:// en primer lugar, hasta que la revoque. Entonces, mantener un puntero hacia ese Blob no cambiará nada.

Pero para aquellos que no son responsables de la creación del URI blob:// (por ejemplo, porque lo creó una biblioteca), aún podemos llenar ese vacío de API nosotros mismos anulando los métodos predeterminados URL.createObjectURL y URL.revokeObjectURL para que almacene referencias al objeto pasado.

Asegúrese de llamar a esta función antes de llamar al código que genera el URI blob://.

// Adds an URL.getFromObjectURL( <blob:// URI> ) method
// returns the original object (<Blob> or <MediaSource>) the URI points to or null
(() => {
  // overrides URL methods to be able to retrieve the original blobs later on
  const old_create = URL.createObjectURL;
  const old_revoke = URL.revokeObjectURL;
  Object.defineProperty(URL, 'createObjectURL', {
    get: () => storeAndCreate
  });
  Object.defineProperty(URL, 'revokeObjectURL', {
    get: () => forgetAndRevoke
  });
  Object.defineProperty(URL, 'getFromObjectURL', {
    get: () => getBlob
  });
  const dict = {};

  function storeAndCreate(blob) {
    const url = old_create(blob); // let it throw if it has to
    dict[url] = blob;
    return url
  }

  function forgetAndRevoke(url) {
    old_revoke(url);
    try {
      if(new URL(url).protocol === 'blob:') {
        delete dict[url];
      }
    } catch(e){}
  }

  function getBlob(url) {
    return dict[url] || null;
  }
})();

//  Usage:
const blob = new Blob( ["foo"] );
const url = URL.createObjectURL( blob );
console.log( url );
const retrieved = URL.getFromObjectURL( url );
console.log( "retrieved Blob is Same Object?", retrieved === blob );
fetch( url ).then( (resp) => resp.blob() )
  .then( (fetched) => console.log( "fetched Blob is Same Object?", fetched === blob ) );
Expandir fragmento

Y otra ventaja es que incluso puede recuperar objetos MediaSource , mientras que las soluciones de búsqueda simplemente se equivocarían en ese caso.

Kaiido avatar Apr 08 '2021 06:04 Kaiido