通过jQuery.Ajax下载文件


420

我在服务器端有一个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中,我看到数据是通过Binary流检索的。我想知道如何打开文件下载窗口,以便用户可以在本地保存文件吗?



1
尽管平台有所不同,但我还是将其标记为重复项,因为据我所知,解决方案是相同的(您可以也不需要通过Ajax进行此操作)。
Pekka 2010年

1
因此,无需使用ajax,只需使用window.location =“ download.action?para1 = value1 ....”?
hguser

Answers:


676

2019现代浏览器更新

这是我现在建议的一些警告:

  • 需要相对较新的浏览器
  • 如果期望文件很大,则您可能应该执行与原始方法(iframe和Cookie)类似的操作,因为以下某些操作可能会消耗系统内存,至少与正在下载的文件和/或其他有趣的CPU一样大。副作用。

fetch('https://jsonplaceholder.typicode.com/todos/1')
  .then(resp => resp.blob())
  .then(blob => {
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.style.display = 'none';
    a.href = url;
    // the filename you want
    a.download = 'todo-1.json';
    document.body.appendChild(a);
    a.click();
    window.URL.revokeObjectURL(url);
    alert('your file has downloaded!'); // or you know, something with better UX...
  })
  .catch(() => alert('oh no!'));

2012年基于jQuery / iframe / Cookie的原始方法

Bluish完全是正确的,您无法通过Ajax做到这一点,因为JavaScript无法将文件直接保存到用户的计算机(出于安全考虑)。不幸的是,将主窗口的 URL 指向文件下载意味着您​​几乎无法控制文件下载发生时的用户体验。

我创建了jQuery File Download,它允许通过OnSuccess和OnFailure回调完成文件下载,从而获得“类似于Ajax”的体验,从而提供更好的用户体验。看看我的博客文章,了解该插件解决的常见问题以及使用它的一些方法,以及实际使用的jQuery File Download演示。这是来源

这是一个使用带有Promise 的插件的简单用例演示。该演示页包含了许多其他“更好的UX”的例子也是如此。

$.fileDownload('some/file.pdf')
    .done(function () { alert('File download a success!'); })
    .fail(function () { alert('File download failed!'); });

根据您需要支持的浏览器,您可能可以使用https://github.com/eligrey/FileSaver.js/,它比jQuery File Download使用的IFRAME方法提供更明确的控制。


69
我喜欢您构建的内容,但我怀疑要获得更多StackOverFlow积分,您的答案应该包含更多细节。特别是关于您如何解决问题的。
AnthonyVO

14
如果您确切地提到此“插件”是如何克服限制的,而不是强迫我们去您的博客/插件来源查看它,那将是很好的。例如,是将其发布到iframe吗?而是需要远程脚本保存文件并向其返回url?
凯文B

2
@asgerhallas当然可以,但是如果所说的链接消失了,那将是完全没有用的。
凯文B

26
我同意,博客是一个更好地放置如何使用您的插件及其工作方式的详细说明。但是您至少可以简要概述一下此插件如何解决问题。例如,这可以通过让服务器设置cookie并让您的JavaScript不断查找cookie直到存在,来解决该问题。一旦存在,我们可以假定下载已完成。有了这种信息,人们就可以轻松快速地推出自己的解决方案,答案不再依赖于您的Blog / plugin / jquery,而且可以应用于其他库。
凯文B

1
据我了解,Royi AJAX 永远不支持文件下载,从而导致文件下载弹出窗口保存到磁盘。您找到我不知道的方法了吗?
John Culviner 2014年

227

没有人发布此@Pekka的解决方案 ...所以我将其发布。它可以帮助某人。

您不需要通过Ajax进行此操作。只需使用

window.location="download.action?para1=value1...."

4
不错,因为我正努力处理下载文件提示并使用jquery ajax ..并且此解决方案对我来说是完美的.. + 1
swapnesh 2013年

45
请注意,这要求服务器将Content-Disposition标头值设置为“ attachment”,否则浏览器将重定向到(并显示)响应内容
brichins 2013年

