如何让浏览器提示用户保存服务器动态生成的文件?

2022-01-24 00:00:00 file save browser javascript ajax

客户端使用 jquery ajax 提交 post 请求,如下图所示:

Clients submit post requests using jquery ajax as illustrated below:

$.ajax({
    url: "/xxx?request1", 
    data: theParams,
    type: 'post',
    error: function(XMLHttpRequest, textStatus, errorThrown){
        // error handling
    },
    success: function(data){
    var theResult = JSON.parse(data);
        // success handling
    }
});

Apache 配置为将带有 /xxx? 的请求传递给自定义应用程序.应用程序处理请求并返回信息.通常,此信息以 JSoN 形式返回,并使用 jquery/javascript 显示在成功处理"部分.应用程序在写入 JSON 之前写入以下标头(然后通过 apache 返回):

Apache is configured to pass requests with /xxx? to a custom app. The app processes the request and returns information. Usually, this information returned as JSoN and is displayed in the "success handling" section, using jquery/javascript. The app writes the following headers (which are then returned via apache) prior to writing the JSON:

HTTP/1.0 200 OK
Connection: close
Server: MyServer v. 0.826
Content-Type: text/html

我还可以通过在 200 OK 行之后放置以下内容来使用 cookie:

I also have cookies working by placing the following right after the 200 OK line:

Set-Cookie: cookie_name=value; Domain=example.com; Path=/; Expires=Fri, 21-Mar-2014 01:40:22 GMT; HttpOnly

在一种情况下,我希望用户能够保存返回的数据(作为纯文本文件).但这是我遇到问题的地方.

In one scenario, I'd like the user to be able to save the returned data (as a plain text file). But here is where I run into problems.

如果我使用 JavaScript 打开一个窗口并将数据写入新窗口,我发现 Safari 和 Chrome 都禁用了另存为..."菜单选项(在 Mac 上).

If I use JavaScript to open a window and write the data to the new window, I've found that Safari and Chrome both disable the "Save as..." menu option (on Macs).

(我有一个使用 base64 URI 的版本,但相对于网站的其他部分而言,它看起来很粗糙.)

(I have got a version working by using base64 URI, but that looks unpolished relative to the rest of the site.)

然后我开始尝试包含一个 Content-Disposition: attachment;文件名=文件.txt 标头,但我永远无法让浏览器产生提示.我尝试将此标题放在 200 OK 行之后,以及稍后:

I then began trying to include a Content-Disposition: attachment; filename=file.txt header but I could never get the browser to produce a prompt. I tried placing this header right after the 200 OK line, as well as later:

Content-Type: text/plain
Content-Disposition: attachment; filename=file.txt

除了不确定 Content-Disposition 标头的确切格式之外,我还不确定 Content-Type 是否正确或ajax 处理程序代码中存在阻止保存的内容.

In addition to being not sure of the exact format of the Content-Disposition header, I'm also not sure if I have the Content-Type correct or if there's something in the ajax handler code that is preventing the save.

我试图找到将所有内容组合在一起的示例,但只能找到部分,显然我无法将它们正确组合在一起.有什么建议吗?

I've tried to find examples where everything is put together, but can only find parts and clearly I can't get it put together correctly. Any suggestions?

我只针对现代浏览器、三个主要平台(Mac/Windows/Linux)和客户端的 HTML5/JavaScript.

I'm targeting only modern browsers, three major platforms (Mac/Windows/Linux), and only HTML5/JavaScript on the client side.

推荐答案

Content-Disposition: attachment 是正确的header,但是浏览器只有在直接加载文件时才会服从.使用 AJAX(甚至通过 jQuery)将不起作用.

Content-Disposition: attachment is the right header, but the browser only obeys it when loading the file directly. Using AJAX (even through jQuery) will not work.

有办法做到这一点.阅读 this answer 中的完整说明,但基本上您只需将其写在 success 中功能:

There is a way to do this. Read the full explanation in this answer, but basically you just have to write that in your success function:

window.location = "data:application/octet-stream," + encodeURIComponent(theResult);

FileSaver.js 提供了一个包装器 提高浏览器兼容性并添加对兼容浏览器文件名的支持:

FileSaver.js provides a wrapper improving browser compatibility and adding support for filenames for compatible browsers:

var blob = new Blob(["Hello, world!"], {type: "text/plain;charset=utf-8"});
saveAs(blob, "hello world.txt");

相关文章