Comment obtenir un fichier ou un objet blob à partir d'une URL d'objet?

127

J'autorise l'utilisateur à charger des images dans une page par glisser-déposer et d'autres méthodes. Lorsqu'une image est supprimée, j'utilise URL.createObjectURLpour convertir en une URL d'objet pour afficher l'image. Je ne révoque pas l'URL, car je la réutilise.

Donc, quand vient le temps de créer un FormDataobjet afin que je puisse leur permettre de télécharger un formulaire avec l'une de ces images, y a-t-il un moyen pour que je puisse ensuite inverser cette URL d'objet en un Blobou Filealors je peux ensuite l'ajouter à un FormDataobjet?

BrianFreud
la source
peu importe les deux commentaires précédents - tout ce que vous avez à faire est d'envoyer un XMLHttpRequestà l'URL du blob.
gengkev
2
Pourquoi ne pas simplement stocker les objets du fichier d'origine quelque part, puis utiliser l'URL de l'objet pour les afficher et, lors de l'envoi du formulaire, utiliser les fichiers d'origine?
user764754
@ user764754 parce que la tentative d'obtenir l'URL du fichier en cours de téléchargement par l'utilisateur s'affiche sous la forme "C: \ fakepath"
Malcolm Salvador

Réponses:

89

Solution moderne:

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

L'URL peut être une URL d'objet ou une URL normale.


