Есть ли способ указать предложенное имя файла при использовании data: URI?

225

Если, например, вы переходите по ссылке:

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

Браузер предложит вам загрузить файл, содержащий данные, хранящиеся в виде base64 в самой гиперссылке. Есть ли способ предложить имя по умолчанию в разметке? Если нет, есть ли решение JavaScript?

tshepang
источник
возможно, не имеет отношения к этой проблеме, но я предлагаю использовать blob's & URL.createObjectURL, если это не помеха для сервера или старого браузера
Endless
3
Некоторые браузеры поддерживают необязательный параметр mediatype «name»:data:application/pdf;name=document.pdf;base64,BASE64_DATA_ENCODED
mems
У меня была проблема с Firefox pdf.js, который в некоторых случаях может зависнуть, если не удается извлечь имя файла из данных URI. см. stackoverflow.com/questions/45585921/…
Бернхард
@mems Какие браузеры поддерживают параметр "name"? Можете ли вы указать мне некоторую справочную документацию? (мой гугл-фу подвел меня).
TheAddonDepot
@DimuDesigns По крайней мере, Firefox в то время. Похоже, это уже не так. Это связано с параметром «имя» MIME Content-Type (! = Content-Disposition) (не в RFC?)
мемы

Ответы:

158

Используйте downloadатрибут:

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

Живой пример на html5-demos.appspot.com / ... .

В настоящее время работает на Chrome, Firefox, Edge, Opera и настольном Safari, но не на iOS Safari или IE11.

Дан Фабулич
источник
3
@BioDesign: работает даже с данными: URI в Chrome. Смотрите: jsfiddle.net/pYpqW
Чувственный
6
но вы не можете сделать это с window.location.replace. например, если вы хотите создать данные: uri или данные, сгенерированные window.URL.createObjectURLи загрузить их в виде файла, вам нужно создать <a> и щелкнуть по нему: jsfiddle.net/flyingsheep/wpQtH (нет, $(...).click()не работает)
летающие овцы
1
Только если бы все браузеры были похожи на Chrome ... [вздох]
уличный свет
6
@flyingsheep $('<a href="data:text/plain,Test" download="test.txt">')[0].click()здесь работает нормально (Chrome 23) (примечание: я использовал нативный clickметод , а не jQuery). Демонстрация: jsfiddle.net/2zsRW
Роб W
1
@flyingsheep кажется, что они применяют политику одинакового происхождения в Firefox: «В Firefox 20 этот атрибут важен только для ссылок на ресурсы с тем же происхождением». developer.mozilla.org/en-US/docs/Web/HTML/Element/a В моем тестировании Chrome не имеет этого ограничения.
Уильям Деннис
62

Chrome делает это очень просто в эти дни:

function saveContent(fileContents, fileName)
{
    var link = document.createElement('a');
    link.download = fileName;
    link.href = 'data:,' + fileContents;
    link.click();
}
Holf
источник
ИДК , что все эти другие ответы говорят об этом работал на первой попытке в Chrome 30.
Michael J. Калкинс
2
Это происходит сейчас, но это не всегда было так просто. Многие из этих ответов от лет назад. И они также работают для других браузеров.
Холф
7
Обратитесь к http://caniuse.com/#feat=download для получения полного списка совместимости браузера.
tixastronauta
2
@tixastronauta: Несмотря на информацию на этой странице, в моем Firefox 44 не работает. Прекрасно работает в Chrome. 48
Луис А. Флорит
Привет @Holf Есть ли способ также добавить тип файла или расширение или его так же просто, как spceficy его как имя файла?
Fraccier
51

Только HTML: используйте downloadатрибут:

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


Только Javascript: вы можете сохранить любой URI данных с помощью этого кода:

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 и Edge 13+ будут использовать указанное имя файла.

IE11, Edge 12 и Safari 9 (которые не поддерживают downloadатрибут ) загрузят файл со своим именем по умолчанию или просто отобразят его на новой вкладке, если это файл поддерживаемого типа: изображения, видео, аудиофайлы …

fregante
источник
У меня обе демонстрации отлично работают в Chrome 38 (но они должны работать в Chrome 14+)
fregante
Для более полного решения, я предлагаю использовать downloadjsна npm
fregante
Это работает для меня, но страница браузера обновляется после этого. Интересно, как это предотвратить?
1
Не работает в chrome для размера файла> 2 МБ из-за ограничения chrome stackoverflow.com/questions/695151/…
Пранав Сингх,
Ограничение принадлежит data:URI, о котором упоминается в вопросе. Этот ответ также работает с Blobs и всем остальным, имеющим URI
fregante
40

Согласно RFC 2397 , нет, нет.

Там также не наблюдается никаких атрибутов из <a>элемента , который вы можете использовать либо.

Однако HTML5 впоследствии ввел downloadатрибут в <a>элемент, хотя на момент написания поддержка не была универсальной (например, поддержка MSIE отсутствует)

