Existe alguma maneira de especificar um nome de arquivo sugerido ao usar dados: URI?

225

Se, por exemplo, você seguir o link:

data:application/octet-stream;base64,SGVsbG8=

O navegador solicitará que você baixe um arquivo que consiste nos dados mantidos como base64 no próprio hiperlink. Existe alguma maneira de sugerir um nome padrão na marcação? Caso contrário, existe uma solução JavaScript?

tshepang
fonte
talvez não relacionado com esta questão, mas eu sugiro usar o & blob URL.createObjectURL se este não é um servidor ou obstáculo navegador antigo
Sem Fim
3
Alguns navegadores suportam parâmetro "nome" opcional do mediatype:data:application/pdf;name=document.pdf;base64,BASE64_DATA_ENCODED
MEMS
Eu tive o problema com o Firefox pdf.js, que tende a travar em alguns casos, se não puder extrair um nome de arquivo do uri de dados. veja stackoverflow.com/questions/45585921/…
Bernhard
@mems Quais navegadores suportam o parâmetro "name"? Você pode me indicar alguma documentação de referência? (meu google-fu falhou comigo).
TheAddonDepot 25/09
@DimuDesigns Pelo menos o Firefox naquele momento. Parece que não é mais o caso. Ele está relacionado ao tipo de conteúdo MIME (= Content-Disposition!) Parâmetro "nome" (não em RFC?)
MEMS

Respostas:

158

Use o downloadatributo:

<a download='FileName' href='your_url'>

Exemplo vivo no html5-demos.appspot.com / ... .

Atualmente, funciona no Chrome, Firefox, Edge, Opera e Safari de desktop, mas não no iOS Safari ou IE11.

Dan Fabulich
fonte
3
@BioDesign: Funciona mesmo com dados: URI's no chrome. Veja: jsfiddle.net/pYpqW
Sensível
6
mas você não pode fazer isso com window.location.replace. se, por exemplo, você deseja criar um dado: uri ou um gerado por window.URL.createObjectURL, e fazer o download como arquivo, será necessário criar um <a> e clicar nele: jsfiddle.net/flyingsheep/wpQtH (não, $(...).click()não funciona)
ovelha voadora
1
Só se tudo navegador eram como Chrome ... [suspiro]
rua
6
O @flyingsheep $('<a href="data:text/plain,Test" download="test.txt">')[0].click()parece funcionar bem aqui (Chrome 23) (nota: usei o clickmétodo nativo , não o jQuery). Exemplo
Rob W
1
@flyingsheep, parece que eles estão aplicando uma política de mesma origem no Firefox "No Firefox 20, esse atributo só é respeitado por links para recursos com a mesma origem". developer.mozilla.org/pt-BR/docs/Web/HTML/Element/a Nos meus testes, o Chrome não tem essa limitação.
precisa
62

Atualmente, o Chrome simplifica muito isso:

function saveContent(fileContents, fileName)
{
    var link = document.createElement('a');
    link.download = fileName;
    link.href = 'data:,' + fileContents;
    link.click();
}
Holf
fonte
Idk o que todas essas outras respostas estão falando sobre isso funcionou na primeira tentativa no Chrome 30.
Michael J. Calkins
2
Faz agora, mas nem sempre foi tão fácil. Muitas dessas respostas são de anos atrás. E eles também funcionam para outros navegadores.
quer
7
Consulte http://caniuse.com/#feat=download para obter uma lista completa da compatibilidade do navegador.
tixastronauta
2
@tixastronauta: Apesar das informações nessa página, não funcionou no meu firefox 44. Funcionando bem no Chrome. 48
Luis A. Florit
Oi @ Wolf, também existe uma maneira de adicionar o tipo ou extensão de arquivo ou é tão simples quanto possível como nome do arquivo?
Fraccier
51

Apenas HTML: use o downloadatributo:

<a download="logo.gif" href="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">Download transparent png</a>


Somente Javascript: você pode salvar qualquer URI de dados com este código:

function saveAs(uri, filename) {
  var link = document.createElement('a');
  if (typeof link.download === 'string') {
    link.href = uri;
    link.download = filename;

    //Firefox requires the link to be in the body
    document.body.appendChild(link);
    
    //simulate click
    link.click();

    //remove the link when done
    document.body.removeChild(link);
  } else {
    window.open(uri);
  }
}

var file = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
saveAs(file, 'logo.gif');

Chrome, Firefox e Edge 13+ usarão o nome do arquivo especificado.

O IE11, Edge 12 e Safari 9 (que não suportam o downloadatributo ) farão o download do arquivo com o nome padrão ou eles simplesmente o exibirão em uma nova guia, se for do tipo de arquivo suportado: imagens, vídeos, arquivos de áudio ,…

