Gibt es eine Möglichkeit, einen vorgeschlagenen Dateinamen anzugeben, wenn Daten verwendet werden: URI?


225

Wenn Sie zum Beispiel dem Link folgen:

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

Der Browser fordert Sie auf, eine Datei herunterzuladen, die aus den Daten besteht, die im Hyperlink selbst als base64 gespeichert sind. Gibt es eine Möglichkeit, einen Standardnamen im Markup vorzuschlagen? Wenn nicht, gibt es eine JavaScript-Lösung?


Vielleicht ohne Bezug zu diesem Problem, aber ich schlage vor, blobs & URL.createObjectURL zu verwenden, wenn dies kein Server oder altes Browser-Hindernis ist
Endless

3
Einige Browser unterstützen den optionalen Parameter "name" des Medientyps:data:application/pdf;name=document.pdf;base64,BASE64_DATA_ENCODED
mems

Ich hatte das Problem mit Firefox pdf.js, das in einigen Fällen hängen bleibt, wenn es keinen Dateinamen aus der Daten-URL extrahieren kann. siehe stackoverflow.com/questions/45585921/…
Bernhard

@mems Welche Browser unterstützen den Parameter "name"? Können Sie mich auf eine Referenzdokumentation verweisen? (Mein Google-Fu hat mich gescheitert).
TheAddonDepot

@ DimuDesigns Zumindest Firefox zu dieser Zeit. Es sieht so aus, als wäre das nicht mehr der Fall. Es ist verwandt mit MIME Content-Type (! = Content-Disposition) "Name" -Parameter (nicht in RFC?)
Mems

Antworten:


158

Verwenden Sie das downloadAttribut:

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

Live - Beispiel auf html5-demos.appspot.com / ... .

Funktioniert derzeit auf Chrome, Firefox, Edge, Opera und Desktop Safari, jedoch nicht auf iOS Safari oder IE11.


3
@ BioDesign: Es funktioniert auch mit Daten: URIs in Chrom. Siehe: jsfiddle.net/pYpqW
Senseful

6
aber du kannst es nicht damit machen window.location.replace. Wenn Sie beispielsweise Daten erstellen möchten: uri oder eine, die von generiert wurde window.URL.createObjectURL, und diese als Datei herunterladen möchten , müssen Sie eine <a> erstellen und darauf klicken: jsfiddle.net/flyingsheep/wpQtH (nein, $(...).click()funktioniert nicht)
fliegende Schafe

1
Nur wenn alle Browser wie Chrome wären ... [seufz]
Straßenlaterne

6
@flyingsheep $('<a href="data:text/plain,Test" download="test.txt">')[0].click()scheint hier gut zu funktionieren (Chrome 23) (Hinweis: Ich habe die native clickMethode verwendet , nicht die von jQuery). Demo: jsfiddle.net/2zsRW
Rob W

1
@flyingsheep Es scheint, dass sie in Firefox eine Richtlinie mit demselben Ursprung erzwingen. "In Firefox 20 wird dieses Attribut nur für Links zu Ressourcen mit demselben Ursprung berücksichtigt." developer.mozilla.org/en-US/docs/Web/HTML/Element/a In meinen Tests hat Chrome diese Einschränkung nicht.
William Denniss

62

Chrome macht dies heutzutage sehr einfach:

function saveContent(fileContents, fileName)
{
    var link = document.createElement('a');
    link.download = fileName;
    link.href = 'data:,' + fileContents;
    link.click();
}

Idk, worüber all diese anderen Antworten sprechen, hat beim ersten Versuch in Chrome 30 funktioniert.
Michael J. Calkins

2
Das tut es jetzt, aber es war nicht immer so einfach. Viele dieser Antworten stammen aus früheren Jahren. Und sie funktionieren auch für andere Browser.
Holf

7
Eine vollständige Liste der Browserkompatibilität finden Sie unter http://caniuse.com/#feat=download .
Tixastronauta

2
@tixastronauta: Trotz der Informationen auf dieser Seite funktioniert es nicht in meinem Firefox 44. Funktioniert gut in Chrome. 48
Luis A. Florit

Hallo @Holf, gibt es eine Möglichkeit, auch den Dateityp oder die Dateierweiterung hinzuzufügen, oder ist es genauso einfach wie der Dateiname?
Fraccier

51

Nur HTML: Verwenden Sie das downloadAttribut:

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


Nur Javascript: Mit diesem Code können Sie jeden Daten-URI speichern:

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 und Edge 13+ verwenden den angegebenen Dateinamen.

