Existe-t-il un moyen de spécifier un nom de fichier suggéré lors de l'utilisation de données: URI?

225

Si par exemple vous suivez le lien:

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

Le navigateur vous invitera à télécharger un fichier composé des données contenues en base64 dans l'hyperlien lui-même. Existe-t-il un moyen de suggérer un nom par défaut dans le balisage? Sinon, existe-t-il une solution JavaScript?

tshepang
la source
peut-être pas lié à ce problème, mais je suggère d'utiliser le blob's & URL.createObjectURL s'il ne s'agit pas d'un serveur ou d'un ancien obstacle de navigateur
Endless
3
Certains navigateurs prennent en charge le paramètre facultatif "nom" du data:application/pdf;name=document.pdf;base64,BASE64_DATA_ENCODED
médiatype
J'ai eu le problème avec Firefox pdf.js qui a tendance à se bloquer dans certains cas s'il ne peut pas extraire un nom de fichier de l'URI de données. voir stackoverflow.com/questions/45585921/…
Bernhard
@mems Quels navigateurs prennent en charge le paramètre "nom"? Pouvez-vous m'indiquer une documentation de référence? (mon google-fu m'a échoué).
TheAddonDepot
@DimuDesigns Au moins Firefox à ce moment-là. On dirait que ce n'est plus le cas. Il est lié au paramètre "nom" MIME Content-Type (! = Content-Disposition) (pas dans RFC?)
mems

Réponses:

158

Utilisez l' downloadattribut:

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

Exemple en direct sur html5-demos.appspot.com / ... .

Fonctionne actuellement sur Chrome, Firefox, Edge, Opera et Safari de bureau, mais pas iOS Safari ou IE11.

