Czy jest jakiś sposób na określenie sugerowanej nazwy pliku podczas korzystania z danych: URI?

225

Jeśli na przykład skorzystasz z linku:

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

Przeglądarka wyświetli monit o pobranie pliku zawierającego dane przechowywane jako base64 w samym hiperłączu. Czy istnieje jakiś sposób sugerowania domyślnej nazwy w znacznikach? Jeśli nie, czy istnieje rozwiązanie JavaScript?

tshepang
źródło
może nie ma to związku z tym problemem, ale sugeruję użycie obiektu blob & URL.createObjectURL, jeśli nie jest to przeszkoda dla serwera lub starej przeglądarki
Endless
3
Niektóre przeglądarki obsługują opcjonalny parametr „name” data:application/pdf;name=document.pdf;base64,BASE64_DATA_ENCODED
mediatype
Miałem problem z Firefox pdf.js, który ma tendencję do zawieszania się w niektórych przypadkach, jeśli nie może wyodrębnić nazwy pliku z URI danych. patrz stackoverflow.com/questions/45585921/…
Bernhard
@mems Które przeglądarki obsługują parametr „name”? Czy możesz wskazać mi dokumentację referencyjną? (moje google-fu mnie zawiodło).
TheAddonDepot
@DimuDesigns Przynajmniej Firefox w tym czasie. Wygląda na to, że już tak nie jest. Jest to związane z parametrem „nazwa” MIME Content-Type (! = Content-Disposition) (nie w RFC?)
mems

Odpowiedzi:

158

Użyj downloadatrybutu:

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

Przykładem na żywo html5-demos.appspot.com / ... .

Obecnie działa na Chrome, Firefox, Edge, Opera i Safari na komputery stacjonarne, ale nie na Safari na iOS ani IE11.

Dan Fabulich
źródło
3
@BioDesign: Działa nawet z danymi: URI w chrome. Zobacz: jsfiddle.net/pYpqW
Sensowny
6
ale nie możesz tego zrobić window.location.replace. jeśli np. chcesz utworzyć dane: uri lub jeden wygenerowany przez window.URL.createObjectURLi pobrać go jako plik, musisz utworzyć <a> i kliknąć go: jsfiddle.net/flyingsheep/wpQtH (nie, $(...).click()nie działa)
latające owce
1
Tylko jeśli wszystkie przeglądarki były jak Chrome ... [westchnienie]
latarnia
6
@flyingsheep $('<a href="data:text/plain,Test" download="test.txt">')[0].click()wydaje się tutaj dobrze działać (Chrome 23) (uwaga: użyłem clickmetody natywnej , a nie jQuery). Demo: jsfiddle.net/2zsRW
Rob W
1
@flyingsheep wygląda na to, że egzekwują zasady tego samego pochodzenia w Firefoksie „W Firefox 20 ten atrybut jest honorowany tylko w przypadku linków do zasobów o tym samym pochodzeniu”. developer.mozilla.org/en-US/docs/Web/HTML/Element/a W moich testach Chrome nie ma tego ograniczenia.
William Denniss,
62

Chrome sprawia, że ​​jest to teraz bardzo proste:

function saveContent(fileContents, fileName)
{
    var link = document.createElement('a');
    link.download = fileName;
    link.href = 'data:,' + fileContents;
    link.click();
}
Holf
źródło
Nie wiem, o czym mówią te wszystkie inne odpowiedzi, które zadziałały przy pierwszej próbie w Chrome 30.
Michael J. Calkins,
2
Tak jest teraz, ale nie zawsze było tak łatwo. Wiele z tych odpowiedzi pochodzi z lat temu. Działają również dla innych przeglądarek.
Holf
7
Pełną listę kompatybilności przeglądarki można znaleźć na stronie http://caniuse.com/#feat=download .
tixastronauta
2
@tixastronauta: Pomimo informacji na tej stronie, nie działa w moim Firefoksie 44. Ładnie działa w Chrome. 48
Luis A. Florit
Cześć @Holf, czy istnieje sposób na dodanie typu pliku lub rozszerzenia, czy jest to tak proste, jak spceficy jak nazwa pliku?
Fraccier
51

Tylko HTML: użyj downloadatrybutu:

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


Tylko JavaScript: możesz zapisać dowolny identyfikator URI danych za pomocą tego kodu:

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 i Edge 13+ będą używać podanej nazwy pliku.

