Come ottenere un file o un BLOB dall'URL di un oggetto?


127

Consento all'utente di caricare immagini in una pagina tramite drag & drop e altri metodi. Quando un'immagine viene rilasciata, sto usando URL.createObjectURLper convertire in un oggetto URL per visualizzare l'immagine. Non revoco l'URL, poiché lo riutilizzo.

Quindi, quando arriva il momento di creare un FormDataoggetto in modo che possa consentire loro di caricare un modulo con una di quelle immagini al suo interno, c'è un modo per invertire l'URL dell'oggetto in un Blobo Filecosì posso quindi aggiungerlo a un FormDataoggetto?


non preoccuparti dei due commenti precedenti: tutto ciò che devi fare è inviare un XMLHttpRequestall'URL del blob.
gengkev

2
Perché non memorizzare semplicemente gli oggetti file originali da qualche parte, quindi utilizzare l'URL dell'oggetto per visualizzarli e durante l'invio del modulo utilizzare i file originali?
user764754

@ user764754 perché il tentativo di ottenere l'URL del file caricato dall'utente viene visualizzato come "C: \ fakepath"
Malcolm Salvador

Risposte:


89

Soluzione moderna:

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

L'URL può essere l'URL di un oggetto o un URL normale.


3
Bello. Sono contento di vedere che ES5 sta rendendo le cose come questa più semplici.
BrianFreud

11
E se vuoi ottenere direttamente un file dalla promessa, puoi generare un file come segue. let file = await fetch(url).then(r => r.blob()).then(blobFile => new File([blobFile], "fileNameGoesHere", { type: "image/png" })
dbakiu

3
Sfortunatamente, questa soluzione non funziona per me in Chrome. Il browser non riesce a caricare questo URL.
waldgeist

Waldgeist, l'hai racchiuso in createObjectUrl ()?
Matt Fletcher

73

Come allude gengkev nel suo commento sopra, sembra che il modo migliore / unico per farlo sia con una chiamata xhr2 asincrona:

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

Aggiornamento (2018): per le situazioni in cui ES5 può essere utilizzato in sicurezza, Joe ha una risposta più semplice basata su ES5 di seguito.


19
Quando avresti mai un objectURL che non è locale per il dominio e l'ambito correnti?
BrianFreud

4
l'ho fatto come sopra, ma non ho trovato 404 con xhr. cosa sta succedendo?
albert yu

Si prega di notare che alcuni browser (leggi vecchio IE ...), se è necessario gestire quelli vedi post stackoverflow.com/questions/17657184/...
mraxus

4
@BrianFreud "Quando avresti mai un objectURL che non è locale per il dominio e l'ambito correnti?" Nel mio caso sto cercando di dare a un blob di Amazon S3 un nome file diverso, che attualmente è un "guid" su S3 senza estensione. Quindi, nel mio caso, sto utilizzando una chiamata interdominio.
Wagner Bertolini Junior

6
"Gli URL degli oggetti sono URL che puntano a file su disco." Gli ObjectURL per definizione possono essere solo locali.
BrianFreud

13

Forse qualcuno lo trova utile quando lavora con React / Node / Axios. L'ho usato per la mia funzione di caricamento delle immagini Cloudinary con react-dropzonesull'interfaccia utente.

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

    })

2
Funziona per le richieste interdominio? I video di Twitter hanno un URL BLOB. Devo essere in grado di raccogliere l'oggetto BLOB a cui punta l'URL BLOB. Sto usando fetch api nel browser, che mi dà questo errore - Refused to connect to 'blob:https://twitter.com/9e00aec3-6729-42fb-b5a7-01f50be302fa' because it violates the following Content Security Policy directive: "connect-src . Potresti avere un suggerimento su cosa potrei fare di sbagliato / non ottenere?
gdadsriver

Sono stato in grado di utilizzare questo one-liner per ottenere il risultato con il blob come proprietà dei dati: const result = await axios.get (url, {responseType: "blob"});
Noah Stahl


4

Usando fetch per esempio come di seguito:

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

Puoi incollare sulla console Chrome per testare. il file con il download con 'sample.xlsx' Spero che possa aiutare!