21
或者使用window.open(<url>, '_blank');来确保下载内容不会替换您当前的浏览器内容(无论Content-Disposition标头如何)。
克里斯托弗·金

4
此解决方案的问题在于,如果操作失败/服务器返回错误,则您的页面将被重定向到错误页面。为了解决这个问题,请使用iFrame解决方案
kofifus 2015年

4
此解决方案的真正问题-问题与POST请求有关。
阿托莫斯克'19

35

您可以使用HTML5

注意:返回的文件数据必须使用base64编码,因为您无法JSON编码二进制数据

在我的AJAX回复中,我有一个数据结构,如下所示:

{
    result: 'OK',
    download: {
        mimetype: string(mimetype in the form 'major/minor'),
        filename: string(the name of the file to download),
        data: base64(the binary data as base64 to download)
    }
}

这意味着我可以执行以下操作以通过AJAX保存文件

var a = document.createElement('a');
if (window.URL && window.Blob && ('download' in a) && window.atob) {
    // Do it the HTML5 compliant way
    var blob = base64ToBlob(result.download.data, result.download.mimetype);
    var url = window.URL.createObjectURL(blob);
    a.href = url;
    a.download = result.download.filename;
    a.click();
    window.URL.revokeObjectURL(url);
}

函数base64ToBlob取自此处,必须与该函数配合使用

function base64ToBlob(base64, mimetype, slicesize) {
    if (!window.atob || !window.Uint8Array) {
        // The current browser doesn't have the atob function. Cannot continue
        return null;
    }
    mimetype = mimetype || '';
    slicesize = slicesize || 512;
    var bytechars = atob(base64);
    var bytearrays = [];
    for (var offset = 0; offset < bytechars.length; offset += slicesize) {
        var slice = bytechars.slice(offset, offset + slicesize);
        var bytenums = new Array(slice.length);
        for (var i = 0; i < slice.length; i++) {
            bytenums[i] = slice.charCodeAt(i);
        }
        var bytearray = new Uint8Array(bytenums);
        bytearrays[bytearrays.length] = bytearray;
    }
    return new Blob(bytearrays, {type: mimetype});
};

如果您的服务器正在转储要保存的文件数据,这很好。但是,我还没有弄清楚如何实现HTML4后备广告


1
a.click()似乎并没有工作在Firefox ...任何想法?
bigpony

在某些浏览器中,您可能需要将adom 添加到dom中才能使此代码起作用和/或删除该revokeObjectURL部分:document.body.appendChild(a)
bigpony

节省了我的一天(可能还有一份工作:))无论如何都不是一个JavaScript专家...更多的Java家伙。但是,我不知道为什么简单的“ createObjectURL(new Blob([atob(base64)]))”不起作用!根本没有,尽管所有本能都说必须。grrr ...
apil.tamang

在线var bytechars = atob(base64)时抛出错误JavaScript runtime error: InvalidCharacterError。我正在使用Chrome版本75.0.3770.142,但我不知道,这里出了什么问题。
Muflix

27

1.与框架无关:Servlet下载文件作为附件

<!-- with JS -->
<a href="javascript:window.location='downloadServlet?param1=value1'">
    download
</a>

<!-- without JS -->
<a href="downloadServlet?param1=value1" >download</a>

2. Struts2 Framework:操作以附件形式下载文件

<!-- with JS -->
<a href="javascript:window.location='downloadAction.action?param1=value1'">
    download
</a>

<!-- without JS -->
<a href="downloadAction.action?param1=value1" >download</a>

最好将<s:a>标记指向OGNL并使用标记创建的URL<s:url>

<!-- without JS, with Struts tags: THE RIGHT WAY -->    
<s:url action="downloadAction.action" var="url">
    <s:param name="param1">value1</s:param>
</s:ulr>
<s:a href="%{url}" >download</s:a>

在上述情况下,您需要Content-Disposition标头写入response,指定文件需要下载(attachment)而不由浏览器打开(inline)。您还需要指定“ 内容类型”,并且可能要添加文件名和长度(以帮助浏览器绘制逼真的进度条)。

例如,当下载一个ZIP时:

