使用data: URI时,有没有办法指定建议的文件名?

275
如果你举个例子,点击这个链接:
data:application/octet-stream;base64,SGVsbG8=

浏览器会提示您下载一个文件,该文件包含在超链接本身中以base64形式保存的数据。在标记中是否有一种方式可以建议一个默认名称?如果没有,是否有JavaScript解决方案?

也许与此问题无关,但我建议如果这不是服务器或旧浏览器的障碍,可以使用Blob和URL.createObjectURL。 - Endless
8
一些浏览器支持多媒体类型的可选参数 "name":data:application/pdf;name=document.pdf;base64,BASE64_DATA_ENCODED - mems
我遇到了Firefox的pdf.js问题,如果它无法从数据URI中提取文件名,则在某些情况下会出现挂起的情况。请参见https://stackoverflow.com/questions/45585921/firefox-hangs-when-displaying-a-pdf-via-data-url/45585922#45585922 - Bernhard
2
@mems 哪些浏览器支持“name”参数?你能给我指一些参考文档吗?(我的谷歌搜索失败了)。 - TheAddonDepot
1
@DimuDesigns 至少在那个时候是这样的。看起来现在不再是这种情况了。这与 MIME Content-Type (!= Content-Disposition) 的 "name" 参数有关(不在 RFC 中?) - mems
这适用于 Blob URI 吗?如何为 Blob 数据设置下载文件扩展名 - LF00
17个回答

214

3
@BioDesign:在 Chrome 中,即使使用 data:URI,它也能正常工作。请参见:http://jsfiddle.net/pYpqW/。 - Senseful
6
但是你不能使用window.location.replace来实现。例如,如果你想创建一个由window.URL.createObjectURL生成的data:uri,并将其作为文件下载,你需要创建一个<a>标签并点击它:http://jsfiddle.net/flyingsheep/wpQtH/ (不,$(...).click()不起作用)。 - flying sheep
6
在这里(Chrome 23),$('<a href="data:text/plain,Test" download="test.txt">')[0].click() 看起来运行良好(注意:我使用了本地的 click 方法,而不是 jQuery 的)。演示链接:http://jsfiddle.net/2zsRW/。 - Rob W
3
@flyingsheep,看起来他们正在Firefox中执行同源策略:“在Firefox 20中,此属性仅适用于指向具有相同来源的资源的链接。” https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a 在我的测试中,Chrome没有这个限制。 - William Denniss
2
这个解决方案的问题在于,如果用户单击链接,文件将被下载。如果您只想在用户右键单击+另存为时指定文件名怎么办? - phn
显示剩余9条评论

71

如今,Chrome让这个过程非常简单:

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

我不知道其他答案在说什么,但这在 Chrome 30 上第一次尝试就成功了。 - Michael J. Calkins
2
现在可以了,但过去并不那么容易。许多答案都是几年前的。它们也适用于其他浏览器。 - Holf
9
请参考http://caniuse.com/#feat=download获取浏览器兼容性完整列表。 - tixastronauta
2
@tixastronauta:尽管该页面上有信息,但在我的Firefox 44中无法工作。在Chrome中运行得很好。48 - Luis A. Florit
有一种更好的方式,因为通过这种方式不能保存分段的文本文件。我的变量是: `var file = new File(rows, "file.csv", {type: 'text/csv'});var link = document.createElement('a'); link.href = URL.createObjectURL(file); link.download = file.name; link.click();`我在这里找到了答案: https://dev59.com/OWYr5IYBdhLWcg3wxM-8 - barbariania
显示剩余2条评论

61

仅限HTML:使用download属性:

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


Javascript only: 你可以使用以下代码保存任何数据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属性)将以默认名称下载文件,或者如果文件类型是图片、视频、音频等受支持的文件类型,则在新标签页中直接显示