IE11, Edge 12 und Safari 9 (die das downloadAttribut nicht unterstützen ) laden die Datei mit ihrem Standardnamen herunter oder zeigen sie einfach auf einer neuen Registerkarte an, wenn es sich um einen unterstützten Dateityp handelt: Bilder, Videos, Audiodateien ,…


Beide Demos funktionieren gut für mich in Chrome 38 (aber sie sollten in Chrome 14+ funktionieren)
fregante

Für eine vollständigere Lösung schlage ich vor, downloadjsam
Uhr

Es funktioniert bei mir, aber die Browserseite wird danach aktualisiert. Frage mich, wie ich das verhindern kann?

1
Funktioniert nicht in Chrome für Dateigröße> 2 MB aufgrund von Einschränkungen durch Chrome stackoverflow.com/questions/695151/…
Pranav Singh

Das Limit gehört zur data:URI, was in der Frage erwähnt wird. Diese Antwort funktioniert auch mit Blobs und was auch immer sonst hat eine URI
fregante

40

Laut RFC 2397 gibt es keine.

Es scheint auch kein Attribut des <a>Elements zu geben, das Sie verwenden können.

HTML5 hat jedoch später das downloadAttribut für das <a>Element eingeführt, obwohl die Unterstützung zum Zeitpunkt des Schreibens nicht universell ist (z. B. keine MSIE-Unterstützung).


9
Der zweite Satz war zum Zeitpunkt des Schreibens richtig, ist aber nicht mehr . Derzeit ist es jedoch noch nicht weit verbreitet.
fliegende Schafe

siehe diesen Kommentar für weitere Informationen :)
fliegende Schafe

@flyingsheep, Es ist weit verbreitet.
Pacerier

1
Es war nicht vor 3 Jahren, als ich diesen Kommentar schrieb
fliegende Schafe

Wenn die Datei so lang ist, schlägt der Download fehl
deFreitas

21

Ich habe ein bisschen in Firefox-Quellen in netwerk / protocol / data / nsDataHandler.cpp gesucht

Der Datenhandler analysiert nur Inhalt / Typ und Zeichensatz und prüft, ob die Zeichenfolge "; base64" enthält

Der RFC gibt keinen Dateinamen an und zumindest Firefox verarbeitet keinen Dateinamen dafür. Der Code generiert einen zufälligen Namen plus ".part".

Ich habe auch das Firefox-Protokoll überprüft

[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

interessante Dateien, wenn Sie sich Mozilla-Quellen ansehen möchten:

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

Ich denke, Sie können vorerst aufhören, nach einer Lösung zu suchen, weil ich vermute, dass es keine gibt :)

Wie in diesem Thread bemerkt, hat HTML5 ein downloadAttribut, es funktioniert auch unter Firefox 20 http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#attr-hyperlink-download


3
Cool! Obwohl ich nicht unbedingt zustimme, dass Firefox die ultimative Autorität für das ist, was existiert. :)
Gleno

14

Das folgende Javascript-Snippet funktioniert in Chrome, indem das neue Download-Attribut von Links verwendet und ein Klick simuliert wird.

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

Das folgende Beispiel zeigt die Verwendung:

downloadWithName("data:,Hello%2C%20World!", "helloWorld.txt")

1
Dies funktioniert in Firefox nicht. Ich habe unten eine erweiterte Antwort mit Fx-Kompatibilität hinzugefügt.
Fregante

12

Nein.

Der gesamte Zweck besteht darin, dass es sich um einen Datenstrom handelt, nicht um eine Datei. Die Datenquelle sollte keine Kenntnisse über den Benutzeragenten haben, der sie als Datei behandelt ... und dies ist nicht der Fall.


6
Der Zweck von data:besteht darin, einen Block interner Daten in das URL-Format zu verschieben, ohne ihn aus einer protokollbasierten Quelle lesen zu müssen . Der Link in der Antwort von @ silex zeigt, dass die Möglichkeit, einen bevorzugten Namen zum Schreiben vorzuschlagen , als nützlich angesehen wird, auch wenn er noch nicht implementiert ist.
Alnitak

1
@ Alnitak: Nützlich? Absolut. Technisch angemessen? Immer noch nicht überzeugt. :)
Leichtigkeitsrennen im Orbit

3
@Tomalak berücksichtigt den Unterschied zwischen dem Laden und Speichern der Daten - nur weil ein Blob in Daten inline codiert ist: URL bedeutet nicht, dass es keinen bevorzugten Namen zum Speichern haben sollte.
Alnitak

