使用 HTML5/JavaScript 生成并保存文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2897619/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using HTML5/JavaScript to generate and save a file
提问by Toji
I've been fiddling with WebGL lately, and have gotten a Collada reader working. Problem is it's pretty slow (Collada is a very verbose format), so I'm going to start converting files to a easier to use format (probably JSON). I already have the code to parse the file in JavaScript, so I may as well use it as my exporter too! The problem is saving.
我最近一直在摆弄 WebGL,并且让 Collada 阅读器开始工作。问题是它很慢(Collada 是一种非常冗长的格式),所以我将开始将文件转换为更易于使用的格式(可能是 JSON)。我已经有了用 JavaScript 解析文件的代码,所以我也可以将它用作我的导出器!问题是节省。
Now, I know that I can parse the file, send the result to the server, and have the browser request the file back from the server as a download. But in reality the server has nothing to do with this particular process, so why get it involved? I already have the contents of the desired file in memory. Is there any way that I could present the user with a download using pure JavaScript? (I doubt it, but might as well ask...)
现在,我知道我可以解析文件,将结果发送到服务器,并让浏览器从服务器请求返回文件作为下载。但实际上服务器与这个特定的过程无关,那么为什么要涉及它呢?我已经在内存中拥有所需文件的内容。有什么方法可以使用纯 JavaScript 向用户展示下载内容?(我对此表示怀疑,但不妨问一下……)
And to be clear: I am not trying to access the filesystem without the users knowledge! The user will provide a file (probably via drag and drop), the script will transform the file in memory, and the user will be prompted to download the result. All of which should be "safe" activities as far as the browser is concerned.
并且要明确:我不会在用户不知情的情况下尝试访问文件系统!用户将提供一个文件(可能通过拖放),脚本将在内存中转换文件,并提示用户下载结果。就浏览器而言,所有这些都应该是“安全”的活动。
[EDIT]:I didn't mention it upfront, so the posters who answered "Flash" are valid enough, but part of what I'm doing is an attempt to highlight what can be done with pure HTML5... so Flash is right out in my case. (Though it's a perfectly valid answer for anyone doing a "real" web app.) That being the case it looks like I'm out of luck unless I want to involve the server. Thanks anyway!
[编辑]:我没有预先提到它,所以回答“Flash”的海报足够有效,但我正在做的部分工作是试图突出使用纯 HTML5 可以做什么......所以 Flash 是就我而言。(尽管对于任何人做“真正的”网络应用程序来说,这是一个完全有效的答案。)在这种情况下,除非我想涉及服务器,否则看起来我很不走运。不管怎么说,还是要谢谢你!
采纳答案by N?k
OK, creating a data:URI definitely does the trick for me, thanks to Matthew and Dennkster pointing that option out! Here is basically how I do it:
好的,创建一个 data:URI 对我来说绝对有用,感谢 Matthew 和 Dennkster 指出了这个选项!这基本上是我如何做到的:
1) get all the content into a string called "content" (e.g. by creating it there initially or by reading innerHTML of the tag of an already built page).
1) 将所有内容放入一个名为“content”的字符串中(例如,通过最初在那里创建它或通过读取已构建页面的标记的innerHTML)。
2) Build the data URI:
2)构建数据URI:
uriContent = "data:application/octet-stream," + encodeURIComponent(content);
There will be length limitations depending on browser type etc., but e.g. Firefox 3.6.12 works until at least 256k. Encoding in Base64 instead using encodeURIComponent might make things more efficient, but for me that was ok.
根据浏览器类型等会有长度限制,但例如 Firefox 3.6.12 至少可以工作到 256k。在 Base64 中编码而不是使用 encodeURIComponent 可能会使事情更高效,但对我来说这没问题。
3) open a new window and "redirect" it to this URI prompts for a download location of my JavaScript generated page:
3) 打开一个新窗口并将其“重定向”到这个 URI 提示我的 JavaScript 生成页面的下载位置:
newWindow = window.open(uriContent, 'neuesDokument');
That's it.
就是这样。
回答by Matěj Pokorny
Simple solution for HTML5 ready browsers...
适用于 HTML5 浏览器的简单解决方案...
function download(filename, text) {
var pom = document.createElement('a');
pom.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
pom.setAttribute('download', filename);
if (document.createEvent) {
var event = document.createEvent('MouseEvents');
event.initEvent('click', true, true);
pom.dispatchEvent(event);
}
else {
pom.click();
}
}
Usage
用法
download('test.txt', 'Hello world!');
回答by panzi
HTML5 defined a window.saveAs(blob, filename)method. It isn't supported by any browser right now. But there is a compatibility library called FileSaver.jsthat adds this function to most modern browsers (including Internet Explorer 10+). Internet Explorer 10 supports a navigator.msSaveBlob(blob, filename)method (MSDN), which is used in FileSaver.js for Internet Explorer support.
HTML5 定义了一个window.saveAs(blob, filename)方法。目前任何浏览器都不支持它。但是有一个名为FileSaver.js的兼容性库,可以将此功能添加到大多数现代浏览器(包括 Internet Explorer 10+)。Internet Explorer 10 支持一种navigator.msSaveBlob(blob, filename)方法 ( MSDN),该方法在 FileSaver.js 中用于 Internet Explorer 支持。
I wrote a blog postingwith more details about this problem.
我写了一篇博客文章,其中包含有关此问题的更多详细信息。
回答by bcmpinc
Saving large files
保存大文件
Long data URIs can give performance problems in browsers. Another option to save client-side generated files, is to put their contents in a Blob (or File) object and create a download link using URL.createObjectURL(blob). This returns an URL that can be used to retrieve the contents of the blob. The blob is stored inside the browser until either URL.revokeObjectURL()is called on the URL or the document that created it is closed. Most web browsers have support for object URLs, Opera Mini is the only one that does not support them.
长数据 URI 可能会给浏览器带来性能问题。保存客户端生成文件的另一种选择是将它们的内容放在 Blob(或文件)对象中,并使用URL.createObjectURL(blob). 这将返回一个可用于检索 blob 内容的 URL。该 blob 存储在浏览器中,直到URL.revokeObjectURL()在 URL 上调用或关闭创建它的文档。大多数网络浏览器都支持对象 URL,Opera Mini 是唯一不支持它们的浏览器。
Forcing a download
强制下载
If the data is text or an image, the browser can open the file, instead of saving it to disk. To cause the file to be downloaded upon clicking the link, you can use the the downloadattribute. However, not all web browsers have support for the download attribute. Another option is to use application/octet-streamas the file's mime-type, but this causes the file to be presented as a binary blob which is especially user-unfriendly if you don't or can't specify a filename. See also 'Force to open "Save As..." popup open at text link click for pdf in HTML'.
如果数据是文本或图像,浏览器可以打开文件,而不是将其保存到磁盘。要在单击链接时下载文件,您可以使用该download属性。然而,并不是所有的网络浏览器都支持下载属性。另一种选择是application/octet-stream用作文件的 MIME 类型,但这会导致文件显示为二进制 blob,如果您不指定或不能指定文件名,这对用户尤其不友好。另请参阅“强制打开“另存为...”弹出窗口,在文本链接单击 HTML 中的pdf 时打开。
Specifying a filename
指定文件名
If the blob is created with the File constructor, you can also set a filename, but only a few web browsers (including Chrome & Firefox) have support for the File constructor. The filename can also be specified as the argument to the downloadattribute, but this is subject to a ton of security considerations. Internet Explorer 10 and 11 provides its own method, msSaveBlob, to specify a filename.
如果 blob 是使用 File 构造函数创建的,您还可以设置文件名,但只有少数 Web 浏览器(包括 Chrome 和 Firefox)支持 File 构造函数。文件名也可以指定为download属性的参数,但这要考虑很多安全因素。Internet Explorer 10 和 11 提供了自己的方法msSaveBlob来指定文件名。
Example code
示例代码
var file;
var data = [];
data.push("This is a test\n");
data.push("Of creating a file\n");
data.push("In a browser\n");
var properties = {type: 'text/plain'}; // Specify the file's mime-type.
try {
// Specify the filename using the File constructor, but ...
file = new File(data, "file.txt", properties);
} catch (e) {
// ... fall back to the Blob constructor if that isn't supported.
file = new Blob(data, properties);
}
var url = URL.createObjectURL(file);
document.getElementById('link').href = url;
<a id="link" target="_blank" download="file.txt">Download</a>
回答by Yassir Ennazk
function download(content, filename, contentType)
{
if(!contentType) contentType = 'application/octet-stream';
var a = document.createElement('a');
var blob = new Blob([content], {'type':contentType});
a.href = window.URL.createObjectURL(blob);
a.download = filename;
a.click();
}
回答by Pekka
Take a look at Doug Neiner's Downloadifywhich is a Flash based JavaScript interface to do this.
看看 Doug Neiner 的Downloadify,它是一个基于 Flash 的 JavaScript 界面来实现这一点。
Downloadify is a tiny JavaScript + Flash library that enables the generation and saving of files on the fly, in the browser, without server interaction.
Downloadify 是一个很小的 JavaScript + Flash 库,可以在浏览器中即时生成和保存文件,无需服务器交互。
回答by T.Todua
Simple Solution!
简单的解决方案!
<a download="My-FileName.txt" href="data:application/octet-stream,HELLO-WORLDDDDDDDD">Click here</a>
Works in all Modern browsers.
适用于所有现代浏览器。
回答by Matthew Flaschen
回答by Razakhel
I've used FileSaver (https://github.com/eligrey/FileSaver.js) and it works just fine.
For example, I did this function to export logs displayed on a page.
You have to pass an array for the instanciation of the Blob, so I just maybe didn't write this the right way, but it works for me.
Just in case, be careful with the replace: this is the syntax to make this global, otherwise it will only replace the first one he meets.
我使用过 FileSaver ( https://github.com/eligrey/FileSaver.js),它工作得很好。
例如,我做了这个功能来导出页面上显示的日志。
你必须传递一个数组来实例化 Blob,所以我可能没有以正确的方式写这个,但它对我有用。
以防万一,小心替换:这是使这个全局化的语法,否则它只会替换他遇到的第一个。
exportLogs : function(){
var array = new Array();
var str = $('#logs').html();
array[0] = str.replace(/<br>/g, '\n\t');
var blob = new Blob(array, {type: "text/plain;charset=utf-8"});
saveAs(blob, "example.log");
}
回答by maikel
I found two simple approaches that work for me. First, using an already clicked aelement and injecting the download data. And second, generating an aelement with the download data, executing a.click()and removing it again. But the second approach works only if invoked by a user click action as well. (Some) Browser block click()from other contexts like on loading or triggered after a timeout (setTimeout).
我找到了两种对我有用的简单方法。首先,使用已经点击的a元素并注入下载数据。其次,a用下载数据生成一个元素,a.click()再次执行并删除它。但是第二种方法也只有在被用户点击操作调用时才有效。(某些)浏览器阻止click()其他上下文,例如加载时或超时后触发 (setTimeout)。
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<script type="text/javascript">
function linkDownload(a, filename, content) {
contentType = 'data:application/octet-stream,';
uriContent = contentType + encodeURIComponent(content);
a.setAttribute('href', uriContent);
a.setAttribute('download', filename);
}
function download(filename, content) {
var a = document.createElement('a');
linkDownload(a, filename, content);
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
</script>
</head>
<body>
<a href="#" onclick="linkDownload(this, 'test.txt', 'Hello World!');">download</a>
<button onclick="download('test.txt', 'Hello World!');">download</button>
</body>
</html>

