¿Hay alguna forma de especificar un nombre de archivo sugerido cuando se usan datos: URI?

225

Si por ejemplo sigues el enlace:

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

El navegador le pedirá que descargue un archivo que consta de los datos que se mantienen como base64 en el propio hipervínculo. ¿Hay alguna forma de sugerir un nombre predeterminado en el marcado? Si no, ¿hay una solución de JavaScript?

tshepang
fuente
tal vez no esté relacionado con este problema, pero sugiero usar blob's & URL.createObjectURL si este no es un servidor o un obstáculo de navegador antiguo
Endless
3
Algunos navegadores admiten el parámetro opcional "nombre" del tipo de medio:data:application/pdf;name=document.pdf;base64,BASE64_DATA_ENCODED
mems
Tuve el problema con Firefox pdf.js que tiende a colgarse en algunos casos si no puede extraer un nombre de archivo de la uri de datos. ver stackoverflow.com/questions/45585921/…
Bernhard
@mems ¿Qué navegadores admiten el parámetro "nombre"? ¿Me puede indicar alguna documentación de referencia? (mi google-fu me ha fallado).
TheAddonDepot
@DimuDesigns Al menos Firefox en ese momento. Parece que ya no es el caso. Está relacionado con el parámetro MIME Content-Type (! = Content-Disposition) "name" (¿no está en RFC?)
mems

Respuestas:

158

Usa el downloadatributo:

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

Ejemplo vivo por html5-demos.appspot.com / ... .

Actualmente funciona en Chrome, Firefox, Edge, Opera y Safari de escritorio, pero no iOS Safari o IE11.

Dan Fabulich
fuente
3
@BioDesign: Funciona incluso con datos: URI en cromo. Ver: jsfiddle.net/pYpqW
Senseful
66
pero no puedes hacerlo con window.location.replace. si, por ejemplo, desea crear un dato: uri o uno generado por window.URL.createObjectURL, y descargarlo como archivo, deberá crear un <a> y hacer clic en él: jsfiddle.net/flyingsheep/wpQtH (no, $(...).click()no funciona)
ovejas voladoras
1
Solo si todos los navegadores fueran como Chrome ... [suspiro]
streetlight
66
@flyingsheep $('<a href="data:text/plain,Test" download="test.txt">')[0].click()parece funcionar bien aquí (Chrome 23) (nota: utilicé el clickmétodo nativo , no el de jQuery). Demostración: jsfiddle.net/2zsRW
Rob W
1
@flyingsheep parece que están aplicando una política del mismo origen en Firefox "En Firefox 20, este atributo solo se respeta por los enlaces a recursos con el mismo origen". developer.mozilla.org/en-US/docs/Web/HTML/Element/a En mis pruebas, Chrome no tiene esta limitación.
William Denniss
62

Chrome hace esto muy simple en estos días:

function saveContent(fileContents, fileName)
{
    var link = document.createElement('a');
    link.download = fileName;
    link.href = 'data:,' + fileContents;
    link.click();
}
Holf
fuente
No sé de qué hablan todas estas otras respuestas sobre esto funcionó en el primer intento en Chrome 30.
Michael J. Calkins
2
Ahora sí, pero no siempre fue tan fácil. Muchas de estas respuestas son de hace años. Y también funcionan para otros navegadores.
Holf
77
Consulte http://caniuse.com/#feat=download para obtener una lista completa de compatibilidad del navegador.
tixastronauta
2
@tixastronauta: a pesar de la información en esa página, no funciona en mi firefox 44. Funciona muy bien en Chrome. 48
Luis A. Florit
Hola @Holf, ¿hay alguna manera de agregar también el tipo o extensión de archivo o es tan simple como especificarlo como nombre de archivo?
Fraccier
51

Solo HTML: use el downloadatributo:

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


Solo Javascript: puede guardar cualquier URI de datos con 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 y Edge 13+ usarán el nombre de archivo especificado.

IE11, Edge 12 y Safari 9 (que no admiten el downloadatributo ) descargarán el archivo con su nombre predeterminado o simplemente lo mostrarán en una nueva pestaña, si es de un tipo de archivo compatible: imágenes, videos, archivos de audio , ...

fregante
fuente
Ambas demostraciones funcionan bien para mí en Chrome 38 (pero deberían funcionar en Chrome 14+)
fregante
Para una solución más completa, sugiero usar downloadjsen npm
fregante
Funciona para mí, pero la página del navegador se actualiza después de eso. ¿Se pregunta cómo evitar eso?
1
No funciona en Chrome para tamaño de archivo> 2 MB debido a la restricción de Chrome stackoverflow.com/questions/695151/…
Pranav Singh
El límite pertenece al data:URI, que es lo que menciona la pregunta. Esta respuesta también funciona con Blobs y cualquier otra cosa que tenga un URI
fregante
40