la source
3
Agréable. Heureux de voir ES5 simplifier les choses comme ça.
BrianFreud
11
Et si vous souhaitez obtenir directement un fichier à partir de la promesse, vous pouvez générer un fichier comme suit. let file = await fetch(url).then(r => r.blob()).then(blobFile => new File([blobFile], "fileNameGoesHere", { type: "image/png" })
dbakiu
3
Malheureusement, cette solution ne fonctionne pas pour moi dans Chrome. Le navigateur ne parvient pas à charger cette URL.
waldgeist
Waldgeist, l'avez-vous enveloppé dans createObjectUrl ()?
Matt Fletcher
73

Comme Gengkev y fait allusion dans son commentaire ci-dessus, il semble que la meilleure / seule façon de le faire soit avec un appel async 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();

Mise à jour (2018): Pour les situations où ES5 peut être utilisé en toute sécurité, Joe a une réponse plus simple basée sur ES5 ci-dessous.

BrianFreud
la source
19
Quand auriez-vous jamais une objectURL qui n'est pas locale au domaine et à la portée actuels?
BrianFreud
4
je l'ai fait comme ci-dessus, mais j'ai obtenu 404 introuvable avec le xhr. Que se passe-t-il?
albert yu
Veuillez noter que certains navigateurs (lisez l'ancien IE ...), si vous avez besoin de gérer ceux-ci, consultez l'article stackoverflow.com/questions/17657184
...
4
@BrianFreud "Quand auriez-vous jamais une objectURL qui n'est pas locale au domaine et à la portée actuels?" Dans mon cas, j'essaie de donner un nom de fichier différent à un objet blob Amazon S3, qui est actuellement un "guid" sur le S3 sans extension. Donc, dans mon cas, j'utilise un appel inter-domaines.
Wagner Bertolini Junior
6
"Les URL d'objets sont des URL qui pointent vers des fichiers sur le disque." Les ObjectURL par définition ne peuvent être que locales.
BrianFreud
13

Peut-être que quelqu'un trouve cela utile lorsque vous travaillez avec React / Node / Axios. Je l'ai utilisé pour ma fonctionnalité de téléchargement d'image Cloudinary avec react-dropzonesur l'interface utilisateur.

    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
la source
2
Cela fonctionne-t-il pour les demandes interdomaines? Les vidéos Twitter ont une URL blob. Je dois pouvoir rassembler l'objet blob vers lequel pointe l'URL du blob. J'utilise fetch api dans le navigateur, ce qui me donne cette erreur - Refused to connect to 'blob:https://twitter.com/9e00aec3-6729-42fb-b5a7-01f50be302fa' because it violates the following Content Security Policy directive: "connect-src . Pourriez-vous avoir une idée de ce que je pourrais faire de mal / ne pas obtenir?
gdadsriver
J'ai pu utiliser ce one-liner pour obtenir le résultat avec le blob comme propriété de données: const result = await axios.get (url, {responseType: "blob"});
Noah Stahl
4

En utilisant fetch par exemple comme ci-dessous:

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

Vous pouvez coller sur la console Chrome pour tester. le fichier à télécharger avec «sample.xlsx» J'espère qu'il peut vous aider!

user3843418
la source
Je pense que l'OP a posé des questions sur la conversion d'une URL de blob en fichier / blob réel, plutôt que l'inverse.
Abhishek Ghosh
3

Malheureusement, la réponse de @ BrianFreud ne correspond pas à mes besoins, j'avais un besoin un peu différent, et je sais que ce n'est pas la réponse à la question de @ BrianFreud, mais je la laisse ici parce que beaucoup de personnes sont arrivées ici avec mon même besoin. J'avais besoin de quelque chose comme «Comment obtenir un fichier ou un objet blob à partir d'une URL?», Et la bonne réponse actuelle ne correspond pas à mes besoins car elle n'est pas interdomaine.

J'ai un site Web qui consomme des images d'un stockage Amazon S3 / Azure, et j'y stocke des objets nommés avec des identificateurs uniques:

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

Certaines de ces images doivent être téléchargées depuis notre interface système. Pour éviter de faire passer ce trafic via mon serveur HTTP, puisque cet objet ne nécessite aucune sécurité pour accéder (sauf par filtrage de domaine), j'ai décidé de faire une requête directe sur le navigateur de l'utilisateur et d'utiliser un traitement local pour donner au fichier un vrai nom et extension.

Pour ce faire, j'ai utilisé cet excellent article de Henry Algus: http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/

1. Première étape: ajouter le support binaire à 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. Deuxième étape: faites une demande en utilisant ce type de transport.

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

Vous pouvez maintenant utiliser le Blob créé comme vous le souhaitez, dans mon cas, je veux l'enregistrer sur le disque.

3. Facultatif: enregistrez le fichier sur l'ordinateur de l'utilisateur à l'aide de FileSaver

J'ai utilisé FileSaver.js pour enregistrer sur le disque le fichier téléchargé, si vous devez accomplir cela, veuillez utiliser cette bibliothèque javascript:

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

J'espère que cela aidera d'autres personnes ayant des besoins plus spécifiques.

Wagner Bertolini Junior
la source
1
Les ObjectURL sont par définition locales. Les URL d'objets sont des URL qui pointent vers des fichiers sur le disque. Étant donné qu'il n'existe pas de URL d'objet inter-domaines, vous combinez deux concepts différents, donc votre problème en utilisant la solution donnée.
BrianFreud
1
@BrianFreud J'ai compris que ce n'est pas exactement la réponse à cette question. Mais je pense toujours que c'est une bonne réponse à partir puisque je suis arrivé ici à la recherche de la réponse à une question un peu différente «Comment obtenir un fichier ou un objet blob à partir d'une URL?». Si vous cochez votre propre réponse, il y a 10 votes positifs sur «Cela ne fonctionne pas en cas de demandes interdomaines. ». Donc plus de 10 personnes sont venues chercher ça. J'ai alors décidé de le laisser ici.
Wagner Bertolini Junior
Le problème est que votre réponse ne répond pas sciemment à cette question. Une URL normale et une URL d'objet sont deux choses totalement différentes. En ce qui concerne le nombre de votes positifs sur "Cela ne fonctionne pas en cas de requêtes entre domaines", ne pensez-vous pas qu'il vaut mieux expliquer pourquoi c'est littéralement impossible ici, et pointer vers un endroit qui montre la réponse pour les URL normales, plutôt que de confondre les choses ici en confondant les URL normales et les URL d'objet?
BrianFreud
@BrianFreud n'hésitez pas à suggérer une modification de ma réponse. J'étudierai sur le sujet, peut-être ai-je mal compris ce qu'est une "objectURL" par rapport à une "URL d'objet" ... L'anglais n'est pas ma langue maternelle. Je vais faire une recherche sur le sujet pour donner une meilleure réponse. Mais je pense que d'autres viennent ici à la recherche de quelque chose de différent. Je n'ai pas cherché "objectURL" pour arriver ici, c'était mon argument avant. Mais je t'ai aussi.
Wagner Bertolini Junior le
2

Si vous affichez quand même le fichier dans un canevas, vous pouvez également convertir le contenu du canevas en objet 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
la source
2
Notez que cela ne vous rend pas réellement le même fichier d'origine; il crée un nouveau fichier image à la volée. La dernière fois que j'ai testé cela il y a quelques années, j'ai trouvé qu'au moins dans Chrome, le nouveau fichier image n'était pas du tout compressé - j'avais 10k jpgs qui se transformaient en jpgs de 2,5 mb.
BrianFreud