Como obter um arquivo ou blob de uma URL de objeto?

127

Estou permitindo que o usuário carregue imagens em uma página via arrastar e soltar e outros métodos. Quando uma imagem é descartada, estou usando URL.createObjectURLpara converter em um URL de objeto para exibir a imagem. Não estou revogando o URL, pois o reutilizo.

Portanto, quando chega a hora de criar um FormDataobjeto para permitir que eles carreguem um formulário com uma dessas imagens, existe alguma maneira de reverter esse URL de objeto novamente para um Blobou mais Filee depois anexá-lo a um FormDataobjeto?

BrianFreud
fonte
deixa para lá os dois comentários anteriores - tudo o que você precisa fazer é enviar um XMLHttpRequestpara o URL do blob.
gengkev
2
Por que não simplesmente armazenar os objetos de arquivo originais em algum lugar e usar o URL do objeto para exibi-los e, no formulário, enviar usar os arquivos originais?
user764754
@ user764754 porque a tentar obter a URL do arquivo que está sendo carregado pelo usuário é exibido como "C: \ fakepath"
Malcolm Salvador

Respostas:

86

Solução moderna:

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

O URL pode ser um objeto ou um URL normal.


fonte
3
Agradável. Fico feliz em ver que o ES5 está simplificando as coisas.
18718 BrianFreud #
11
E se você deseja obter diretamente um arquivo da promessa, pode gerar um arquivo da seguinte maneira. let file = await fetch(url).then(r => r.blob()).then(blobFile => new File([blobFile], "fileNameGoesHere", { type: "image/png" })
dbakiu 13/02/19
3
Infelizmente, esta solução não funciona para mim no Chrome. O navegador falha ao carregar este URL.
Waldgeist # 29/19
Waldgeist, você o envolveu em createObjectUrl ()?
Matt Fletcher
73

Como menciona gengkev em seu comentário acima, parece que a melhor / única maneira de fazer isso é com uma chamada assí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();

Atualização (2018): Para situações em que o ES5 pode ser usado com segurança, Joe tem uma resposta mais simples baseada em ES5 abaixo.

BrianFreud
fonte
19
Quando você teria um objectURL que não é local para o domínio e escopo atuais?
BrianFreud
4
Eu fiz o mesmo que acima, mas tenho 404 não encontrado com o xhr. o que está acontecendo?
precisa saber é o seguinte
Por favor, note que alguns navegadores (leia velho IE ...), se você precisa lidar com aqueles ver post stackoverflow.com/questions/17657184/...
mraxus
4
@BrianFreud "Quando você teria um objectURL que não é local para o domínio e escopo atuais?" No meu caso, estou tentando atribuir a um blob do Amazon S3 um nome de arquivo diferente, que atualmente é um "guid" no S3 sem extensão. Portanto, no meu caso, estou usando uma chamada entre domínios.
Wagner Bertolini Junior
6
"URLs de objetos são URLs que apontam para arquivos no disco." ObjectURLs por definição podem ser apenas locais.
BrianFreud
12

Talvez alguém ache isso útil ao trabalhar com React / Node / Axios. Usei isso para meu recurso de upload de imagem do Cloudinary react-dropzonena interface do usuário.

    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
fonte
1
Isso funciona para solicitações entre domínios? Os vídeos do Twitter têm URL de blob. Eu preciso ser capaz de reunir o objeto de blob que o URL do blob está apontando. Estou usando a API de busca no navegador, o que está me dando esse erro - Refused to connect to 'blob:https://twitter.com/9e00aec3-6729-42fb-b5a7-01f50be302fa' because it violates the following Content Security Policy directive: "connect-src . Você poderia ter uma dica do que eu poderia estar fazendo de errado / não entendendo?
Gdadsriver 13/0318
Consegui usar esse liner para obter o resultado com o blob como propriedade de dados: const resultado = aguardar axios.get (url, {responseType: "blob"});
Noah Stahl
4

Consulte Obtendo dados BLOB da solicitação XHR, que indica que o BlobBuilder não funciona no Chrome, então você precisa usar:

xhr.responseType = 'arraybuffer';
Michel Floyd
fonte
4

Usando busca, por exemplo, como abaixo:

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

Você pode colar no console do Chrome para testar. o arquivo com download com 'sample.xlsx' Espero que ajude!

user3843418
fonte
Eu acho que o OP perguntou sobre a conversão de um URL de blob em arquivo / blob real, e não o contrário.
Abhishek Ghosh
3

Infelizmente, a resposta do @ BrianFreud não se encaixa nas minhas necessidades, eu tinha uma necessidade um pouco diferente e sei que essa não é a resposta para a pergunta do @ BrianFreud, mas estou deixando aqui porque muitas pessoas chegaram aqui com a minha mesma necessidade. Eu precisava de algo como 'Como obter um arquivo ou blob de um URL?', E a resposta correta atual não se encaixa nas minhas necessidades porque não é um domínio cruzado.

Eu tenho um site que consome imagens de um Amazon S3 / Azure Storage e lá guardo objetos nomeados com identificadores exclusivos:

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

Algumas dessas imagens devem ser baixadas da nossa interface do sistema. Para evitar a transmissão desse tráfego pelo meu servidor HTTP, já que esses objetos não exigem acesso a nenhuma segurança (exceto pela filtragem de domínio), decidi fazer uma solicitação direta no navegador do usuário e usar o processamento local para dar ao arquivo um nome real e extensão.

Para conseguir isso, usei este excelente artigo de Henry Algus: http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/

1. Primeiro passo: adicione suporte binário ao 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. Segunda etapa: faça uma solicitação usando 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);
    });
}