4
Aber Ihre Aussage über den "gesamten Zweck" ist falsch. data:wurde speziell erfunden, um zu ermöglichen, dass (kleine) Inline- Inhalte in einem zusammengewürfelten URL-Format angezeigt werden, sodass sie beispielsweise von Bild-Tags ohne separate HTTP-Anforderung verwendet werden können. HTML sagt, dass der Inhalt eines img srcAttributs eine URL sein muss, also hat RFC 2397 das erstellt. Es gibt keine "Datenquelle".
Alnitak

6
@ Alnitak: Genau. Es gibt keine Datenquelle. Es gibt keinen Kontext. Der URI sind die Daten.
Leichtigkeitsrennen im Orbit

9

Sie können dem Ankerelement ein Download-Attribut hinzufügen.

Stichprobe:

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

5

Schauen Sie sich diesen Link an: http://lists.w3.org/Archives/Public/uri/2010Feb/0069.html

Zitat:

Es funktioniert sogar (wie in, verursacht kein Problem) mit; base64 am Ende
wie folgt (zumindest in Opera):

Daten: Text / Klartext; Zeichensatz = utf-8; Header = Inhaltsdisposition% 3A% 20Anhang% 3B% 20Dateiname% 3D% 22mit% 20spaces.txt% 22% 0D% 0AContent-Language% 3A% 20en; base64,4oiaDQo% 3D

Es gibt auch einige Informationen in den restlichen Nachrichten der Diskussion.


Leider wird dies nicht heruntergeladen.
James Khoury

7
Diese Diskussion betraf eine vorgeschlagene Erweiterung des Daten-URI-Formats - es wurde nicht implementiert.
Alnitak

Implementiert oder nicht, mit der vorhandenen Unterstützung für beliebige Parameter wäre dies eine großartige Sache.
Dan Lugg

5

Mit Servicemitarbeitern ist dies endlich im wahrsten Sinne des Wortes möglich.

  1. Erstellen Sie eine gefälschte URL. Zum Beispiel /saveAs/myPrettyName.jpg
  2. Verwenden Sie die URL in <a href, <img srcwindow.open (url), absolut alles, was mit einer "echten" URL möglich ist.
  3. Fangen Sie im Worker das Abrufereignis ab und antworten Sie mit den richtigen Daten.

Der Browser schlägt jetzt myPrettyName.jpg vor, auch wenn der Benutzer die Datei in einem neuen Tab öffnet und versucht, sie dort zu speichern. Es wird genau so sein, als ob die Datei vom Server gekommen wäre.

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

1
Interessant! Die Unterstützung scheint jedoch vorerst
tuomassalo

Gibt es eine Möglichkeit, mit einer anderen direkten URL auf eine Datei zu "antworten"?
Iulian Onofrei

4

Es gibt ein kleines Problemumgehungsskript in Google Code, das für mich funktioniert hat:

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

Es fügt ein Formular mit den darin enthaltenen Daten hinzu, sendet es und entfernt das Formular erneut. Hacky, aber es hat den Job für mich gemacht. Benötigt jQuery.

Dieser Thread wurde in Google vor der Google-Codepage angezeigt, und ich dachte, es könnte hilfreich sein, den Link auch hier zu haben.


Interessantes Skript, aber es erfordert, dass der Server die Antwort erhält und sie zurücksendet, oder? jsfiddle.net/hZySf
James Khoury

Ich bin nicht sicher, woher die Datei generiert wird. Wird diese Datei in der Base64-Codierung gespeichert? (Ich bin nicht zu vertraut mit base64)
Straßenlaterne

@streetlight: Die "Datei" (dh Daten) wird von Javascript generiert. Der Kontext dieses Projekts (und wahrscheinlich die meisten hier) setzt voraus, dass Sie eine Möglichkeit haben, Ihre gewünschten Daten in eine JS-Variable zu übertragen. Der Unterschied besteht darin data:..., dass dieses Skript , anstatt es dem Benutzer über einen URI zu präsentieren, ein Formular erstellt, um es an den Server zu senden. Und der Server gibt es dann vermutlich direkt als HTTP-Download-Antwort zurück (dh mit einem entsprechenden Content-Disposition-Header, der den Dateinamen angibt).
Andrzej Doyle

4