为了获得更完整的解决方案,我建议在npm上使用downloadjs - fregante
它对我有效,但浏览器页面在此之后刷新。不知道如何防止这种情况? - user6269864
1
由于Chrome的限制,文件大小大于2MB时无法在Chrome中工作。请参考https://dev59.com/SHRB5IYBdhLWcg3wLkxM。 - Pranav Singh
限制属于data: URI,这就是问题所提到的。此答案也适用于Blob和任何具有URI的内容。 - fregante
运行得很顺利。做得非常好。 - Aarmora

42
根据RFC 2397,答案是否定的。 也没有任何可以使用的<a>元素的attribute 然而,HTML5随后引入了<a>元素上的download属性,但在撰写本文时,支持并不普遍(例如没有MSIE支持)。

10
写作时第二句话是正确的,但现在不再正确。目前尽管还没有被广泛采用。 Translated: The second sentence was correct when it was written, but it is no longer correct. As of now, it has not yet been widely implemented. - flying sheep
请参阅 此评论 了解更多详细信息 :) - flying sheep
@flyingsheep,它被广泛实现。 - Pacerier
1
我写下那条评论还不到3年的时间。 - flying sheep
如果文件太长,下载会失败。 - deFreitas
如果有人想知道支持情况,这里是该功能的 caniuse 页面。 - 3ocene

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


3
很酷!虽然我不一定同意 Firefox 对于存在的事物是终极权威 :) - Gleno

16
以下Javascript代码片段通过使用链接的新“download”属性并模拟点击,在Chrome中起作用。

以下JavaScript代码片段利用链接的新“download”属性并模拟点击,在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")

1
这在Firefox中不起作用,我在下面添加了一个扩展答案,其中包括Fx的兼容性。 - fregante

12

不。

整个目的是它是一个数据流,而非文件。数据源不应该知道用户代理将其处理为文件…也确实没有这种情况。


7
data: 的目的是将一个块的内部数据伪装成 URL 格式,而无需从协议源中读取它。@silex 回答中的链接显示,即使尚未实现,建议提供首选名称以进行写入也被认为很有用。 - Alnitak
1
@Alnitak:有用吗?绝对有。技术上是否合适?我还不确定。 :) - Lightness Races in Orbit
3
考虑加载数据和保存数据之间的区别-仅因为一个 blob 在 data: URL 中被编码成行内元素并不意味着它不应该有一个首选名称用于保存它。@Tomalak - Alnitak
4
但是,你提到它的“全部目的”是错误的。data: 的特定发明目的是允许(小型)的 内联 内容以拼凑在一起的 URL 格式出现,以便可以被像图像标签这样的东西使用,而无需单独进行HTTP请求。HTML 规定 img src 属性的内容必须是一个URL,因此 RFC 2397 就创造了它。没有“数据源”。 - Alnitak
6
@Alnitak:没错。没有数据来源。没有上下文。URI本身就是数据。 - Lightness Races in Orbit
显示剩余3条评论

11
你可以在锚元素上添加download属性。
示例:
<a download="abcd.cer"
    href="data:application/stream;base64,MIIDhTC......">down</a>

9
利用service workers,这一点终于在最真实的意义上变得可能。
  1. 创建一个虚假URL。例如/saveAs/myPrettyName.jpg
  2. 将URL用于 <a href, <img src,window.open( url )等任何可以使用“真实”URL的地方。
  3. 在worker内,捕获fetch事件并响应正确的数据。

如果用户在新标签页中打开文件并尝试在那里保存文件,浏览器现在将建议使用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 );
    }
});

1
有趣!不过目前支持似乎还比较薄弱:http://caniuse.com/#feat=serviceworkers - tuomassalo
有没有办法用另一个直接的URL来“响应”文件? - Iulian Onofrei

5

请查看此链接: http://lists.w3.org/Archives/Public/uri/2010Feb/0069.html

引用:

它甚至可以像这样在末尾添加;base64(至少在Opera中):

data: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

讨论中的其余消息中还有一些信息。


很遗憾,这个无法下载。 - James Khoury
9
这次讨论是关于对数据URI格式的 拟议 扩展,尚未实施。 - Alnitak
无论是否实现,只要存在对任意参数的支持,这都将是一个很棒的功能。 - Dan Lugg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接