fregante
fonte
Ambos bem demos trabalho para mim no Chrome 38 (mas eles devem trabalhar no Chrome 14+)
fregante
Para uma solução mais completa, eu sugiro usar downloadjsem npm
fregante
Funciona para mim, mas a página do navegador é atualizada depois disso. Gostaria de saber como evitar isso?
1
Não funciona no chrome para tamanho de arquivo> 2 MB devido a restrição por chrome stackoverflow.com/questions/695151/…
Pranav Singh
O limite pertence ao data:URI, que é o que a pergunta menciona. Essa resposta também trabalha com Blobs e tudo aquilo que tem um URI
fregante
40

De acordo com a RFC 2397 , não, não existe.

Também não parece haver nenhum atributo do <a>elemento que você possa usar.

No entanto, o HTML5 introduziu posteriormente o downloadatributo no <a>elemento, embora no momento da gravação o suporte não seja universal (sem suporte ao MSIE, por exemplo)

Alnitak
fonte
9
a segunda frase estava certa no momento da redação, mas não está mais . no momento, ainda não está amplamente implementado.
ovelhas voando
veja este comentário para obter mais informações :)
flying sheep
@flyingsheep, é amplamente implementado.
Pacerier
1
não foi há 3 anos quando escrevi que o comentário
ovelhas voando
Se o arquivo for tão longo, o download falhará
deFreitas 14/10
21

Procurei um pouco nas fontes do firefox em netwerk / protocol / data / nsDataHandler.cpp

manipulador de dados analisa apenas conteúdo / tipo e conjunto de caracteres e verifica se há "; base64" na sequência

o rfc não especifica nome de arquivo e, pelo menos, o Firefox não lida com nome de arquivo, o código gera um nome aleatório mais ".part"

Eu também verifiquei o log do firefox

[b2e140]: DOCSHELL 6e5ae00 InternalLoad data:application/octet-stream;base64,SGVsbG8=
[b2e140]: Found extension '' (filename is '', handling attachment: 0)
[b2e140]: HelperAppService::DoContent: mime 'application/octet-stream', extension ''
[b2e140]: Getting mimeinfo from type 'application/octet-stream' ext ''
[b2e140]: Extension lookup on '' found: 0x0
[b2e140]: Ext. lookup for '' found 0x0
[b2e140]: OS gave back 0x43609a0 - found: 0
[b2e140]: Searched extras (by type), rv 0x80004005
[b2e140]: MIME Info Summary: Type 'application/octet-stream', Primary Ext ''
[b2e140]: Type/Ext lookup found 0x43609a0

arquivos interessantes se você quiser ver fontes do mozilla:

data uri handler: netwerk/protocol/data/nsDataHandler.cpp
where mozilla decides the filename: uriloader/exthandler/nsExternalHelperAppService.cpp
InternalLoad string in the log: docshell/base/nsDocShell.cpp

Acho que você pode parar de procurar uma solução por enquanto, porque suspeito que não exista :)

conforme observado neste tópico, o html5 tem downloadatributo, mas também funciona no firefox 20 http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#attr-hyperlink-download

sherpya
fonte
3
Legal! Embora eu não concorde necessariamente que o Firefox seja a autoridade máxima sobre o que existe. :)
Gleno
14

O seguinte snippet Javascript funciona no Chrome usando o novo atributo 'baixar' dos links e simulando um clique.

function downloadWithName(uri, name) {
  var link = document.createElement("a");
  link.download = name;
  link.href = uri;
  link.click();
}

E o exemplo a seguir mostra seu uso:

downloadWithName("data:,Hello%2C%20World!", "helloWorld.txt")
owencm
fonte
1
Isso não funciona no Firefox, adicionei uma resposta estendida abaixo com compatibilidade com FX.
fregante
12

Não.

O objetivo todo é que seja um fluxo de dados, não um arquivo. A fonte de dados não deve ter nenhum conhecimento do agente do usuário que a manipula como um arquivo ... e não.

Raças de leveza em órbita
fonte
6
O objetivo data:é falsificar um bloco de dados internos no formato de URL sem precisar lê- lo de uma fonte baseada em protocolo. O link na resposta do @ silex mostra que a capacidade de sugerir um nome preferido para o escrever é considerada útil, mesmo que ainda não esteja implementada.
Alnitak
1
@Alnitak: Útil? Absolutamente. Tecnicamente apropriado? Ainda não está convencido. :)
Lightness Races in Orbit
3
O @Tomalak considera a diferença entre carregar e salvar os dados - apenas porque um blob é codificado em linha nos dados: URL não significa que ele não deve ter um nome preferido para salvá-lo.
Alnitak
4
Mas sua opinião sobre seu "propósito completo" está errada. data:foi inventado especificamente para permitir que o conteúdo interno (pequeno) apareça em um formato de URL falsificado, para que pudesse ser usado por coisas como tags de imagem sem uma solicitação HTTP separada. O HTML diz que o conteúdo de um img srcatributo deve ser um URL, e foi isso que o RFC 2397 criou. Não há "fonte de dados".
Alnitak
6
@ Alnitak: Exatamente. Não há fonte de dados. Não há contexto. O URI são os dados.
Corridas de leveza em órbita
9