IE11, Edge 12 i Safari 9 (które nie obsługują tego downloadatrybutu ) pobiorą plik z jego domyślną nazwą lub po prostu wyświetlą go w nowej karcie, jeśli jest obsługiwanego typu pliku: obrazy, filmy, pliki audio ,…

fregante
źródło
Oba dema działają dobrze dla mnie w Chrome 38 (ale powinny działać w Chrome 14+)
fregante
Aby uzyskać bardziej kompletne rozwiązanie, sugeruję użycie downloadjsna npm
fregante 28.09.16
Działa dla mnie, ale potem strona przeglądarki odświeża się. Zastanawiasz się, jak temu zapobiec?
1
Nie działa w chrome dla rozmiaru pliku> 2 MB z powodu ograniczenia przez chrome stackoverflow.com/questions/695151/...
Pranav Singh
Limit należy do data:identyfikatora URI, o którym wspomina pytanie. Ta odpowiedź działa również z obiektami BLOB i wszystkim innym, które mają identyfikator URI
obiektami Blob
40

Według RFC 2397 nie, nie ma.

Ani też nie wydaje się, że każdy atrybut z <a>elementu, który można użyć albo.

Jednak HTML5 wprowadził następnie downloadatrybut tego <a>elementu, chociaż w momencie pisania obsługa nie jest uniwersalna (na przykład brak obsługi MSIE)

Alnitak
źródło
9
drugie zdanie było właściwe w momencie pisania, ale już nie jest . na razie jednak nie jest jeszcze powszechnie wdrażany.
latające owce
zobacz ten komentarz, aby uzyskać więcej informacji :)
latające owce
@flyingsheep, jest szeroko wdrażany.
Pacerier
1
nie było 3 lata temu, kiedy napisałem ten komentarz
latające owce
Jeśli plik jest tak długi, pobieranie nie powiedzie się
deFreitas
21

Szukałem trochę w źródłach Firefoxa w netwerk / protokół / dane / nsDataHandler.cpp

procedura obsługi danych analizuje tylko zawartość / typ i zestaw znaków i sprawdza, czy w ciągu znajduje się „; base64”

rfc określa brak nazwy pliku i przynajmniej firefox nie obsługuje nazwy pliku, kod generuje losową nazwę plus „.part”

Sprawdziłem także dziennik 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

ciekawe pliki, jeśli chcesz spojrzeć na źródła Mozilli:

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

Myślę, że możesz teraz przestać szukać rozwiązania, ponieważ podejrzewam, że nie ma :)

jak zauważono w tym wątku html5 ma downloadatrybut, działa również na Firefox 20 http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#attr-hyperlink-download

Sherpya
źródło
3
Chłodny! Chociaż niekoniecznie zgadzam się, że Firefox jest najwyższym autorytetem w tym, co istnieje. :)
Gleno,
14

Poniższy fragment kodu JavaScript działa w przeglądarce Chrome, używając nowego atrybutu pobierania linków i symulując kliknięcie.

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

Poniższy przykład pokazuje, że jest to przydatne:

downloadWithName("data:,Hello%2C%20World!", "helloWorld.txt")
owencm
źródło
1
To nie działa w Firefoksie, dodałem poniżej rozszerzoną odpowiedź z kompatybilnością z Fx.
fregante
12

Nie.

Głównym celem jest to, że jest to strumień danych, a nie plik. Źródło danych nie powinno mieć żadnej wiedzy o kliencie użytkownika przetwarzającym go jako plik ... i tak nie jest.

Lekkość Wyścigi na orbicie
źródło
6
Ma to na celu przekształcenie data:bloku danych wewnętrznych w format adresu URL bez konieczności odczytywania go ze źródła opartego na protokole. Link w odpowiedzi @ silex pokazuje, że możliwość zaproponowania preferowanej nazwy do napisania jest uważana za przydatną, nawet jeśli nie została jeszcze zaimplementowana.
Alnitak
1
@Alnitak: Przydatne? Absolutnie. Technicznie odpowiednie? Nadal nie jestem przekonany. :)
Wyścigi lekkości na orbicie
3
@ Tomalak rozważa różnicę między ładowaniem danych a ich zapisywaniem - tylko dlatego, że obiekt blob jest zakodowany w danych: adres URL nie oznacza, że ​​nie powinien mieć preferowanej nazwy do zapisywania.
Alnitak
4
Ale twoje zdanie na temat „całego celu” jest błędne. data:został specjalnie opracowany w celu umożliwienia (małej) wbudowanej treści wyświetlania w formacie skróconego URL-a, tak aby mógł być używany przez takie rzeczy jak tagi graficzne bez osobnego żądania HTTP. HTML mówi, że treść img srcatrybutu musi być adresem URL, więc to właśnie stworzył RFC 2397. Nie ma „źródła danych”.
Alnitak
6
@Alnitak: Dokładnie. Nie ma źródła danych. Nie ma kontekstu. Identyfikator URI to dane.
Wyścigi lekkości na orbicie
9