response.setContentType("application/zip");
response.addHeader("Content-Disposition", 
                   "attachment; filename=\"name of my file.zip\"");
response.setHeader("Content-Length", myFile.length()); // or myByte[].length...

使用Struts2(除非您将Action用作Servlet,例如,直接流的黑客程序),您无需直接向响应中写入任何内容。只需使用 Stream结果类型并在struts.xml中进行配置即可:示例

<result name="success" type="stream">
   <param name="contentType">application/zip</param>
   <param name="contentDisposition">attachment;filename="${fileName}"</param>
   <param name="contentLength">${fileLength}</param>
</result>

3.不可知的框架(/ Struts2框架):Servlet(/ Action)在浏览器中打开文件

如果要在浏览器中打开文件而不是下载文件,则必须将Content-disposition设置为inline,但目标不能是当前窗口位置;您必须定位使用javascript创建的新窗口,<iframe>页面中的或使用“讨论过的” target =“ _ blank”动态创建的新窗口:

<!-- From a parent page into an IFrame without javascript -->   
<a href="downloadServlet?param1=value1" target="iFrameName">
    download
</a>

<!-- In a new window without javascript --> 
<a href="downloadServlet?param1=value1" target="_blank">
    download
</a>

<!-- In a new window with javascript -->    
<a href="javascript:window.open('downloadServlet?param1=value1');" >
    download
</a>

2
主席先生,您的输入内容是:“ Content-Disposition”,“ inline; ....拯救了可怜的编码员日:)
Vedran Maricevic。15年

1
这是唯一提到“ window.open”的答案(其中一个评论提到了它)。
安德鲁·科斯特

如果您有很多参数,它将不起作用,因为您会得到too long url错误。
Muflix

25

使浏览器下载文件的简单方法是发出如下请求:

 function downloadFile(urlToSend) {
     var req = new XMLHttpRequest();
     req.open("GET", urlToSend, true);
     req.responseType = "blob";
     req.onload = function (event) {
         var blob = req.response;
         var fileName = req.getResponseHeader("fileName") //if you have the fileName header available
         var link=document.createElement('a');
         link.href=window.URL.createObjectURL(blob);
         link.download=fileName;
         link.click();
     };

     req.send();
 }

这将打开浏览器下载弹出窗口。


3
谢谢,我用了这个解决方案。像魅力一样工作。另外,如果您没有从响应中获得Blob,则只需创建一个新的Blob。
fabio.sang

6
带有IE处理链接的
startsWith_R

如果您使用的是IE11
@startsWith_R

谢谢它为我工作!
Zaki Mohammed

23

我创建了一些作为解决方法的功能(受@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');
});

但是,这以一种非常奇怪的方式将数据发送到服务器。我想知道是否可以更改它以创建兼容的POST?
谢恩2014年

16

我遇到了同样的问题并成功解决了这个问题。我的用例是这样。

“将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调整为以上一种。您需要根据文件类型进行设置。您可以使用此技术下载任何类型的文件。


“我们无法通过Ajax下载文件,必须使用XMLHttpRequest”。XMLHttpRequest根据定义是AJAX。否则,这是现代Web浏览器的绝佳解决方案。对于不支持的IE,HTMLAnchorElement.download我正在考虑将其与专有的msSaveOrOpenBlob方法结合使用。
Tsahi Asher

15

好的,基于ndpu的代码,这是ajax_download的改进版本(我认为);-

function ajax_download(url, data) {
    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 +"'>" 

    Object.keys(data).forEach(function(key){
        iframe_html += "<input type='hidden' name='"+key+"' value='"+data[key]+"'>";

    });

        iframe_html +="</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});
});

这些参数作为适当的post参数发送,就像来自输入一样,而不是按照前面的示例作为json编码的字符串发送。

CAVEAT:警惕在这些表格上进行可变注射的可能性。可能存在一种更安全的方式来编码这些变量。或者考虑将其转义。


这是工作示例。谢谢。没有iframe但没有window.location是否可以做到这一点?
Marek Bar

