我在服务器端有一个Struts2操作用于文件下载。

<action name="download" class="com.xxx.DownAction">
    <result name="success" type="stream">
        <param name="contentType">text/plain</param>
        <param name="inputName">imageStream</param>
        <param name="contentDisposition">attachment;filename={fileName}</param>
        <param name="bufferSize">1024</param>
    </result>
</action>

然而,当我使用jQuery调用动作时:

$.post(
  "/download.action",{
    para1:value1,
    para2:value2
    ....
  },function(data){
      console.info(data);
   }
);

在Firebug中,我看到数据是用二进制流检索的。我想知道如何打开文件下载窗口,用户可以在本地保存文件?


当前回答

我为这个问题挣扎了很长一段时间。最后,这里建议的一个优雅的外部库帮助了我。

其他回答

我也遇到过同样的问题,并成功地解决了它。我的用例是这样的。

“将JSON数据发送到服务器并接收一个excel文件。 该excel文件由服务器创建,并作为响应返回给客户机。在浏览器中下载具有自定义名称的响应文件”

$("#my-button").on("click", function(){

// Data to post
data = {
    ids: [1, 2, 3, 4, 5]
};

// Use XMLHttpRequest instead of Jquery $ajax
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
    var a;
    if (xhttp.readyState === 4 && xhttp.status === 200) {
        // Trick for making downloadable link
        a = document.createElement('a');
        a.href = window.URL.createObjectURL(xhttp.response);
        // Give filename you wish to download
        a.download = "test-file.xls";
        a.style.display = 'none';
        document.body.appendChild(a);
        a.click();
    }
};
// Post data to URL which handles post request
xhttp.open("POST", excelDownloadUrl);
xhttp.setRequestHeader("Content-Type", "application/json");
// You should set responseType as blob for binary responses
xhttp.responseType = 'blob';
xhttp.send(JSON.stringify(data));
});

上面的代码片段只是执行以下操作

使用XMLHttpRequest将数组作为JSON发送到服务器。 在获取内容作为一个blob(二进制)后,我们正在创建一个可下载的URL,并将其附加到不可见的“a”链接,然后单击它。我在这里做了一个POST请求。相反,您也可以使用简单的GET。我们不能通过Ajax下载文件,必须使用XMLHttpRequest。

这里我们需要小心地在服务器端设置一些东西。我在Python Django HttpResponse中设置了几个头。如果使用其他编程语言,则需要相应地设置它们。

# In python django code
response = HttpResponse(file_content, content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")

由于我在这里下载xls(excel),我将contentType调整为1以上。您需要根据您的文件类型设置它。您可以使用这种技术下载任何类型的文件。

我的方法完全基于jQuery。对我来说,问题是它必须是一个POST-HTTP调用。我希望它是由jQuery单独完成。

解决方案:

$.ajax({
    type: "POST",
    url: "/some/webpage",
    headers: {'X-CSRF-TOKEN': csrfToken},
    data: additionalDataToSend,
    dataType: "text",
    success: function(result) {
        let blob = new Blob([result], { type: "application/octetstream" }); 

        let a = document.createElement('a');
        a.href = window.URL.createObjectURL(blob);
        a.download = "test.xml";;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        window.URL.revokeObjectURL(a.href);
                        
        ...
    },
    error: errorDialog
});

解释:

我和很多人所做的是在网页上创建一个链接,指出应该下载目标,并将http-request的结果作为目标。之后,我将链接附加到文档,而不是简单地单击链接,然后删除链接。你不再需要iframe了。

神奇之处在于线条

let blob = new Blob([result], { type: "application/octetstream" }); 
a.href = window.URL.createObjectURL(blob);

The interesting point is that this solution is only working with a "blob". As you can see in other answers, some are simply using a blob but not explaining why and how to create it. As you can read e.g. in the Mozilla developer documentation you need a file, media ressource or blob for the function "createObjectURL()" to work. The problem is that your http-response might not be any of those. Therefore the first thing you must do is to convert your response to a blob. This is what the first line does. Then you can use the "createObjectURL" with your newly created blob. If you than click the link your browser will open a file-save dialog and you can save your data. Obviously it s possible that you cannot define a fixed filename for your file to download. Then you must make your response more complex like in the answer from Luke.

不要忘记释放内存,特别是在处理大文件时。要获得更多的示例和信息,您可以查看JS blob对象的详细信息

这是我所做的,纯javascript和html。没有测试,但这应该在所有浏览器工作。

Javascript函数

var iframe = document.createElement('iframe');
iframe.id = "IFRAMEID";
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframe.src = 'SERVERURL'+'?' + $.param($scope.filtro);
iframe.addEventListener("load", function () {
     console.log("FILE LOAD DONE.. Download should start now");
});

只使用所有浏览器都支持的组件,不添加其他组件 库。

下面是我的服务器端JAVA Spring控制器代码。

@RequestMapping(value = "/rootto/my/xlsx", method = RequestMethod.GET)
public void downloadExcelFile(@RequestParam(value = "param1", required = false) String param1,
    HttpServletRequest request, HttpServletResponse response)
            throws ParseException {

    Workbook wb = service.getWorkbook(param1);
    if (wb != null) {
        try {
            String fileName = "myfile_" + sdf.format(new Date());
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-disposition", "attachment; filename=\"" + fileName + ".xlsx\"");
            wb.write(response.getOutputStream());
            response.getOutputStream().close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    }
function downloadURI(uri, name) 
{
    var link = document.createElement("a");
    link.download = name;
    link.href = uri;
    link.click();
}

我创建了一个小函数作为解决方案(灵感来自@JohnCulviner插件):

// creates iframe and form in it with hidden field,
// then submit form with provided data
// url - form url
// data - data to form field
// input_name - form hidden input name

function ajax_download(url, data, input_name) {
    var $iframe,
        iframe_doc,
        iframe_html;

    if (($iframe = $('#download_iframe')).length === 0) {
        $iframe = $("<iframe id='download_iframe'" +
                    " style='display: none' src='about:blank'></iframe>"
                   ).appendTo("body");
    }

    iframe_doc = $iframe[0].contentWindow || $iframe[0].contentDocument;
    if (iframe_doc.document) {
        iframe_doc = iframe_doc.document;
    }

    iframe_html = "<html><head></head><body><form method='POST' action='" +
                  url +"'>" +
                  "<input type=hidden name='" + input_name + "' value='" +
                  JSON.stringify(data) +"'/></form>" +
                  "</body></html>";

    iframe_doc.open();
    iframe_doc.write(iframe_html);
    $(iframe_doc).find('form').submit();
}

演示点击事件:

$('#someid').on('click', function() {
    ajax_download('/download.action', {'para1': 1, 'para2': 2}, 'dataname');
});