możesz dodać atrybut pobierania do elementu kotwicy.

próba:

<a download="abcd.cer"
    href="data:application/stream;base64,MIIDhTC......">down</a>
cuixiping
źródło
5

Spójrz na ten link: http://lists.w3.org/Archives/Public/uri/2010Feb/0069.html

Zacytować:

Działa nawet (jak w, nie powoduje problemu) z; base64 na końcu w
ten sposób (przynajmniej w Operze):

data: text / plain; charset = utf-8; headers = Content-Disposition% 3A% 20 załącznik% 3B% 20 nazwa pliku% 3D% 22 z% 20 spaces.txt% 22% 0D% 0AContent-Language% 3A% 20en; base64,4oiaDQo% 3D

Ponadto w pozostałych wiadomościach dyskusji znajdują się informacje.

silex
źródło
niestety to się nie pobiera.
James Khoury
7
ta dyskusja dotyczyła proponowanego rozszerzenia formatu URI danych - nie została zaimplementowana.
Alnitak
Zaimplementowane czy nie, przy istniejącym wsparciu dla dowolnych parametrów byłoby świetnie.
Dan Lugg,
5

Korzystanie z pracowników serwisowych jest wreszcie możliwe w najprawdziwszym tego słowa znaczeniu.

  1. Utwórz fałszywy adres URL. Na przykład /saveAs/myPrettyName.jpg
  2. Używaj URL w <a href, <img src, window.open (url), absolutnie wszystko, co można zrobić za pomocą „prawdziwego” adresu URL.
  3. Wewnątrz pracownika, złap zdarzenie pobierania i odpowiedz poprawnymi danymi.

Przeglądarka zasugeruje teraz myPrettyName.jpg, nawet jeśli użytkownik otworzy plik w nowej karcie i spróbuje go tam zapisać. Będzie dokładnie tak, jakby plik pochodził z serwera.

// 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
źródło
1
Ciekawy! Wsparcie wydaje się na razie dość płytkie: caniuse.com/#feat=serviceworkers
tuomassalo
Czy jest jakiś sposób, aby „odpowiedzieć” innym plikiem bezpośrednio na plik?
Iulian Onofrei
4

Jest mały skrypt obejścia w Google Code, który działał dla mnie:

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

Dodaje formularz z zawartymi w nim danymi, przesyła go, a następnie ponownie usuwa formularz. Hacky, ale to zadziałało dla mnie. Wymaga jQuery.

Wątek pojawił się w Google przed stroną Google Code i pomyślałem, że może być pomocne, aby mieć tutaj również link.

Fabian B.
źródło
Ciekawy skrypt, ale wymaga odpowiedzi serwera i odesłania go z powrotem? jsfiddle.net/hZySf
James Khoury
Nie jestem pewien, skąd plik jest generowany ... czy plik jest przechowywany w kodzie base64? (Nie znam się zbyt dobrze na base64)
latarnia
@streetlight: „Plik” (tzn. dane) jest generowany przez Javascript. Kontekst tego projektu (i prawdopodobnie większość tutaj) zakłada, że ​​masz jakiś sposób na przeniesienie pożądanych danych do zmiennej JS. Różnica polega na tym, że zamiast przedstawiać go użytkownikowi za pomocą data:...identyfikatora URI, skrypt tworzy formularz do wysłania go na serwer. A serwer prawdopodobnie przypisze to ponownie jako odpowiedź HTTP „pobieranie” (tj. Z odpowiednim nagłówkiem Content-Disposition określającym nazwę pliku).
Andrzej Doyle
4