Según RFC 2397 , no, no lo hay.

Tampoco parece haber ningún atributo del <a>elemento que pueda usar.

Sin embargo, HTML5 ha introducido posteriormente el downloadatributo en el <a>elemento, aunque en el momento de la escritura, el soporte no es universal (no hay soporte de MSIE, por ejemplo)

Alnitak
fuente
99
la segunda oración era correcta al momento de escribir, pero ya no lo es . a partir de ahora, sin embargo, aún no se ha implementado ampliamente.
ovejas voladoras
mira este comentario para más información :)
ovejas voladoras
@flyingsheep, está ampliamente implementado.
Pacerier
1
no fue hace 3 años cuando escribí ese comentario
ovejas voladoras el
Si el archivo es tan largo, la descarga falla
deFreitas
21

He buscado un poco en las fuentes de Firefox en netwerk / protocol / data / nsDataHandler.cpp

el manejador de datos solo analiza el contenido / tipo y el conjunto de caracteres, y busca si hay "; base64" en la cadena

el rfc no especifica nombre de archivo y al menos firefox no maneja ningún nombre de archivo para él, el código genera un nombre aleatorio más ".part"

También revisé el registro de 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

archivos interesantes si quieres ver las fuentes de 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

Creo que puedes dejar de buscar una solución por ahora, porque sospecho que no hay ninguna :)

como se observó en este hilo, html5 tiene downloadatributo, también funciona en firefox 20 http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#attr-hyperlink-download

Sherpya
fuente
3
¡Frio! Aunque no estoy necesariamente de acuerdo en que Firefox es la máxima autoridad en lo que existe. :)
Gleno
14

El siguiente fragmento de Javascript funciona en Chrome mediante el uso del nuevo atributo 'descargar' de enlaces y simulando un clic.

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

Y el siguiente ejemplo muestra su uso:

downloadWithName("data:,Hello%2C%20World!", "helloWorld.txt")
owencm
fuente
1
Esto no funciona en Firefox, agregué una respuesta extendida a continuación con compatibilidad Fx.
Fregante
12

No.

El objetivo es que sea un flujo de datos, no un archivo. La fuente de datos no debe tener ningún conocimiento del agente de usuario que lo maneja como un archivo ... y no lo hace.

Carreras de ligereza en órbita
fuente
66
El propósito de data:es falsificar un bloque de datos internos en formato URL sin tener que leerlo desde una fuente basada en el protocolo. El enlace en la respuesta de @ silex muestra que la capacidad de sugerir un nombre preferido para escribirlo se considera útil, incluso si aún no está implementado.
Alnitak
1
@Alnitak: ¿Útil? Absolutamente. ¿Técnicamente apropiado? Todavía no está convencido. :)
ligereza corre en órbita el
3
@Tomalak considera la diferencia entre cargar los datos y guardarlos, solo porque un blob está codificado en línea en un dato: la URL no significa que no deba tener un nombre preferido para guardarlo.
Alnitak
44
Pero su línea sobre su "propósito completo" está equivocada. data:se inventó específicamente para permitir que el contenido en línea (pequeño) aparezca en un formato de URL falsificado para que pueda ser utilizado por cosas como etiquetas de imagen sin una solicitud HTTP separada. HTML dice que el contenido de un img srcatributo debe ser una URL, así que eso es lo que creó RFC 2397. No hay "fuente de datos".
Alnitak
66
@Alnitak: Exactamente. No hay fuente de datos. No hay contexto El URI son los datos.
Luminosidad razas en órbita
9

puede agregar un atributo de descarga al elemento de anclaje.

muestra:

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

Mire este enlace: http://lists.w3.org/Archives/Public/uri/2010Feb/0069.html

Citar:

Incluso funciona (como en, no causa un problema) con; base64 al final
así (al menos en Opera):

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

También hay algo de información en los mensajes restantes de la discusión.

sílex
fuente
lamentablemente esto no se descarga.
James Khoury
77
Esta discusión fue para una extensión propuesta al formato URI de datos, no se ha implementado.
Alnitak
Implementado o no, con soporte existente para parámetros arbitrarios, esto sería genial.
Dan Lugg
5

Utilizando trabajadores de servicio , esto finalmente es posible en el sentido más verdadero.

  1. Crea una URL falsa. Por ejemplo /saveAs/myPrettyName.jpg
  2. Use URL en <a href, <img src, window.open (url), absolutamente cualquier cosa que pueda hacerse con una URL "real".
  3. Dentro del trabajador, capture el evento de búsqueda y responda con los datos correctos.