Agora você pode usar o Blob criado como quiser, no meu caso, quero salvá-lo em disco.

3. Opcional: salve o arquivo no computador do usuário usando o FileSaver

Eu usei o FileSaver.js para salvar em disco o arquivo baixado. Se você precisar fazer isso, use esta biblioteca javascript:

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

Espero que isso ajude outras pessoas com necessidades mais específicas.

Wagner Bertolini Junior
fonte
1
ObjectURLs são por definição locais. URLs de objeto são URLs que apontam para arquivos no disco. Como não existe um ObjectURL entre domínios, você está combinando dois conceitos diferentes, portanto, o seu problema é usar a solução fornecida.
BrianFreud
1
@BrianFreud Eu entendi que não é exatamente a resposta desta pergunta. Mas ainda acho que é uma boa resposta para deixar, pois cheguei aqui procurando a resposta de uma pergunta um pouco diferente 'Como obter um arquivo ou blob de um URL?'. Se você verificar sua própria resposta, há 10 upvotes em 'Isso não funciona em caso de solicitações entre domínios. ' Então, mais de 10 pessoas chegaram aqui procurando por isso. Decidi então deixá-lo aqui.
Wagner Bertolini Junior
O problema é que sua resposta conscientemente não responde a essa pergunta. Uma URL normal e uma URL de objeto são duas coisas totalmente diferentes. Em relação ao número de votos positivos em "Não funciona no caso de solicitações entre domínios.", Você não acha melhor explicar por que isso é literalmente impossível aqui e apontar para algum lugar que mostre a resposta para URLs normais? do que confundir as coisas aqui, combinando URLs normais e URLs de objetos?
27417 BrianFreud
@BrianFreud, sinta-se à vontade para sugerir uma edição na minha resposta. Vou estudar sobre o assunto, talvez eu tenha entendido mal o que é um "objectURL" versus um "URL do objeto" ... O inglês não é meu nativo. Farei uma pesquisa sobre o assunto para dar uma resposta melhor. Mas acho que existem outros que vêm aqui procurando algo diferente. Eu não procurei por "objectURL" para chegar até aqui, esse era o meu ponto antes. Mas eu também peguei você.
Wagner Bertolini Junior 12/02
2

Se você mostrar o arquivo em uma tela de qualquer maneira, também poderá converter o conteúdo da tela em um objeto de 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
fonte
2
Observe que isso não está devolvendo o mesmo arquivo original; está criando um novo arquivo de imagem em tempo real. Quando testei isso pela última vez há alguns anos, descobri que, pelo menos no Chrome, o novo arquivo de imagem não é compactado - eu tinha 10k jpgs transformando-os em 2.5 mb jpgs.
BrianFreud