Oto wersja jQuery oparta na wersji Holfa i współpracuje z Chrome i Firefox, podczas gdy jego wersja wydaje się działać tylko z Chrome. To trochę dziwne, aby dodać coś do ciała, aby to zrobić, ale jeśli ktoś ma lepszą opcję, jestem za tym wszystkim.

var exportFileName = "export-" + filename;
$('<a></a>', {
    "download": exportFileName,
    "href": "data:," + JSON.stringify(exportData, null,5),
    "id": "exportDataID"
}).appendTo("body")[0].click().remove();
kgividen
źródło
1
Z jQuery 1.11 dostaję wyjątek z powodu .remove (). $().appendTo()variable.click(); variable.remove()
Obejrzałem
@ p0lar_bear powinieneś otrzymać ten wyjątek z dowolnym jQuery, ponieważ uzyskanie [0]z dowolnego „elementu jQuery” powinno zwrócić pierwszy reprezentowany przez niego element DOM, co w zasadzie „wyciąga cię z” jQuery.
drzaus
Właściwie nie powinieneś w ogóle dodawać / usuwać elementu - patrz komentarze na stackoverflow.com/a/17311705/1037948
drzaus
3

To trochę hackish, ale byłem w tej samej sytuacji wcześniej. Dynamicznie generowałem plik tekstowy w javascript i chciałem go udostępnić do pobrania, kodując go za pomocą URI danych.

Jest to możliwe przy niewielkiej interwencji dużego użytkownika. Wygeneruj link<a href="data:...">right-click me and select "Save Link As..." and save as "example.txt"</a> . Jak powiedziałem, jest to nieeleganckie, ale działa, jeśli nie potrzebujesz profesjonalnego rozwiązania.

Można to uczynić mniej bolesnym za pomocą flashowania, aby najpierw skopiować nazwę do schowka. Oczywiście, jeśli pozwolisz sobie na użycie Flasha lub Javy (myślę, że teraz z coraz mniejszą obsługą przeglądarki?), Prawdopodobnie możesz znaleźć inny sposób na zrobienie tego.

ninjagecko
źródło
To nie jest rozwiązanie i nie spełnia oczekiwań. Przepraszam.
jcolebrand
7
Lol @ „drobna interwencja użytkownika”. Nakłonienie użytkownika do zrobienia wszystkiego za Ciebie nie jest „drobną interwencją użytkownika”.
Wyścigi lekkości na orbicie
Połącz to z stackoverflow.com/questions/17311645/…, aby uruchomić wygenerowany link i nie potrzebujesz interwencji użytkownika. Możesz określić atrybut HTML5,download aby sugerować nazwę, jak wspomniano w wielu innych odpowiedziach .
drzaus
Jest to świetne obejście dla Safari. Użyj Modernizr, aby wykryć, kiedy atrybut pobierania nie jest obsługiwany, i zaktualizuj tekst linku!
littledynamo
2

Ten działa z Firefoksem 43.0 (starszy nie testowany):

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>

Po kliknięciu przycisku zaoferowano do pobrania plik o nazwie hello.bin . Sztuczka polega na użyciu pliku zamiast obiektu Blob .

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

NeutronenStern
źródło
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
źródło
1
proszę dodać bardziej szczegółowe wyjaśnienie do swojej odpowiedzi - stackoverflow.com/help/how-to-answer
Sebastian Brosch
1
ta odpowiedź to śmieć
Jonathan Taylor
-2

Możesz to osiągnąć w Chrome i FireFox.

Wypróbuj następujący adres URL, aby pobrać użyty kod.

data:text/html;base64,PGEgaHJlZj0iZGF0YTp0ZXh0L2h0bWw7YmFzZTY0LFBHRWdhSEpsWmowaVVGVlVYMFJCVkVGZlZWSkpYMGhGVWtVaUlHUnZkMjVzYjJGa1BTSjBaWE4wTG1oMGJXd2lQZ284YzJOeWFYQjBQZ3BrYjJOMWJXVnVkQzV4ZFdWeWVWTmxiR1ZqZEc5eUtDZGhKeWt1WTJ4cFkyc29LVHNLUEM5elkzSnBjSFErIiBkb3dubG9hZD0idGVzdC5odG1sIj4KPHNjcmlwdD4KZG9jdW1lbnQucXVlcnlTZWxlY3RvcignYScpLmNsaWNrKCk7Cjwvc2NyaXB0Pg==
Chad Scira
źródło