Dan Fabulich
la source
3
@BioDesign: Cela fonctionne même avec les données: les URI sont en chrome. Voir: jsfiddle.net/pYpqW
Senseful
6
mais vous ne pouvez pas le faire avec window.location.replace. par exemple, si vous souhaitez créer une donnée: uri ou générée par window.URL.createObjectURL, et la télécharger en tant que fichier, vous devrez créer un <a> et cliquer dessus: jsfiddle.net/flyingsheep/wpQtH (non, $(...).click()ne fonctionne pas)
mouton volant
1
Seulement si tous les navigateurs étaient comme Chrome ... [soupir]
réverbère
6
@flyingsheep $('<a href="data:text/plain,Test" download="test.txt">')[0].click()semble bien fonctionner ici (Chrome 23) (note: j'ai utilisé la clickméthode native , pas celle de jQuery). Démo: jsfiddle.net/2zsRW
Rob W
1
@flyingsheep il semble qu'ils appliquent une politique de même origine dans Firefox "Dans Firefox 20, cet attribut n'est honoré que pour les liens vers des ressources de même origine." developer.mozilla.org/en-US/docs/Web/HTML/Element/a Dans mes tests, Chrome n'a pas cette limitation.
William Denniss
62

Chrome rend cela très simple de nos jours:

function saveContent(fileContents, fileName)
{
    var link = document.createElement('a');
    link.download = fileName;
    link.href = 'data:,' + fileContents;
    link.click();
}
Holf
la source
Je veux savoir ce que toutes ces autres réponses en disent, cela a fonctionné lors du premier essai dans Chrome 30.
Michael J. Calkins
2
C'est le cas maintenant, mais cela n'a pas toujours été aussi facile. Beaucoup de ces réponses remontent à des années. Et ils fonctionnent également pour d'autres navigateurs.
Holf
7
Reportez-vous à http://caniuse.com/#feat=download pour une liste complète de la compatibilité du navigateur.
tixastronauta
2
@tixastronauta: Malgré les informations de cette page, ne fonctionne pas dans mon firefox 44. Fonctionne bien dans Chrome. 48
Luis A. Florit
Salut @Holf est-il également possible d'ajouter le type de fichier ou l'extension ou son aussi simple que spceficy comme nom de fichier?
Fraccier
51

HTML uniquement: utilisez l' downloadattribut:

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


Javascript uniquement: vous pouvez enregistrer n'importe quelle URI de données avec ce code:

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 et Edge 13+ utiliseront le nom de fichier spécifié.

IE11, Edge 12 et Safari 9 (qui ne prennent pas en charge l' downloadattribut ) téléchargeront le fichier avec leur nom par défaut ou ils l'afficheront simplement dans un nouvel onglet, s'il s'agit d'un type de fichier pris en charge: images, vidéos, fichiers audio ,…

fregante
la source
Les deux démos fonctionnent bien pour moi dans Chrome 38 (mais elles devraient fonctionner dans Chrome 14+)
fregante
Pour une solution plus complète, je suggère d'utiliser downloadjssur npm
fregante
Cela fonctionne pour moi, mais la page du navigateur se rafraîchit après cela. Vous vous demandez comment empêcher cela?
1
Ne fonctionne pas en chrome pour une taille de fichier> 2 Mo due à la restriction par chrome stackoverflow.com/questions/695151/…
Pranav Singh
La limite appartient à l' data:URI, ce que mentionne la question. Cette réponse fonctionne également avec les blobs et tout ce qui a un URI
fregante
40

Selon la RFC 2397 , non, il n'y en a pas.

Il ne semble pas non plus y avoir d' attribut de l' <a>élément que vous puissiez utiliser non plus.

Cependant HTML5 a par la suite introduit l' downloadattribut sur l' <a>élément, bien qu'au moment de la rédaction le support ne soit pas universel (pas de support MSIE, par exemple)

Alnitak
la source
9
la deuxième phrase était juste au moment de la rédaction, mais ne l'est plus . pour l'instant, il n'est pas encore largement mis en œuvre.
vol de moutons
voir ce commentaire pour plus d'infos :)
moutons volants
@flyingsheep, Il est largement mis en œuvre.
Pacerier
1
il n'y a pas 3 ans quand j'ai écrit ce commentaire
voler des moutons
Si le fichier est si long, le téléchargement échoue
deFreitas
21

J'ai regardé un peu dans les sources de Firefox dans netwerk / protocol / data / nsDataHandler.cpp

le gestionnaire de données analyse uniquement le contenu / type et le jeu de caractères et recherche s'il y a "; base64" dans la chaîne

le rfc ne spécifie aucun nom de fichier et au moins firefox ne gère aucun nom de fichier pour cela, le code génère un nom aléatoire plus ".part"

J'ai également vérifié le journal 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

fichiers intéressants si vous voulez regarder les sources 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

Je pense que vous pouvez arrêter de chercher une solution pour l'instant, car je pense qu'il n'y en a pas :)

comme remarqué dans ce thread, html5 a un downloadattribut, il fonctionne également sur firefox 20 http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#attr-hyperlink-download

sherpya
la source
3
Cool! Bien que je ne convienne pas nécessairement que Firefox soit l'autorité ultime sur ce qui existe. :)
Gleno
14

L'extrait Javascript suivant fonctionne dans Chrome en utilisant le nouvel attribut "télécharger" des liens et en simulant un clic.

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

Et l'exemple suivant montre son utilisation:

downloadWithName("data:,Hello%2C%20World!", "helloWorld.txt")
Owencm
la source
1
Cela ne fonctionne pas dans Firefox, j'ai ajouté une réponse étendue ci-dessous avec la compatibilité Fx.
fregante
12

Non.

Le but est que c'est un flux de données, pas un fichier. La source de données ne doit pas avoir connaissance de l'agent utilisateur qui le gère comme un fichier ... et ce n'est pas le cas.

Courses de légèreté en orbite
la source
6
Le but de data:est de truquer un bloc de données internes au format URL sans avoir à le lire à partir d'une source basée sur un protocole. Le lien dans la réponse de @ silex montre que la possibilité de suggérer un nom préféré pour l' écrire est considérée comme utile, même si elle n'est pas encore implémentée.
Alnitak
1
@Alnitak: utile? Absolument. Techniquement approprié? Toujours pas convaincu. :)
Courses de légèreté en orbite le
3
@Tomalak considère la différence entre charger les données et les enregistrer - simplement parce qu'un blob est encodé en ligne dans une donnée: URL ne signifie pas qu'il ne devrait pas avoir de nom préféré pour l'enregistrer.
Alnitak
4
Mais votre argument à propos de son «objectif complet» est faux. data:a été spécifiquement inventé pour permettre au contenu en ligne (petit) d'apparaître dans un format d'URL fudged ensemble afin qu'il puisse être utilisé par des choses comme les balises d'image sans demande HTTP distincte. HTML dit que le contenu d'un img srcattribut doit être une URL, c'est donc ce que la RFC 2397 a créé. Il n'y a pas de "source de données".
Alnitak
6
@Alnitak: Exactement. Il n'y a pas de source de données. Il n'y a pas de contexte. L'URI est les données.
Courses de légèreté en orbite le
9

vous pouvez ajouter un attribut de téléchargement à l'élément d'ancrage.

échantillon:

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

Regardez ce lien: http://lists.w3.org/Archives/Public/uri/2010Feb/0069.html

Citation:

Cela fonctionne même (comme dans, ne cause pas de problème) avec; base64 à la fin
comme ceci (dans Opera au moins):

données: 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

Il y a aussi quelques informations dans les autres messages de la discussion.

silex
la source
malheureusement, cela ne se télécharge pas.
James Khoury
7
cette discussion portait sur une proposition d' extension au format URI de données - elle n'a pas été implémentée.
Alnitak
Mis en œuvre ou non, avec la prise en charge existante de paramètres arbitraires, ce serait formidable.
Dan Lugg
5

En utilisant des travailleurs de service , cela est enfin possible dans le vrai sens du terme.

  1. Créez une fausse URL. Par exemple /saveAs/myPrettyName.jpg
  2. Utilisez URL dans <a href, <img src, window.open (url), absolument tout ce qui peut être fait avec une "vraie" URL.
  3. À l'intérieur du travailleur, capturez l'événement d'extraction et répondez avec les données correctes.

Le navigateur proposera désormais myPrettyName.jpg même si l'utilisateur ouvre le fichier dans un nouvel onglet et essaie de l'enregistrer. Ce sera exactement comme si le fichier venait du serveur.

// 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
la source
1
Intéressant! Cependant, le support semble être assez superficiel pour l'instant: caniuse.com/#feat=serviceworkers
tuomassalo
Existe-t-il un moyen de "répondre" avec une autre URL directe vers un fichier?
Iulian Onofrei
4

Il existe un petit script de contournement sur Google Code qui a fonctionné pour moi:

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

Il ajoute un formulaire avec les données qu'il contient, le soumet, puis supprime à nouveau le formulaire. Hacky, mais il a fait le travail pour moi. Nécessite jQuery.

Ce fil est apparu dans Google avant la page de code Google et j'ai pensé qu'il pourrait être utile d'avoir le lien ici aussi.

Fabian B.
la source
Script intéressant mais il nécessite que le serveur obtienne la réponse et la renvoie, n'est-ce pas? jsfiddle.net/hZySf
James Khoury
Je ne sais pas d'où le fichier est généré. Ce fichier est-il stocké dans le codage base64? (Je ne connais pas trop le base64)
lampadaire
@streetlight: Le "fichier" (c'est-à-dire les données) est généré par Javascript. Le contexte de ce projet (et probablement le plus ici) suppose que vous avez un moyen d'obtenir vos données souhaitées dans une variable JS. La différence est qu'au lieu de le présenter à l'utilisateur via un data:...URI, ce script crée un formulaire pour le POSTER sur le serveur. Et le serveur se fait alors probablement l'écho directement en tant que réponse de "téléchargement" HTTP (c'est-à-dire avec un en-tête Content-Disposition approprié spécifiant le nom de fichier).
Andrzej Doyle
4

Voici une version jQuery basée sur la version de Holf et fonctionne avec Chrome et Firefox alors que sa version ne semble fonctionner qu'avec Chrome. C'est un peu étrange d'ajouter quelque chose au corps pour le faire, mais si quelqu'un a une meilleure option, je suis tout à fait d'accord.

var exportFileName = "export-" + filename;
$('<a></a>', {
    "download": exportFileName,
    "href": "data:," + JSON.stringify(exportData, null,5),
    "id": "exportDataID"
}).appendTo("body")[0].click().remove();
kgividen
la source
1
Avec jQuery 1.11, j'obtiens une exception à cause de .remove (). J'ai contourné cela en attribuant $().appendTo()à une variable puis en appelantvariable.click(); variable.remove()
p0lar_bear
@ p0lar_bear, vous devriez obtenir cette exception avec n'importe quel jQuery, car le fait d'obtenir [0]de n'importe quel "élément jQuery" devrait renvoyer le premier élément DOM qu'il représente, ce qui "vous fait sortir" de jQuery.
drzaus
En fait, vous ne devriez pas du tout avoir besoin d'ajouter / supprimer l'élément - voir les commentaires sur stackoverflow.com/a/17311705/1037948
drzaus
3

C'est un peu hackish, mais j'ai déjà été dans la même situation. Je générais dynamiquement un fichier texte en javascript et je voulais le fournir en téléchargement en le codant avec l'URI de données.

Cela est possible avec une intervention mineure majeure de l'utilisateur. Générer un lien<a href="data:...">right-click me and select "Save Link As..." and save as "example.txt"</a> . Comme je l'ai dit, cela n'est pas élégant, mais cela fonctionne si vous n'avez pas besoin d'une solution professionnelle.

Cela pourrait être rendu moins douloureux en utilisant Flash pour copier le nom dans le presse-papiers en premier. Bien sûr, si vous vous permettez d'utiliser Flash ou Java (maintenant avec de moins en moins de support de navigateur je pense?), Vous pourriez probablement trouver une autre façon de le faire.

ninjagecko
la source
Ce n'est pas une solution et ne répond pas à ce qui était demandé. Désolé.
jcolebrand
7
Lol @ "intervention mineure de l'utilisateur". Faire en sorte que l'utilisateur fasse tout cela pour vous n'est pas une "intervention mineure de l'utilisateur".
Courses de légèreté en orbite le
Combinez cela avec stackoverflow.com/questions/17311645/… pour déclencher le lien généré et vous n'avez pas besoin de l'intervention de l'utilisateur. Vous pouvez spécifier l' attribut HTML5download pour suggérer un nom comme mentionné par de nombreuses autres réponses .
drzaus
Il s'agit d'une excellente solution de contournement pour Safari. Utilisez Modernizr pour détecter quand l'attribut de téléchargement n'est pas pris en charge et mettez à jour le texte du lien!
littledynamo
2

Celui-ci fonctionne avec Firefox 43.0 (ancien non testé):

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 le bouton est cliqué, il propose un fichier nommé hello.bin à télécharger. L'astuce consiste à utiliser File au lieu de Blob .

référence: https://developer.mozilla.org/de/docs/Web/API/File

NeutronenStern
la source
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
la source
1
les pls ajoutent une explication plus détaillée à votre réponse - stackoverflow.com/help/how-to-answer
Sebastian Brosch
1
cette réponse est des ordures
Jonathan Taylor
-2

Vous pouvez réellement y parvenir, dans Chrome et FireFox.

Essayez l'url suivante, elle téléchargera le code qui a été utilisé.

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