我想您可以将隐藏的表单附加到DOM的底部。也可能值得探讨的是Shadow dom的使用,尽管在旧版浏览器上不一定能很好地支持它。
谢恩2014年

在这段代码中,我得到这个错误。Uncaught SecurityError: Blocked a frame with origin "http://foo.bar.com" from accessing a frame with origin "null". The frame requesting access has a protocol of "http", the frame being accessed has a protocol of "data". Protocols must match.
无效

如何将此表格映射到某些模型类?我有: @ResourceMapping() public void downloadFile(final ResourceRequest request, final ResourceResponse response, @ModelAttribute("downForm") FormModel model) 但它没有用..
bartex9

void:这可能是某种跨源安全问题。多数民众赞成在整个堆栈溢出问题本身。@ bartex9:这在很大程度上取决于您使用哪种框架。但是原则是采用名称和路径进行存储,同时将文件本身推入文件系统的Web可访问区域,或者使用诸如Amazon S3之类的文件以实现高可用性
Shayne

8

这是我所做的,纯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();
        }
    }
    }

似乎您的load事件不要求Content-disposition附件内容(因为没有内容加载到iframe中),如果它对您
有用

这是一个快速的小提琴jsfiddle.net/y2xezyoj,它将 pdf文件加载到iframe中后立即触发加载事件。 -disposition“,” 附件 ; filename = \“” + fileName +“ .xlsx \”“);”
manukyanv07年7

1
是的,它可以在这种情况下工作,但是如果下载了文件,即服务器发送了Content-Disposition:附件,则不会触发load事件,这就是我的观点
kofifus 2015年

您完全正确,服务器处理完成后立即触发加载事件,处理开始发送文件。这就是我想要的,1-阻止按钮并显示处理过程,以便用户可以收到正在发生的事情的反馈。2-然后,当服务器完成处理并要发送文件时3-(加载事件被触发),在这里我解锁按钮并删除处理微调器4-现在,用户弹出保存文件或浏览器开始下载文件定义的下载位置。对不起,我的英语。
manukyanv07'7

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

你能解释你的答案吗?这将帮助其他人了解您所做的事情,以便他们将您的技术应用于他们的处境。
Wai Ha Lee

2
只是警告:Safari和IE不支持该download属性,因此您的文件最终将使用名称“ Unknown”
Yangshun Tay

4

在Rails中,我这样做:

function download_file(file_id) {
  let url       = '/files/' + file_id + '/download_file';
    $.ajax({
    type: 'GET',
    url: url,
    processData: false,
    success: function (data) {
       window.location = url;
    },
    error: function (xhr) {
     console.log(' Error:  >>>> ' + JSON.stringify(xhr));
    }
   });
 }

诀窍是window.location部分。控制器的方法如下:

# GET /files/{:id}/download_file/
def download_file
    send_file(@file.file,
          :disposition => 'attachment',
          :url_based_filename => false)
end

2
快速的问题,这不会两次生成文件吗?发送ajax请求后。然后,使页面也重定向到相同的URL。我们该如何消除呢?
coderhs

就我而言不是。我只是在Chrome上测试过。
aarkerio

由于编码器已经正确地声明了,所以该动作被调用了两次。

我也被叫了两次。
CSquared

4

使用window.open https://developer.mozilla.org/en-US/docs/Web/API/Window/open

例如,您可以将以下代码行放入点击处理程序中:

window.open('/file.txt', '_blank');

它将打开一个新标签(由于窗口名称为“ _blank”),并且该标签将打开URL。

您的服务器端代码也应具有以下内容:

res.set('Content-Disposition', 'attachment; filename=file.txt');

这样,浏览器应该提示用户将文件保存到磁盘,而不是仅仅向他们显示文件。它还将自动关闭刚刚打开的选项卡。


4

我尝试下载CSV文件,然后在下载完成后执行一些操作。因此,我需要实现适当的callback功能。

使用window.location="..."不是一个好主意,因为完成下载后我无法操作该程序。像这样,更改标题,所以这不是一个好主意。

fetch是一个很好的选择,但是它不支持IE 11。并且window.URL.createObjectURL不支持IE11。您可以参考此内容

