Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/388.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 处理从ajax post下载的文件_Javascript_Jquery_Ajax - Fatal编程技术网

Javascript 处理从ajax post下载的文件

Javascript 处理从ajax post下载的文件,javascript,jquery,ajax,Javascript,Jquery,Ajax,我有一个javascript应用程序,它将ajax POST请求发送到某个URL。响应可能是JSON字符串,也可能是文件(作为附件)。我可以很容易地在ajax调用中检测内容类型和内容处置,但是一旦我检测到响应包含一个文件,我如何向客户机提供下载它的服务?我在这里读过很多类似的帖子,但没有一个能提供我想要的答案 请,请,请不要张贴建议我不应该使用ajax的答案,或者我应该重定向浏览器,因为这都不是一个选项。使用普通HTML表单也不是一个选项。我需要的是向客户端显示一个下载对话框。这可以做到吗?如何

我有一个javascript应用程序,它将ajax POST请求发送到某个URL。响应可能是JSON字符串,也可能是文件(作为附件)。我可以很容易地在ajax调用中检测内容类型和内容处置,但是一旦我检测到响应包含一个文件,我如何向客户机提供下载它的服务?我在这里读过很多类似的帖子,但没有一个能提供我想要的答案


请,请,请不要张贴建议我不应该使用ajax的答案,或者我应该重定向浏览器,因为这都不是一个选项。使用普通HTML表单也不是一个选项。我需要的是向客户端显示一个下载对话框。这可以做到吗?如何做到?

创建表单,使用POST方法,提交表单-不需要iframe。当服务器页面响应请求时,为文件的mime类型编写一个响应头,它将显示一个下载对话框——我已经做了很多次了


您需要应用程序/下载的内容类型-只需搜索如何为您使用的任何语言提供下载。

您使用的是哪种服务器端语言?在我的应用程序中,通过在PHP响应中设置正确的标题,我可以轻松地从AJAX调用下载文件:

在服务器端设置标题 这实际上会将浏览器“重定向”到这个下载页面,但正如@ahren alread在评论中所说,它不会离开当前页面

这一切都是关于设置正确的头文件的,因此我相信,如果不是PHP,您将为所使用的服务器端语言找到合适的解决方案

处理客户端的响应 假设您已经知道如何进行AJAX调用,那么在客户端向服务器执行AJAX请求。然后,服务器生成一个可从中下载此文件的链接,例如,要指向的“转发”URL。 例如,服务器响应为:

{
    status: 1, // ok
    // unique one-time download token, not required of course
    message: 'http://yourwebsite.com/getdownload/ska08912dsa'
}
在处理响应时,您在体内注入一个
iframe
,并将
iframe
的SRC设置为您刚刚收到的URL,如下所示(为了简化本例,使用jQuery):

$(“正文”)。追加(“”);
如果如上所示设置了正确的标题,iframe将强制执行下载对话框,而不会将浏览器导航到当前页面之外


与你的问题有关的额外补充;我认为在使用AJAX技术请求内容时,最好总是返回JSON。收到JSON响应后,您可以决定客户端如何处理它。例如,您可能希望用户稍后单击指向URL的下载链接,而不是直接强制下载,在当前设置中,您必须同时更新客户端和服务器端才能做到这一点。

不要这么快放弃,因为这可以使用FileAPI的部分来完成(在现代浏览器中):

var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.responseType = 'blob';
xhr.onload = function () {
    if (this.status === 200) {
        var blob = this.response;
        var filename = "";
        var disposition = xhr.getResponseHeader('Content-Disposition');
        if (disposition && disposition.indexOf('attachment') !== -1) {
            var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
            var matches = filenameRegex.exec(disposition);
            if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
        }

        if (typeof window.navigator.msSaveBlob !== 'undefined') {
            // IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed."
            window.navigator.msSaveBlob(blob, filename);
        } else {
            var URL = window.URL || window.webkitURL;
            var downloadUrl = URL.createObjectURL(blob);

            if (filename) {
                // use HTML5 a[download] attribute to specify filename
                var a = document.createElement("a");
                // safari doesn't support this yet
                if (typeof a.download === 'undefined') {
                    window.location.href = downloadUrl;
                } else {
                    a.href = downloadUrl;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                }
            } else {
                window.location.href = downloadUrl;
            }

            setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup
        }
    }
};
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send($.param(params, true));
或者如果使用jQuery.ajax:

$.ajax({
    type: "POST",
    url: url,
    data: params,
    xhrFields: {
        responseType: 'blob' // to avoid binary data being mangled on charset conversion
    },
    success: function(blob, status, xhr) {
        // check for a filename
        var filename = "";
        var disposition = xhr.getResponseHeader('Content-Disposition');
        if (disposition && disposition.indexOf('attachment') !== -1) {
            var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
            var matches = filenameRegex.exec(disposition);
            if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
        }

        if (typeof window.navigator.msSaveBlob !== 'undefined') {
            // IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed."
            window.navigator.msSaveBlob(blob, filename);
        } else {
            var URL = window.URL || window.webkitURL;
            var downloadUrl = URL.createObjectURL(blob);

            if (filename) {
                // use HTML5 a[download] attribute to specify filename
                var a = document.createElement("a");
                // safari doesn't support this yet
                if (typeof a.download === 'undefined') {
                    window.location.href = downloadUrl;
                } else {
                    a.href = downloadUrl;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                }
            } else {
                window.location.href = downloadUrl;
            }

            setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup
        }
    }
});

我知道你已经找到了一个解决方案,但是我只是想添加一些信息,这些信息可能会帮助那些试图通过大的帖子请求实现同样的目标的人

几周前我也遇到过同样的问题,事实上,通过AJAX实现“干净”的下载是不可能的,Filliames小组创建了一个jQuery插件,它的工作方式与您已经发现的完全相同,它被称为jQuery,但这项技术有一个缺点

如果您通过AJAX发送大请求(比如文件+1MB),这将对响应能力产生负面影响。在慢速Internet连接中,您必须等待很长时间,直到发送请求,并且还要等待文件下载。它不像即时的“点击”=>“弹出”=>“下载开始”。它更像是“单击”=>“等待数据发送”=>“等待响应”=>“下载开始”,这使得文件看起来是其大小的两倍,因为您必须等待请求通过AJAX发送,并将其作为可下载文件返回


如果您像其他人所说的那样使用较小的文件大小,您可以通过POST请求创建并提交表单以供下载。但是,您不必手动执行此操作

一个真正简单的库可以做到这一点。它提供了一个类似于标准
jQuery.post
方法的API:

$.redirect(url, [values, [method, [target]]])
我用过这个。在我使用csv文件的情况下,我这样做了(在coffescript中):


我认为对于最复杂的情况,数据必须得到适当的处理。在幕后,FileSaver.js实现了与答案相同的方法。

对于那些从角度寻找解决方案的人来说,这对我来说很有用:

$http.post(
  'url',
  {},
  {responseType: 'arraybuffer'}
).then(function (response) {
  var headers = response.headers();
  var blob = new Blob([response.data],{type:headers['content-type']});
  var link = document.createElement('a');
  link.href = window.URL.createObjectURL(blob);
  link.download = "Filename";
  link.click();
});

