通过Ajax调用下载文件的Extjs 4

4
问题很简单:我需要在提交表单时下载一个文件,当表单被提交时发生了一个ajax调用,让我能够从服务器端获取表单数据来构建一个文件,然后以链接的形式在一个警告框中发送。但事实上,我的老板希望直接下载该文件,而不是通过一个警告框中的链接。因此,我必须确保文件在tornado(web)服务器端可用。
        self.set_header('Content-Type', 'application/octet-stream')
        self.set_header('Content-Disposition', 'attachment; filename=clients_counter.zip')
        with open("static/clients_counter.zip", 'r') as f:
            while True:
                data = f.read()
                if not data:
                    break
        self.write(data)
        self.finish()

服务器端代码似乎没有问题,但是客户端(extjs4.1)真是一场噩梦。以下是我的ajax调用代码,但它不起作用:
Ext.Ajax.request({
method : "GET",
url : 'http://whatever.com/count?client='+client+'&start='+start+'&end='+end,
timeout : 30000,
success :
         function (response) {
    //Ext.Msg.alert(response.responseText);
            desktop.getWindow('count-win').doClose();
            return response;
       }//handler,
     failure : 
     function(response) {
    alert("Wrong request");
    }});

3
这并不是Ext JS,实际上没有浏览器会从Ajax请求中启动文件下载。标准的做法是创建一个隐藏表单并提交它。 - Alex Tokarev
7个回答

12

在阅读了来自Ext JS论坛和stackoverflow的各种来源后,我选择了以下方法(使用Ext JS版本4.2.1):

downloadFile: function(config){
    config = config || {};
    var url = config.url,
        method = config.method || 'POST',// Either GET or POST. Default is POST.
        params = config.params || {};

    // Create form panel. It contains a basic form that we need for the file download.
    var form = Ext.create('Ext.form.Panel', {
        standardSubmit: true,
        url: url,
        method: method
    });

    // Call the submit to begin the file download.
    form.submit({
        target: '_blank', // Avoids leaving the page. 
        params: params
    });

    // Clean-up the form after 100 milliseconds.
    // Once the submit is called, the browser does not care anymore with the form object.
    Ext.defer(function(){
        form.close();
    }, 100);

}

1
我将此函数插入到mixin中,以使其更具可移植性: https://gist.github.com/oniram88/10730767 - Oniram
我怀疑以上的解决方案不可行,因为浏览器在开始下载文件后很快就会关闭表单。 - sakadas
我将其放入代码中,但我的浏览器(FireFox)没有下载任何东西,它只是在另一个选项卡中打开了它。 - g07kore
g07kore - 我刚刚在我的电脑上尝试了一下。有点奇怪。它会在Firefox中打开一个新标签页,然后立即关闭该标签页,并显示一个下载对话框。所以它可以工作,但有点小问题。你使用的是哪个版本的Firefox?我使用的是38.0.1。 - sakadas
你是个传奇,我的朋友 :) 你刚刚解决了我为之苦苦思考数小时的问题。谢谢你。@sakadas +1 - sticky_elbows
显示剩余4条评论

6
我曾经遇到过类似的问题,尝试在Ajax调用中下载Excel文件。我是这样解决的:
使用标准提交而非Ajax提交。
var form = Ext.create('Ext.form.Panel', { // this wolud be your form 
    standardSubmit: true,         // this is the important part 
    url: '../ObtenerArchivoAdjuntoServlet' 
});

form.submit({
    params: {
       nombreArchivo: nombreArchivo
    }
});

完成此操作后,您将能够获取所需的文件。

1
由于这是一个标准提交,您无法处理响应。第一次面对它时我也是这样想的,看:http://stackoverflow.com/questions/18434962/extjs-handling-success-or-failure-when-doing-a-standard-submit-in-a-form - code4jhon
1
有没有一种实现“处理文件”掩码的方法? - VAAA

3

在阅读/提取了许多帖子之后,我成功地得到了这种简单的方法来工作。

                Ext.create('Ext.form.Panel', {
                    renderTo: Ext.getBody(),
                    standardSubmit: true,
                    url: 'URL'
                }).submit({params: {'PARAM1': param1, 'PARAM2': param2}});

是的,那个很好用。如果你使用 Sencha Cmd 来构建,你还需要 require 'Ext.form.action.StandardSubmit'。感谢所有以上的人。 - Murrah

2

我认为您可以采用更简单的解决方案。不要费心使用ajax,只需使用纯JavaScript打开文件:

window.open('http://whatever.com/count?client='+client+'&start='+start+'&end='+end)