Hier ist eine jQuery-Version, die auf Holfs Version basiert und mit Chrome und Firefox funktioniert, während seine Version nur mit Chrome zu funktionieren scheint. Es ist ein wenig seltsam, dem Körper etwas hinzuzufügen, um dies zu tun, aber wenn jemand eine bessere Option hat, bin ich alles dafür.

var exportFileName = "export-" + filename;
$('<a></a>', {
    "download": exportFileName,
    "href": "data:," + JSON.stringify(exportData, null,5),
    "id": "exportDataID"
}).appendTo("body")[0].click().remove();

1
Mit jQuery 1.11 erhalte ich aufgrund der .remove () eine Ausnahme. Ich habe dies $().appendTo()variable.click(); variable.remove()
umgangen,

@ p0lar_bear Sie sollten diese Ausnahme mit jeder jQuery erhalten, da das Abrufen der [0]von jedem "jQuery-Element" das erste DOM-Element zurückgeben sollte, das es darstellt, was Sie im Wesentlichen aus jQuery "herausholt".
Drzaus

Sie sollten das Element eigentlich gar nicht anhängen / entfernen müssen - siehe Kommentare unter stackoverflow.com/a/17311705/1037948
drzaus

3

Es ist irgendwie hackisch, aber ich war schon einmal in der gleichen Situation. Ich habe dynamisch eine Textdatei in Javascript generiert und wollte sie zum Download bereitstellen, indem ich sie mit dem Daten-URI codierte.

Dies ist mit geringfügigen größeren Benutzereingriffen möglich . Generieren Sie einen Link <a href="data:...">right-click me and select "Save Link As..." and save as "example.txt"</a>. Wie gesagt, das ist unelegant, aber es funktioniert, wenn Sie keine professionelle Lösung benötigen.

Dies könnte weniger schmerzhaft sein, wenn Sie den Namen zuerst mit Flash in die Zwischenablage kopieren. Wenn Sie sich Flash oder Java erlauben lassen (jetzt mit immer weniger Browser-Unterstützung, denke ich?), Könnten Sie wahrscheinlich einen anderen Weg finden, dies zu tun.


Dies ist keine Lösung und entspricht nicht den Anforderungen. Es tut uns leid.
Jcolebrand

7
Lol @ "geringfügiger Benutzereingriff". Den Benutzer dazu zu bringen, das Ganze für Sie zu tun, ist kein "geringfügiger Benutzereingriff".
Leichtigkeitsrennen im Orbit

Kombinieren Sie dies mit stackoverflow.com/questions/17311645/… , um den generierten Link auszulösen, und Sie müssen nicht vom Benutzer eingreifen. Sie können das HTML5- downloadAttribut angeben , um einen Namen vorzuschlagen, wie in vielen anderen Antworten erwähnt .
Drzaus

Dies ist eine großartige Problemumgehung für Safari. Verwenden Sie Modernizr, um festzustellen, wenn das Download-Attribut nicht unterstützt wird, und aktualisieren Sie den Link-Text!
littledynamo

2

Dieser funktioniert mit Firefox 43.0 (älter nicht getestet):

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>

Wenn Sie auf die Schaltfläche klicken, wird eine Datei mit dem Namen hello.bin zum Download angeboten. Trick ist, Datei anstelle von Blob zu verwenden .

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


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

1
Bitte fügen Sie eine detailliertere Erklärung zu Ihrer Antwort hinzu - stackoverflow.com/help/how-to-answer
Sebastian Brosch

1
Diese Antwort ist Müll
Jonathan Taylor

-2

Sie können dies tatsächlich in Chrome und FireFox erreichen.

Versuchen Sie die folgende URL, um den verwendeten Code herunterzuladen.

data:text/html;base64,PGEgaHJlZj0iZGF0YTp0ZXh0L2h0bWw7YmFzZTY0LFBHRWdhSEpsWmowaVVGVlVYMFJCVkVGZlZWSkpYMGhGVWtVaUlHUnZkMjVzYjJGa1BTSjBaWE4wTG1oMGJXd2lQZ284YzJOeWFYQjBQZ3BrYjJOMWJXVnVkQzV4ZFdWeWVWTmxiR1ZqZEc5eUtDZGhKeWt1WTJ4cFkyc29LVHNLUEM5elkzSnBjSFErIiBkb3dubG9hZD0idGVzdC5odG1sIj4KPHNjcmlwdD4KZG9jdW1lbnQucXVlcnlTZWxlY3RvcignYScpLmNsaWNrKCk7Cjwvc2NyaXB0Pg==
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.