Альнитак
источник
9
второе предложение было правильным на момент написания, но не больше . на данный момент, это еще не широко реализовано, хотя.
летающие овцы
см. этот комментарий для получения дополнительной информации :)
летающие овцы
@flyingsheep, он будет широко применяться.
Pacerier
1
это было не 3 года назад, когда я написал этот комментарий
летающие овцы
Если файл слишком длинный, загрузка не удалась
deFreitas
21

Я посмотрел немного в исходниках Firefox в netwerk / protocol / data / nsDataHandler.cpp

Обработчик данных только анализирует содержимое / тип и кодировку и проверяет, есть ли в строке «; base64»

rfc не указывает имя файла и, по крайней мере, firefox не обрабатывает имя файла для него, код генерирует случайное имя плюс «.part»

Я также проверил журнал 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

интересные файлы, если вы хотите посмотреть источники 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

Я думаю, что вы можете прекратить поиск решения на данный момент, потому что я подозреваю, что нет никакого :)

как отмечалось в этой теме, html5 имеет downloadатрибут, он также работает в Firefox 20 http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#attr-hyperlink-download

sherpya
источник
3
Прохладно! Хотя я не обязательно согласен с тем, что Firefox - высший авторитет в том, что существует. :)
Глено
14

Следующий фрагмент кода Javascript работает в Chrome с использованием нового атрибута ссылок «загрузить» и имитации щелчка.

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

И следующий пример показывает его использование:

downloadWithName("data:,Hello%2C%20World!", "helloWorld.txt")
owencm
источник
1
Это не работает в Firefox, ниже я добавил расширенный ответ с Fx-совместимостью.
августа в 0:32
12

Нет.

Вся цель в том, что это поток данных, а не файл. Источник данных не должен знать, что пользовательский агент обрабатывает его как файл ... а это не так.

Гонки легкости на орбите
источник
6
Цель data:состоит в том, чтобы переместить блок внутренних данных в формат URL, не считывая их из источника на основе протокола. Ссылка в ответе @ silex показывает, что возможность предложить предпочтительное имя для записи считается полезной, даже если она еще не реализована.
Альнитак
1
@Alnitak: полезно? Абсолютно. Технически уместно? Все еще не убежден. :)
Гонки легкости на орбите
3
@Tomalak учитывает разницу между загрузкой данных и их сохранением - просто потому, что большой двоичный объект закодирован в данных: URL не означает, что у него не должно быть предпочтительного имени для сохранения.
Альнитак
4
Но ваша линия о «всей цели» неверна. data:был специально придуман для того, чтобы позволить (небольшому) встроенному контенту отображаться в вымышленном формате URL, чтобы его можно было использовать такими вещами, как теги изображений, без отдельного HTTP-запроса. HTML говорит, что содержимое img srcатрибута должно быть URL, так что это то, что RFC 2397 создал. Нет «источника данных».
Альнитак
6
@Alnitak: Точно. Там нет источника данных. Там нет контекста. URI - это данные.
Гонки легкости на орбите
9

Вы можете добавить атрибут загрузки к элементу привязки.

образец:

<a download="abcd.cer"
    href="data:application/stream;base64,MIIDhTC......">down</a>
cuixiping
источник
5

Посмотрите на эту ссылку: http://lists.w3.org/Archives/Public/uri/2010Feb/0069.html

Quote:

Он даже работает (как и не вызывает проблем) с; base64 в конце
примерно так (по крайней мере, в Opera):

данные: текст / обычный; кодировка = UTF-8; заголовки = Content-Disposition% 3A% 20attachment% 3B% 20filename% 3D% 22with% 20spaces.txt% 22% 0D% 0AContent-Язык% 3A% 20en; base64,4oiaDQo% 3D

Также есть некоторая информация в остальных сообщениях обсуждения.

кремнезем
источник
к сожалению это не скачивается.
Джеймс Хури
7
это обсуждение было предложено для расширения формата данных URI - оно не было реализовано.
Альнитак
Реализовано или нет, с существующей поддержкой произвольных параметров это было бы здорово.
Дэн Лугг
5

Используя сервисных работников , это наконец возможно в прямом смысле.

  1. Создать поддельный URL. Например /saveAs/myPrettyName.jpg
  2. Используйте URL в <a href, <img src, window.open (url), абсолютно все, что можно сделать с «реальным» URL.
  3. Внутри рабочего перехватите событие извлечения и ответьте правильными данными.

Браузер теперь предложит myPrettyName.jpg, даже если пользователь откроет файл в новой вкладке и попытается сохранить его там. Это будет точно так, как если бы файл пришел с сервера.

// 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
источник
1
Интересный! Однако пока поддержка
невелика
Есть ли способ «ответить» другим прямым URL-адресом файла?
Юлиан Онофрей
4

В Google Code есть небольшой обходной скрипт, который работал для меня:

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

Он добавляет форму с данными в ней, отправляет ее, а затем снова удаляет форму. Хаки, но это сделало всю работу за меня. Требуется JQuery.