Penso che l'OP abbia chiesto di convertire l'URL di un blob in un file / blob effettivo, piuttosto che il contrario.
Abhishek Ghosh

3

Sfortunatamente la risposta di @ BrianFreud non si adatta alle mie esigenze, avevo un'esigenza leggermente diversa, e so che non è la risposta alla domanda di @ BrianFreud, ma la lascio qui perché molte persone sono arrivate qui con la mia stessa esigenza. Avevo bisogno di qualcosa come "Come ottenere un file o un BLOB da un URL?" E la risposta corretta corrente non si adatta alle mie esigenze perché non è interdominio.

Ho un sito Web che utilizza immagini da Amazon S3 / Azure Storage e lì memorizzo oggetti denominati con identificatori univoci:

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

Alcune di queste immagini dovrebbero essere scaricate dalla nostra interfaccia di sistema. Per evitare di far passare questo traffico attraverso il mio server HTTP, dal momento che questi oggetti non richiedono alcuna sicurezza per essere accessibili (eccetto per il filtraggio del dominio), ho deciso di fare una richiesta diretta sul browser dell'utente e utilizzare l'elaborazione locale per dare al file un nome reale e estensione.

Per riuscirci ho usato questo fantastico articolo di Henry Algus: http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/

1. Primo passo: aggiungi il supporto binario a jquery

/**
*
* jquery.binarytransport.js
*
* @description. jQuery ajax transport for making binary data type requests.
* @version 1.0 
* @author Henry Algus <henryalgus@gmail.com>
*
*/

// 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. Secondo passaggio: effettuare una richiesta utilizzando questo tipo di trasporto.

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

Ora puoi usare il Blob creato come vuoi, nel mio caso voglio salvarlo su disco.

3. Opzionale: salvare il file sul computer dell'utente utilizzando FileSaver

Ho usato FileSaver.js per salvare su disco il file scaricato, se è necessario farlo, usa questa libreria javascript:

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

Mi aspetto che questo aiuti gli altri con esigenze più specifiche.


1
Gli ObjectURL sono per definizione locali. Gli URL degli oggetti sono URL che puntano a file su disco. Dato che non esiste un objectURL interdominio, stai combinando due concetti diversi, quindi il tuo problema usando la soluzione data.
BrianFreud

1
@BrianFreud ho capito che non è esattamente la risposta a questa domanda. Ma penso ancora che sia una buona risposta da lasciare dato che sono arrivato qui cercando la risposta a una domanda leggermente diversa "Come ottenere un file o un BLOB da un URL?". Se controlli la tua risposta, ci sono 10 voti positivi su "Non funziona in caso di richieste interdominio. '. Quindi più di 10 persone sono arrivate qui a cercarlo. Ho deciso quindi di lasciarlo qui.
Wagner Bertolini Junior

Il problema è che la tua risposta consapevolmente non risponde a questa domanda. Un URL normale e un URL oggetto sono due cose completamente diverse. Per quanto riguarda il numero di voti positivi su "Non funziona in caso di richieste interdominio", non penseresti che sarebbe meglio spiegare perché è letteralmente impossibile qui e indicare un punto che mostra la risposta per URL normali, piuttosto che confondere le cose qui fondendo URL normali e URL di oggetti?
BrianFreud

@BrianFreud sentiti libero di suggerire una modifica alla mia risposta. Studierò l'argomento, forse ho frainteso cosa sia un "objectURL" rispetto a un "URL oggetto" ... L'inglese non è il mio nativo. Farò una ricerca sull'argomento per dare una risposta migliore. Ma penso che siano altri che vengono qui alla ricerca di qualcosa di diverso. Non ho cercato "objectURL" per arrivare qui, questo era il mio punto prima. Ma ho anche te.
Wagner Bertolini Junior

2

Se mostri comunque il file in una tela, puoi anche convertire il contenuto della tela in un oggetto 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);
})

2
Nota che questo in realtà non ti restituisce lo stesso file originale; sta creando un nuovo file immagine al volo. Quando l'ho testato per l'ultima volta un paio di anni fa, ho scoperto che almeno in Chrome, il nuovo file immagine non è affatto compresso: avevo 10k jpg che si trasformavano in 2,5 mb jpg.
BrianFreud
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.