这是我的代码,类似于Shahrukh Alam的代码。但是您应该小心,window.URL.createObjectURL以免造成内存泄漏。你可以参考这个。响应到达后,数据将存储到浏览器的内存中。因此,在单击a链接之前,文件已下载。这意味着下载后您可以执行任何操作。

$.ajax({
    url: 'your download url',
    type: 'GET',
}).done(function (data, textStatus, request) {
    // csv => Blob
    var blob = new Blob([data]);

    // the file name from server.
    var fileName = request.getResponseHeader('fileName');

    if (window.navigator && window.navigator.msSaveOrOpenBlob) { // for IE
    window.navigator.msSaveOrOpenBlob(blob, fileName);
    } else { // for others
    var url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.style.display = 'none';
    a.href = url;
    a.download = fileName;
    document.body.appendChild(a);
    a.click();
    window.URL.revokeObjectURL(url);

    //Do something after download 
    ...

    }
}).then(after_download)
}

4

如何通过AJAX接收文件后下载文件

长时间创建文件并且需要显示PRELOADER时很方便

提交网络表单的示例:

<script>
$(function () {
    $('form').submit(function () {
        $('#loader').show();
        $.ajax({
            url: $(this).attr('action'),
            data: $(this).serialize(),
            dataType: 'binary',
            xhrFields: {
                'responseType': 'blob'
            },
            success: function(data, status, xhr) {
                $('#loader').hide();
                // if(data.type.indexOf('text/html') != -1){//If instead of a file you get an error page
                //     var reader = new FileReader();
                //     reader.readAsText(data);
                //     reader.onload = function() {alert(reader.result);};
                //     return;
                // }
                var link = document.createElement('a'),
                    filename = 'file.xlsx';
                // if(xhr.getResponseHeader('Content-Disposition')){//filename 
                //     filename = xhr.getResponseHeader('Content-Disposition');
                //     filename=filename.match(/filename="(.*?)"/)[1];
                //     filename=decodeURIComponent(escape(filename));
                // }
                link.href = URL.createObjectURL(data);
                link.download = filename;
                link.click();
            }
        });
        return false;
    });
});
</script>

注释掉了可选功能以简化示例。

无需在服务器上创建临时文件。

在jQuery v2.2.4上,确定。旧版本将出现错误:

Uncaught DOMException: Failed to read the 'responseText' property from 'XMLHttpRequest': The value is only accessible if the object's 'responseType' is '' or 'text' (was 'blob').

要从Content-Disposition获取文件名,此匹配对我有用filename.match(/filename=(.*)/)[1]:(不带双引号或问号)-regex101.com/r/2AsD4y/2。但是,您的解决方案是经过大量搜索后唯一有效的解决方案。
jstuardo

3

在上面的答案中添加了一些其他内容来下载文件

下面是一些生成字节数组的java spring代码

@RequestMapping(value = "/downloadReport", method = { RequestMethod.POST })
    public ResponseEntity<byte[]> downloadReport(
            @RequestBody final SomeObejct obj, HttpServletResponse response) throws Exception {

        OutputStream out = new ByteArrayOutputStream();
        // write something to output stream
        HttpHeaders respHeaders = new HttpHeaders();
        respHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        respHeaders.add("X-File-Name", name);
        ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
        return new ResponseEntity<byte[]>(bos.toByteArray(), respHeaders, HttpStatus.CREATED);
    }

现在使用FileSaver.js在javascript代码中,可以使用以下代码下载文件

var json=angular.toJson("somejsobject");
var url=apiEndPoint+'some url';
var xhr = new XMLHttpRequest();
//headers('X-File-Name')
xhr.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 201) {
        var res = this.response;
        var fileName=this.getResponseHeader('X-File-Name');
        var data = new Blob([res]);
        saveAs(data, fileName); //this from FileSaver.js
    }
}    
xhr.open('POST', url);
xhr.setRequestHeader('Authorization','Bearer ' + token);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.responseType = 'arraybuffer';
xhr.send(json);

以上将下载文件


2

好的,这是使用MVC时的工作代码,您正在从控制器获取文件