这将打开一个新标签页并从那里开始下载。

如果我想传递隐藏的值,怎么办? - dextermini

1

您不能使用ajax下载文件。我已经在extjs中实现了类似ajax的文件下载。请参阅博客ajaxlikefiledownload

FileDownload.downloadFile = function(arguments) {

    var url = arguments['url'];
    var params = arguments['params'];
    var successCallback = arguments['success'];
    var failureCallback = arguments['failure'];

    var body = Ext.getBody();

    var frame = body.createChild({
        tag:'iframe',
        cls:'x-hidden',
        id:'hiddenframe-frame',
        name:'iframe'
    });

    var form = body.createChild({
        tag:'form',
        cls:'x-hidden',
        id:'hiddenform-form',
        action: url,
        method: 'POST',
        target:'iframe'
    });


    if (params) {
        for (var paramName in params) {     
            form.createChild({
                tag:'input',
                cls:'x-hidden',
                id:'hiddenform-'+paramName,
                type: 'text',
                text: params[paramName],
                target:'iframe',
                value: params[paramName],
                name: paramName
                });
        }
    }
    form.dom.submit();
    FileDownload.isFinished(successCallback,failureCallback);
};


FileDownload.isFinished = function(successCallback,failureCallback) {

//Check if file is started downloading
if (Ext.util.Cookies.get('fileDownload') && Ext.util.Cookies.get('fileDownload')=='true' ) {
    //Remove cookie call success callback 
    Ext.util.Cookies.set('fileDownload', null, new Date("January 1, 1970"),application.contextPath+'/');
    Ext.util.Cookies.clear('fileDownload',application.contextPath+'/');
    successCallback();
    return;
} 

//Check for error / IF any error happens then frame will load with content
try {
    if(Ext.getDom('hiddenframe-frame').contentDocument.body.innerHTML.length>0){
        Ext.util.Cookies.set('fileDownload', null, new Date("January 1, 1970"),application.contextPath+'/');
        Ext.util.Cookies.clear('fileDownload',application.contextPath+'/');
        failureCallback();
        //Cleanup
        Ext.getDom('hiddenframe-frame').contentDocument.body.innerHTML = ""; 
        
        return;
    }
} 
catch (e) {
    console.log(e);
}

console.log('polling..');
// If we are here, it is not loaded. Set things up so we check   the status again in 100 milliseconds
    
window.setTimeout('FileDownload.isFinished('+successCallback+','+failureCallback+')', 100);
};

使用方法:

FileDownload.downloadFile({
  url : url,
  params : params,
  success : function(){
   //Success call back here
  },
  failure : function(){
   //Failure callbak here
  }
});

在HTTP响应中,您需要添加一个名为fileDownload = true的cookie。

1
以下代码用于使用extjs 5或6下载文件。将以下代码添加到方法中,并为按钮操作调用此方法。这将直接下载文件,而不是在新标签中打开。
使用如下iframe:
/**
 * prints the file
 */
printReport: function () {
    var url = 'downloadURL';
    Ext.Ajax.request({
        url: url,
        method: 'GET',
        autoAbort: false,
        success: function(result) {
            if(result.status == 204) {
                Ext.Msg.alert('Empty Report', 'There is no data');
            } else if(result.status == 200) {
                Ext.DomHelper.append(Ext.getBody(), {
                    tag:          'iframe',
                    frameBorder:  0,
                    width:        0,
                    height:       0,
                    css:          'display:none;visibility:hidden;height:0px;',
                    src:          url
                });
            }
        },
        failure: function() {
            //failure here will automatically
            //log the user out as it should
        }
    });
}

我从extjs论坛复制了答案

选项:2 如果你想在新标签页中打开文件

    /**
 * open file in tab
 */
openReport: function () {
    var url = 'downloadURL';
    Ext.Ajax.request({
        url: url,
        method: 'GET',
        autoAbort: false,
        success: function(result) {
            if(result.status == 204) {
                Ext.Msg.alert('Empty Report', 'There is no data');
            } else if(result.status == 200) {
                var win = window.open('', '_blank');
                win.location = url;
                win.focus();
            }
        },
        failure: function() {
            //failure here will automatically
            //log the user out as it should
        }
    });
}

0

我只需要添加到ajax请求的成功函数中:

window.open('urltothefile.ext')

我仍然认为ajax是不必要的。你可以在成功回调之外进行此调用。 - Kyle Fransham
你仍然在使用URL打开一个窗口...我会通过更改响应类型来让浏览器提示下载窗口... - code4jhon
采用标准的提交方式 - code4jhon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接