Эта тема появилась в Google до кодовой страницы Google, и я подумал, что, возможно, здесь тоже есть ссылка.

Фабиан Б.
источник
Интересный сценарий, но он требует от сервера получить ответ и отправить его обратно правильно? jsfiddle.net/hZySf
Джеймс Хури
Я не уверен, откуда файл генерируется .. этот файл хранится в кодировке base64? (Я не слишком знаком с base64)
уличный
@streetlight: «файл» (т.е. данные) генерируется Javascript. Контекст этого проекта (и, вероятно, большинство здесь) предполагает, что у вас есть какой-то способ получить нужные данные в переменную JS. Разница в том, что вместо того, чтобы представлять его пользователю через data:...URI, этот сценарий создает форму для отправки его на сервер. И сервер, по-видимому, затем возвращает его обратно в виде HTTP-ответа «загрузка» (т. Е. С соответствующим заголовком Content-Disposition, указывающим имя файла).
Анджей Дойл
4

Вот версия jQuery, основанная на версии Холфа и работающая с Chrome и Firefox, тогда как его версия работает только с Chrome. Немного странно добавлять что-то в тело, чтобы сделать это, но если у кого-то есть лучший вариант, я полностью за это.

var exportFileName = "export-" + filename;
$('<a></a>', {
    "download": exportFileName,
    "href": "data:," + JSON.stringify(exportData, null,5),
    "id": "exportDataID"
}).appendTo("body")[0].click().remove();
kgividen
источник
1
С jQuery 1.11 я получаю исключение из-за .remove (). Я $().appendTo()variable.click(); variable.remove()
справился с
@ p0lar_bear, вы должны получить это исключение с любым jQuery, потому что получение [0]из любого «элемента jQuery» должно вернуть первый элемент DOM, который он представляет, что по сути «выводит вас из» jQuery.
drzaus
На самом деле вам вообще не нужно добавлять / удалять элемент - смотрите комментарии на stackoverflow.com/a/17311705/1037948
drzaus
3

Это немного хакерски, но я уже был в такой же ситуации. Я динамически генерировал текстовый файл в javascript и хотел предоставить его для загрузки, кодируя его с помощью data-URI.

Это возможно при незначительном вмешательстве крупных пользователей. Создать ссылку <a href="data:...">right-click me and select "Save Link As..." and save as "example.txt"</a>. Как я уже сказал, это не элегантно, но работает, если вам не нужно профессиональное решение.

Это может быть сделано менее болезненно, если сначала использовать flash для копирования имени в буфер обмена. Конечно, если вы позволите себе использовать Flash или Java (я думаю, что теперь все меньше и меньше поддерживает браузер?), Вы, возможно, найдете другой способ сделать это.

ninjagecko
источник
Это не решение и не соответствует тому, что просили. Сожалею.
Jcolebrand
7
Lol @ "незначительное вмешательство пользователя". Заставить пользователя сделать все за вас - это не «незначительное вмешательство пользователя».
Гонки легкости на орбите
Объедините это со stackoverflow.com/questions/17311645/…, чтобы активировать сгенерированную ссылку, и вам не нужно вмешательство пользователя. Вы можете указать атрибут HTML5,download чтобы предложить имя, как упоминалось во многих других ответах .
drzaus
Это отличный обходной путь для Safari. Используйте Modernizr, чтобы определить, когда атрибут загрузки не поддерживается, и обновить текст ссылки!
Литтлединамо
2

Этот работает с Firefox 43.0 (старый не тестировался):

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>

Если кнопка нажата, она предлагает файл с именем hello.bin для скачивания. Хитрость заключается в том, чтобы использовать File вместо Blob .

ссылка: https://developer.mozilla.org/de/docs/Web/API/File

NeutronenStern
источник
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
Пожалуйста, добавьте более подробное объяснение к вашему ответу - stackoverflow.com/help/how-to-answer
Себастьян Брош
1
этот ответ - мусор
Джонатан Тейлор
-2

Вы действительно можете достичь этого в Chrome и FireFox.

Попробуйте следующий URL, он загрузит код, который был использован.

data:text/html;base64,PGEgaHJlZj0iZGF0YTp0ZXh0L2h0bWw7YmFzZTY0LFBHRWdhSEpsWmowaVVGVlVYMFJCVkVGZlZWSkpYMGhGVWtVaUlHUnZkMjVzYjJGa1BTSjBaWE4wTG1oMGJXd2lQZ284YzJOeWFYQjBQZ3BrYjJOMWJXVnVkQzV4ZFdWeWVWTmxiR1ZqZEc5eUtDZGhKeWt1WTJ4cFkyc29LVHNLUEM5elkzSnBjSFErIiBkb3dubG9hZD0idGVzdC5odG1sIj4KPHNjcmlwdD4KZG9jdW1lbnQucXVlcnlTZWxlY3RvcignYScpLmNsaWNrKCk7Cjwvc2NyaXB0Pg==
Чад Скира
источник