¿Cómo obtener un archivo o blob desde la URL de un objeto?
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.createObjectURL
para 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 FormData
objeto, para poder permitirles cargar un formulario con una de esas imágenes, ¿hay alguna manera de revertir la URL del objeto nuevamente en una Blob
o File
algo así y luego puedo agregarla? a un FormData
objeto?
Solución moderna:
let blob = await fetch(url).then(r => r.blob());
La URL puede ser una URL de objeto o una URL normal.
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.
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-dropzone
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)
}
})
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 ) );
Y otra ventaja es que incluso puede recuperar objetos MediaSource , mientras que las soluciones de búsqueda simplemente se equivocarían en ese caso.