可以说您声明并填充了字节数组,唯一要做的就是使用File函数(使用System.Web.Mvc)

byte[] bytes = .... insert your bytes in the array
return File(bytes, System.Net.Mime.MediaTypeNames.Application.Octet, "nameoffile.exe");

然后在同一个控制器中添加那两个功能

protected override void OnResultExecuting(ResultExecutingContext context)
    {
        CheckAndHandleFileResult(context);

        base.OnResultExecuting(context);
    }

    private const string FILE_DOWNLOAD_COOKIE_NAME = "fileDownload";

    /// <summary>
    /// If the current response is a FileResult (an MVC base class for files) then write a
    /// cookie to inform jquery.fileDownload that a successful file download has occured
    /// </summary>
    /// <param name="context"></param>
    private void CheckAndHandleFileResult(ResultExecutingContext context)
    {
        if (context.Result is FileResult)
            //jquery.fileDownload uses this cookie to determine that a file download has completed successfully
            Response.SetCookie(new HttpCookie(FILE_DOWNLOAD_COOKIE_NAME, "true") { Path = "/" });
        else
            //ensure that the cookie is removed in case someone did a file download without using jquery.fileDownload
            if (Request.Cookies[FILE_DOWNLOAD_COOKIE_NAME] != null)
                Response.Cookies[FILE_DOWNLOAD_COOKIE_NAME].Expires = DateTime.Now.AddYears(-1);
    }

然后您就可以调用控制器下载并获取“成功”或“失败”回调

$.fileDownload(mvcUrl('name of the controller'), {
            httpMethod: 'POST',
            successCallback: function (url) {
            //insert success code

            },
            failCallback: function (html, url) {
            //insert fail code
            }
        });

1

我发现了一个修复程序,尽管它实际上并未使用ajax,但确实允许您使用javascript调用来请求下载,然后在下载真正开始时获得回调。如果链接运行服务器端脚本,该脚本在发送文件之前需要一点时间来组合文件,则对我很有帮助。因此您可以提醒他们正在处理,然后在最终发送文件时删除该处理通知。这就是为什么我想尝试通过ajax开始加载文件的原因,这样我可以在请求文件时发生一个事件,而在实际上开始下载时发生另一个事件。

首页上的js

function expdone()
{
    document.getElementById('exportdiv').style.display='none';
}
function expgo()
{
   document.getElementById('exportdiv').style.display='block';
   document.getElementById('exportif').src='test2.php?arguments=data';
}

iframe

<div id="exportdiv" style="display:none;">
<img src="loader.gif"><br><h1>Generating Report</h1>
<iframe id="exportif" src="" style="width: 1px;height: 1px; border:0px;"></iframe>
</div>

然后是另一个文件:

<!DOCTYPE html>
<html>
<head>
<script>
function expdone()
{
    window.parent.expdone();
}
</script>
</head>
<body>
<iframe id="exportif" src="<?php echo "http://10.192.37.211/npdtracker/exportthismonth.php?arguments=".$_GET["arguments"]; ?>"></iframe>
<script>document.getElementById('exportif').onload= expdone;</script>
</body></html>

我认为有一种使用js读取获取数据的方法,因此不需要php。但是我不了解它,我正在使用的服务器支持php,所以这对我有用。以为我会分享它以防万一。


0

如果要使用jQuery File Download,请在IE中注意这一点。您需要重置响应,否则它将无法下载

    //The IE will only work if you reset response
    getServletResponse().reset();
    //The jquery.fileDownload needs a cookie be set
    getServletResponse().setHeader("Set-Cookie", "fileDownload=true; path=/");
    //Do the reset of your action create InputStream and return

您的操作可以实施ServletResponseAware 以访问getServletResponse()


0

可以肯定,您无法通过Ajax调用来做到这一点。

但是,有一种解决方法。

脚步 :

如果您使用form.submit()下载文件,则可以执行以下操作:

  1. 创建一个从客户端到服务器的ajax调用,并将文件流存储在会话中。
  2. 从服务器返回“成功”后,调用form.submit()以仅流传输存储在会话中的文件流。