você pode adicionar um atributo de download ao elemento âncora.

amostra:

<a download="abcd.cer"
    href="data:application/stream;base64,MIIDhTC......">down</a>
cuixiping
fonte
5

Veja este link: http://lists.w3.org/Archives/Public/uri/2010Feb/0069.html

Citar:

Até funciona (como em, não causa nenhum problema) com; base64 no final
desta forma (pelo menos no Opera):

data: text / plain; charset = utf-8; headers = Content-Disposition% 3A% 20attachment% 3B% 20filename% 3D% 22with% 20spaces.txt% 22% 0D% 0Documentação% 3A% 20en; base64,4oiaDQo% 3D

Também há algumas informações nas mensagens restantes da discussão.

silex
fonte
infelizmente isso não é baixado.
James Khoury
7
esta discussão foi para uma extensão proposta para o formato URI de dados - ela não foi implementada.
Alnitak 30/05
Implementado ou não, com o suporte existente para parâmetros arbitrários, isso seria ótimo.
quer
5

Usando trabalhadores de serviço , isso é finalmente possível no sentido mais verdadeiro.

  1. Crie um URL falso. Por exemplo /saveAs/myPrettyName.jpg
  2. Use URL <a href, <img src, window.open (url), absolutamente tudo o que pode ser feito com um URL "real".
  3. Dentro do trabalhador, capture o evento de busca e responda com os dados corretos.

O navegador agora sugere myPrettyName.jpg mesmo que o usuário abra o arquivo em uma nova guia e tente salvá-lo lá. Será exatamente como se o arquivo tivesse vindo do servidor.

// In the service worker
self.addEventListener( 'fetch', function(e)
{
    if( e.request.url.startsWith( '/blobUri/' ) )
    {
        // Logic to select correct dataUri, and return it as a Response
        e.respondWith( dataURLAsRequest );
    }
});
Adria
fonte
1
Interessante! Apoio parece ser bastante rasa para agora, embora: caniuse.com/#feat=serviceworkers
tuomassalo
Existe alguma maneira de "responder" com outro URL direto para um arquivo?
Iulian Onofrei
4

Há um pequeno script de solução alternativa no Google Code que funcionou para mim:

http://code.google.com/p/download-data-uri/

Ele adiciona um formulário com os dados, o envia e remove o formulário novamente. Hacky, mas fez o trabalho para mim. Requer jQuery.

Esse tópico apareceu no Google antes da página do Google Code e achei que seria útil ter o link aqui também.

Fabian B.
fonte
Script interessante, mas exige que o servidor obtenha a resposta e envie-a de volta, certo? jsfiddle.net/hZySf
James Khoury
Não tenho certeza de onde o arquivo está sendo gerado. Esse arquivo está sendo armazenado na codificação base64? (Eu não estou muito familiarizado com base64)
streetlight
@streetlight: O "arquivo" (ou seja, dados) é gerado por Javascript. O contexto desse projeto (e provavelmente a maioria aqui) assume que você tem alguma maneira de obter os dados desejados em uma variável JS. A diferença é que, em vez de apresentá-lo ao usuário por meio de um data:...URI, esse script cria um formulário para enviá-lo ao servidor. E o servidor provavelmente o repete como uma resposta HTTP "download" (ou seja, com um cabeçalho de Disposição de Conteúdo apropriado especificando o nome do arquivo).
Andrzej Doyle
4

Aqui está uma versão do jQuery baseada na versão de Holf e funciona com Chrome e Firefox, enquanto sua versão parece funcionar apenas com o Chrome. É um pouco estranho adicionar algo ao corpo para fazer isso, mas se alguém tiver uma opção melhor, eu sou a favor.

var exportFileName = "export-" + filename;
$('<a></a>', {
    "download": exportFileName,
    "href": "data:," + JSON.stringify(exportData, null,5),
    "id": "exportDataID"
}).appendTo("body")[0].click().remove();
kgividen
fonte
1
Com o jQuery 1.11, recebo uma exceção por causa do .remove (). Eu tenho em torno deste, atribuindo $().appendTo()a uma variável, em seguida, chamandovariable.click(); variable.remove()
p0lar_bear
@ p0lar_bear, você deve obter essa exceção com qualquer jQuery, porque a obtenção [0]de qualquer "elemento jQuery" deve retornar o primeiro elemento DOM que ele representa, o que essencialmente "tira você de" jQuery.
drzaus
Você realmente não precisa acrescentar / remover o elemento - veja os comentários em stackoverflow.com/a/17311705/1037948
drzaus
3