El navegador ahora sugerirá myPrettyName.jpg incluso si el usuario abre el archivo en una nueva pestaña e intenta guardarlo allí. Será exactamente como si el archivo hubiera venido del 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
fuente
1
¡Interesante! Sin embargo, el soporte parece ser bastante superficial por ahora: caniuse.com/#feat=serviceworkers
tuomassalo
¿Hay alguna forma de "responder" con otra url directa a un archivo?
Iulian Onofrei
4

Hay un pequeño script de solución en Google Code que funcionó para mí:

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

Agrega un formulario con los datos, lo envía y luego elimina el formulario nuevamente. Hacky, pero hizo el trabajo por mí. Requiere jQuery.

Este hilo apareció en Google antes de la página de Google Code y pensé que podría ser útil tener el enlace aquí también.

Fabian B.
fuente
Script interesante, pero requiere que el servidor obtenga la respuesta y la envíe de vuelta, ¿verdad? jsfiddle.net/hZySf
James Khoury
No estoy seguro de dónde se genera el archivo ... ¿se está almacenando ese archivo en la codificación base64? (No estoy muy familiarizado con base64)
farola
@streetlight: el "archivo" (es decir, los datos) es generado por Javascript. El contexto de ese proyecto (y probablemente la mayoría aquí) supone que tiene alguna forma de obtener sus datos deseados en una variable JS. La diferencia es que, en lugar de presentarlo al usuario a través de un data:...URI, ese script crea un formulario para PUBLICARLO en el servidor. Y, presumiblemente, el servidor lo repite directamente como una respuesta de "descarga" HTTP (es decir, con un encabezado de Disposición de contenido apropiado que especifique el nombre del archivo).
Andrzej Doyle
4

Aquí hay una versión jQuery basada en la versión de Holf y funciona con Chrome y Firefox, mientras que su versión parece funcionar solo con Chrome. Es un poco extraño agregar algo al cuerpo para hacer esto, pero si alguien tiene una mejor opción, estoy 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
fuente
1
Con jQuery 1.11 obtengo una excepción debido a .remove (). $().appendTo()variable.click(); variable.remove()
Resolví
@ p0lar_bear debería obtener esa excepción con cualquier jQuery, porque obtener [0]de cualquier "elemento jQuery" debería devolver el primer elemento DOM que representa, que esencialmente "lo saca de" jQuery.
drzaus
En realidad, no debería necesitar agregar / eliminar el elemento en absoluto - vea los comentarios en stackoverflow.com/a/17311705/1037948
drzaus
3

Es un poco hack, pero he estado en la misma situación antes. Estaba generando dinámicamente un archivo de texto en javascript y quería proporcionarlo para su descarga codificándolo con el URI de datos.

Esto es posible con una intervención menor del usuario mayor. Genera un enlace <a href="data:...">right-click me and select "Save Link As..." and save as "example.txt"</a>. Como dije, esto no es elegante, pero funciona si no necesita una solución profesional.

Esto podría hacerse menos doloroso al usar flash para copiar primero el nombre en el portapapeles. Por supuesto, si te permites usar Flash o Java (¿creo que ahora con menos soporte para el navegador?), Probablemente puedas encontrar otra forma de hacerlo.

ninjagecko
fuente
Esta no es una solución y no cumple con lo solicitado. Lo siento.
jcolebrand 05 de
77
Lol @ "intervención menor del usuario". Lograr que el usuario haga todo por usted no es "intervención menor del usuario".
Carreras de ligereza en órbita
Combine esto con stackoverflow.com/questions/17311645/… para activar el enlace generado y no necesita la intervención del usuario. Puede especificar el atributo HTML5download para sugerir un nombre como lo mencionan muchas otras respuestas .
drzaus
Esta es una gran solución para Safari. ¡Utilice Modernizr para detectar cuándo el atributo de descarga no es compatible y actualice el texto del enlace!
littledynamo
2

Este funciona con Firefox 43.0 (anterior no probado):

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>

Si se hace clic en el botón, se ofrece un archivo llamado hello.bin para descargar. El truco es usar Archivo en lugar de Blob .

referencia: https://developer.mozilla.org/de/docs/Web/API/File

NeutronenStern
fuente
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
fuente
1
pls añadir una explicación más detallada de su respuesta - stackoverflow.com/help/how-to-answer
Sebastián Brosch
1
esta respuesta es basura
Jonathan Taylor
-2

De hecho, puede lograr esto, en Chrome y Firefox.

Pruebe la siguiente URL, descargará el código que se utilizó.

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