我想指出在接受的答案中使用技巧时出现的一些困难,即使用表单帖子:

  • 无法对请求设置标题。如果您的身份验证模式涉及头,即在授权头中传递的Json Web令牌,则必须找到其他发送方式,例如作为查询参数

  • 你真的不知道请求何时完成。好吧,你可以使用一个cookie,它可以根据响应进行设置,正如所做的那样,但它还远远不够完美。它不适用于并发请求,如果响应从未到达,它将中断

  • 如果服务器响应错误,用户将被重定向到错误页面

  • 您只能使用支持的内容类型。这意味着您不能使用JSON


  • 我最终使用了在S3上保存文件并发送预签名URL的方法来获取文件。

    这是一个3年前的问题,但今天我遇到了同样的问题。我查看了您编辑的解决方案,但我认为它可能会牺牲性能,因为它必须提出双重请求。因此,如果任何人需要另一种解决方案,而这种解决方案并不意味着要调用服务两次,那么我就是这样做的:

    <form id="export-csv-form" method="POST" action="/the/path/to/file">
        <input type="hidden" name="anyValueToPassTheServer" value="">
    </form>
    
    
    
    此表单仅用于调用服务并避免使用window.location()。之后,您只需从jquery提交一个表单,即可调用ser
    $.redirect(url, [values, [method, [target]]])
    
      $.ajax
        url: "url-to-server"
        data: "data-to-send"
        success: (csvData)->
          blob = new Blob([csvData], { type: 'text/csv' })
          saveAs(blob, "filename.csv")
    
    $http.post(
      'url',
      {},
      {responseType: 'arraybuffer'}
    ).then(function (response) {
      var headers = response.headers();
      var blob = new Blob([response.data],{type:headers['content-type']});
      var link = document.createElement('a');
      link.href = window.URL.createObjectURL(blob);
      link.download = "Filename";
      link.click();
    });
    
    <form id="export-csv-form" method="POST" action="/the/path/to/file">
        <input type="hidden" name="anyValueToPassTheServer" value="">
    </form>
    
    $("#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));
    });
    
    # In python django code
    response = HttpResponse(file_content, content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
    
    //Create an hidden form
    var form = $('<form>', {'method': 'POST', 'action': this.href}).hide();
    
    //Add params
    var params = { ...your params... };
    $.each(params, function (k, v) {
        form.append($('<input>', {'type': 'hidden', 'name': k, 'value': v}));
    });
    
    //Make it part of the document and submit
    $('body').append(form);
    form.submit();
    
    //Clean up
    form.remove();
    
    var blob = typeof File === 'function'
        ? new File([this.response], filename, { type: type })
        : new Blob([this.response], { type: type });
    
    var f = typeof File+"";
    var blob = f === 'function' && Modernizr.fileapi
        ? new File([this.response], filename, { type: type })
        : new Blob([this.response], { type: type });
    
        String contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
        // Set headers
        response.setHeader("content-disposition", "attachment; filename =" + fileName);
        response.setContentType(contentType);
        // Copy file to output stream
        ServletOutputStream servletOutputStream = response.getOutputStream();
        try (InputStream inputStream = new FileInputStream(file)) {
            IOUtils.copy(inputStream, servletOutputStream);
        } finally {
            servletOutputStream.flush();
            Utils.closeQuitely(servletOutputStream);
            fileToDownload = null;
        }
    
    $.ajax({
    type: 'POST',
    contentType: 'application/json',
        url: <download file url>,
        data: JSON.stringify(postObject),
        error: function(XMLHttpRequest, textStatus, errorThrown) {
            alert(errorThrown);
        },
        success: function(message, textStatus, response) {
           var header = response.getResponseHeader('Content-Disposition');
           var fileName = header.split("=")[1];
           var blob = new Blob([message]);
           var link = document.createElement('a');
           link.href = window.URL.createObjectURL(blob);
           link.download = fileName;
           link.click();
        }
    });   
    
    $.post("CalculusPage.php", { calculusFunction: true, ID: 29, data1: "a", data2: "b" }, function(data, status) { if (status == "success") { /* 2) In the answer the page that uses the previous calculations is downloaded. For example, this can be a page that prints the results of a table calculated in the ajax call. */ window.location.href = DownloadPage.php+"?ID="+29; } } ); // For example: in the CalculusPage.php if ( !empty($_POST["calculusFunction"]) ) { $ID = $_POST["ID"]; $query = "INSERT INTO ExamplePage (data1, data2) VALUES ('".$_POST["data1"]."', '".$_POST["data2"]."') WHERE id = ".$ID; ... } // For example: in the 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"); ...
     if (event.data instanceof ArrayBuffer) {
              var binary = '';
              var bytes = new Uint8Array(event.data);
              for (var i = 0; i < bytes.byteLength; i++) {
                  binary += String.fromCharCode(bytes[i])
              }
              $("#some_id").append("<li><img src=\"data:image/png;base64," + window.btoa(binary) + "\"/></span></li>");
              return;
          }
    
      success: function (data, textStatus, xhr) {
    
    var i = 0;
    var max = 0;
    function DownloadMultipleFiles() {
                if ($(".dataTables_scrollBody>tr.selected").length > 0) {
                    var list = [];
                    showPreloader();
                    $(".dataTables_scrollBody>tr.selected").each(function (e) {
                        var element = $(this);
                        var orderid = element.data("orderid");
                        var iscustom = element.data("iscustom");
                        var orderlineid = element.data("orderlineid");
                        var folderPath = "";
                        var fileName = "";
    
                        list.push({ orderId: orderid, isCustomOrderLine: iscustom, orderLineId: orderlineid, folderPath: folderPath, fileName: fileName });
                    });
                    i = 0;
                    max = list.length;
                    DownloadFile(list);
                }
            }
    
    function DownloadFile(list) {
            $.ajax({
                url: '@Url.Action("OpenFile","OrderLines")',
                type: "post",
                data: list[i],
                xhrFields: {
                    responseType: 'blob'
                },
                beforeSend: function (xhr) {
                    xhr.setRequestHeader("RequestVerificationToken",
                        $('input:hidden[name="__RequestVerificationToken"]').val());
    
                },
                success: function (data, textStatus, xhr) {
                    // check for a filename
                    var filename = "";
                    var disposition = xhr.getResponseHeader('Content-Disposition');
                    if (disposition && disposition.indexOf('attachment') !== -1) {
                        var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
                        var matches = filenameRegex.exec(disposition);
                        if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
                        var a = document.createElement('a');
                        var url = window.URL.createObjectURL(data);
                        a.href = url;
                        a.download = filename;
                        document.body.append(a);
                        a.click();
                        a.remove();
                        window.URL.revokeObjectURL(url);
                    }
                    else {
                        getErrorToastMessage("Production file for order line " + list[i].orderLineId + " does not exist");
                    }
                    i = i + 1;
                    if (i < max) {
                        DownloadFile(list);
                    }
                },
                error: function (XMLHttpRequest, textStatus, errorThrown) {
    
                },
                complete: function () {
                    if(i===max)
                    hidePreloader();
                }
            });
        }
    
     [HttpPost]
     [ValidateAntiForgeryToken]
    public IActionResult OpenFile(OrderLineSimpleModel model)
            {
                byte[] file = null;
    
                try
                {
                    if (model != null)
                    {
                        //code for getting file from api - part is missing here as not important for this example
                        file = apiHandler.Get<byte[]>(downloadApiUrl, token);
    
                        var contentDispositionHeader = new System.Net.Mime.ContentDisposition
                        {
                            Inline = true,
                            FileName = fileName
                        };
                        //    Response.Headers.Add("Content-Disposition", contentDispositionHeader.ToString() + "; attachment");
                        Response.Headers.Add("Content-Type", "application/pdf");
                        Response.Headers.Add("Content-Disposition", "attachment; filename=" + fileName);
                        Response.Headers.Add("Content-Transfer-Encoding", "binary");
                        Response.Headers.Add("Content-Length", file.Length.ToString());
    
                    }
                }
                catch (Exception ex)
                {
                    this.logger.LogError(ex, "Error getting pdf", null);
                    return Ok();
                }
    
                return File(file, System.Net.Mime.MediaTypeNames.Application.Pdf);
            }
    
     if (disposition && disposition.indexOf('attachment') !== -1) {
    
    fetch(url, {
        body: JSON.stringify(data),
        method: 'POST',
        headers: {
            'Content-Type': 'application/json; charset=utf-8'
        },
    })
    .then(response => response.blob())
    .then(response => {
        const blob = new Blob([response], {type: 'application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
        const downloadUrl = URL.createObjectURL(blob);
        const a = document.createElement("a");
        a.href = downloadUrl;
        a.download = "file.xlsx";
        document.body.appendChild(a);
        a.click();
    })
    
    data : {
      body: {
        fileOne: ""col1", "col2", "datarow1.1", "datarow1.2"...so on",
        fileTwo: ""col1", "col2"..."
      }
    }
    
    <template>
      <b-link @click.prevent="handleFileExport">Export<b-link>
    </template>
    
    export default = {
       data() {
         return {
           fileNames: ['fileOne', 'fileTwo'],
         }
       },
      computed: {
        ...mapState({
           fileOne: (state) => state.exportFile.fileOne,
           fileTwo: (state) => state.exportFile.fileTwo,
        }),
      },
      method: {
        handleExport() {
          //exportFileAction in store/exportFile needs to return promise
          this.$store.dispatch('exportFile/exportFileAction', paramsToSend)
            .then(async (response) => {
               const downloadPrep = this.fileNames.map(async (fileName) => {
               // using lodash to get computed data by the file name
               const currentData = await _.get(this, `${fileName}`);
               const currentFileName = fileName;
               return { currentData, currentFileName };
             });
             const response = await Promise.all(downloadPrep);
             return response;
           })
           .then(async (data) => {
             data.forEach(({ currentData, currentFileName }) => {
               this.forceFileDownload(currentData, currentFileName);
             });
           })
           .catch(console.error);
        },
        forceFileDownload(data, fileName) {
         const url = window.URL
             .createObjectURL(new Blob([data], { type: 'text/csv;charset=utf-8;' }));
         const link = document.createElement('a');
         link.href = url;
         link.setAttribute('download', `${fileName}.csv`);
         document.body.appendChild(link);
         link.click();
       },
    }