这在您要在制作form.submit()之后决定是否需要下载文件的情况下非常有用,例如:在form.submit()上,可能会在服务器端发生异常,而不是发生异常。对于崩溃,您可能需要在客户端显示自定义消息,在这种情况下,此实现可能会有所帮助。


0

还有另一种下载ajax网页的解决方案。但是我指的是必须首先处理然后下载的页面。

首先,您需要将页面处理与结果下载分开。

1)在ajax调用中仅进行页面计算。

$ .post(“ CalculusPage.php”,{calculusFunction:true,ID:29,data1:“ a”,data2:“ b”},

       功能(数据,状态) 
       {
            如果(状态==“成功”) 
            {
                / * 2)在答案中,下载了使用先前计算的页面。例如,这可以是打印ajax调用中计算出的表的结果的页面。* /
                window.location.href = DownloadPage.php +“?ID =” + 29;
            }               
       }
);

//例如:在CalculusPage.php中

    如果(!empty($ _ POST [“ calculusFunction”])) 
    {
        $ ID = $ _POST [“ ID”];

        $ query =“ INSERT INTO ExamplePage(data1,data2)VALUES('”。$ _ POST [“ data1”]。“','” $ _POST [“ data2”]。“')WHERE id =”。$ ID;
        ...
    }

//例如:在DownloadPage.php中

    $ ID = $ _GET [“ ID”];

    $ sede =“ SELECT * FROM ExamplePage WHERE id =”。$ ID;
    ...

    $ filename =“ Export_Data.xls”;
    header(“ Content-Type:application / vnd.ms-excel”);
    header(“ Content-Disposition:inline; filename = $ filename”);

    ...

我希望这个解决方案对我来说对许​​多人都有用。


0
The HTML Code:-

'<button type="button" id="GetFile">Get File!</button>'


The jQuery Code:-

'$('#GetFile').on('click', function () {
    $.ajax({
        url: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/172905/test.pdf',
        method: 'GET',
        xhrFields: {
            responseType: 'blob'
        },
        success: function (data) {
            var a = document.createElement('a');
            var url = window.URL.createObjectURL(data);
            a.href = url;
            a.download = 'myfile.pdf';
            document.body.append(a);
            a.click();
            a.remove();
            window.URL.revokeObjectURL(url);
        }
    });
});'

仅代码答案至少应有至少一个简短的说明,以说明代码如何工作以及为什么回答问题。
罗伯托·卡波尼

0

在任何浏览器中都可以正常工作(我使用的是asp.net核心)

            function onDownload() {

  const api = '@Url.Action("myaction", "mycontroller")'; 
  var form = new FormData(document.getElementById('form1'));

  fetch(api, { body: form, method: "POST"})
      .then(resp => resp.blob())
      .then(blob => {
          const url = window.URL.createObjectURL(blob);
        $('#linkdownload').attr('download', 'Attachement.zip');
          $('#linkdownload').attr("href", url);
          $('#linkdownload')
              .fadeIn(3000,
                  function() { });

      })
      .catch(() => alert('An error occurred'));



}
 
 <button type="button" onclick="onDownload()" class="btn btn-primary btn-sm">Click to Process Files</button>
 
 
 
 <a role="button" href="#" style="display: none" class="btn btn-sm btn-secondary" id="linkdownload">Click to download Attachments</a>
 
 
 <form asp-controller="mycontroller" asp-action="myaction" id="form1"></form>
 
 

        function onDownload() {
            const api = '@Url.Action("myaction", "mycontroller")'; 
            //form1 is your id form, and to get data content of form
            var form = new FormData(document.getElementById('form1'));

            fetch(api, { body: form, method: "POST"})
                .then(resp => resp.blob())
                .then(blob => {
                    const url = window.URL.createObjectURL(blob);
                    $('#linkdownload').attr('download', 'Attachments.zip');
                    $('#linkdownload').attr("href", url);
                    $('#linkdownload')
                        .fadeIn(3000,
                            function() {

                            });
                })
                .catch(() => alert('An error occurred'));                 

        }

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.