É meio brincalhão, mas eu já estive na mesma situação antes. Eu estava gerando dinamicamente um arquivo de texto em javascript e queria fornecê-lo para download, codificando-o com o URI de dados.

Isso é possível com uma intervenção menor do usuário principal. Gere um link <a href="data:...">right-click me and select "Save Link As..." and save as "example.txt"</a>. Como eu disse, isso é deselegante, mas funciona se você não precisar de uma solução profissional.

Isso pode ser menos doloroso usando o flash para copiar o nome para a área de transferência primeiro. Obviamente, se você se permitir usar Flash ou Java (agora com menos e menos suporte a navegador, eu acho?), Provavelmente poderá encontrar outra maneira de fazer isso.

ninjagecko
fonte
Esta não é uma solução e não atende ao que foi solicitado. Desculpe.
precisa saber é o seguinte
7
Lol @ "intervenção menor do usuário". Conseguir que o usuário faça a coisa toda por você não é uma "intervenção menor do usuário".
Lightness Races em Órbita
Combine isso com stackoverflow.com/questions/17311645/… para acionar o link gerado e você não precisa de intervenção do usuário. Você pode especificar o atributo HTML5download para sugerir um nome, como mencionado em muitas outras respostas .
#
Esta é uma ótima solução alternativa para o Safari. Use Modernizr para detectar quando o atributo de download não é suportado e atualize o texto do link!
Littledynamo 31/05
2

Este funciona com o Firefox 43.0 (antigo não testado):

dl.js:

function download() {
  var msg="Hello world!";
  var blob = new File([msg], "hello.bin", {"type": "application/octet-stream"});

  var a = document.createElement("a");
  a.href = URL.createObjectURL(blob);

  window.location.href=a;
}

dl.html

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">

<head>
    <meta charset="utf-8"/>
    <title>Test</title>
    <script type="text/javascript" src="dl.js"></script>
</head>

<body>
<button id="create" type="button" onclick="download();">Download</button>
</body>
</html>

Se clicar no botão, ele oferece um arquivo chamado hello.bin para download. O truque é usar o arquivo em vez do Blob .

referência: https://developer.mozilla.org/de/docs/Web/API/File

NeutronenStern
fonte
0
var isIE = /*@cc_on!@*/false || !!document.documentMode; // At least IE6
var sessionId ='\n';
var token = '\n';
var caseId = CaseIDNumber + '\n';
var url = casewebUrl+'\n';
var uri = sessionId + token + caseId + url;//data in file
var fileName = "file.i4cvf";// any file name with any extension
if (isIE)
    {
            var fileData = ['\ufeff' + uri];
            var blobObject = new Blob(fileData);
            window.navigator.msSaveOrOpenBlob(blobObject, fileName);
    }
    else //chrome
    {
        window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
         window.requestFileSystem(window.TEMPORARY, 1024 * 1024, function (fs) {
            fs.root.getFile(fileName, { create: true }, function (fileEntry) { 
                fileEntry.createWriter(function (fileWriter) {
                    var fileData = ['\ufeff' + uri];
                    var blob = new Blob(fileData);
                    fileWriter.addEventListener("writeend", function () {
                        var fileUrl = fileEntry.toURL();
                        var link = document.createElement('a');
                        link.href = fileUrl;
                        link.download = fileName;
                        document.body.appendChild(link);
                        link.click();
                        document.body.removeChild(link);
                    }, false);
                    fileWriter.write(blob);
                }, function () { });
            }, function () { });
         }, function () { });
    }
Sushama Pradhan
fonte
1
pls adicionar uma explicação mais detalhada a sua resposta - stackoverflow.com/help/how-to-answer
Sebastian Brosch
1
esta resposta é lixo
Jonathan Taylor
-2

Você pode conseguir isso no Chrome e no FireFox.

Tente o seguinte URL, ele fará o download do código que foi usado.

data:text/html;base64,PGEgaHJlZj0iZGF0YTp0ZXh0L2h0bWw7YmFzZTY0LFBHRWdhSEpsWmowaVVGVlVYMFJCVkVGZlZWSkpYMGhGVWtVaUlHUnZkMjVzYjJGa1BTSjBaWE4wTG1oMGJXd2lQZ284YzJOeWFYQjBQZ3BrYjJOMWJXVnVkQzV4ZFdWeWVWTmxiR1ZqZEc5eUtDZGhKeWt1WTJ4cFkyc29LVHNLUEM5elkzSnBjSFErIiBkb3dubG9hZD0idGVzdC5odG1sIj4KPHNjcmlwdD4KZG9jdW1lbnQucXVlcnlTZWxlY3RvcignYScpLmNsaWNrKCk7Cjwvc2NyaXB0Pg==
Chad Scira
fonte