Angularjs 将响应下载为excel文件

Angularjs 将响应下载为excel文件,angularjs,spring,servlets,spring-boot,apache-poi,Angularjs,Spring,Servlets,Spring Boot,Apache Poi,文件未在浏览器上下载。我正在准备文件并将其写入响应的输出流 Rest API有: @RequestMapping(value = "/export-companies", method = {RequestMethod.GET, RequestMethod.HEAD}) @Timed public void downloadCompanies(HttpServletResponse response) throws URISyntaxException {

文件未在浏览器上下载。我正在准备文件并将其写入响应的输出流

Rest API有:

@RequestMapping(value = "/export-companies",
        method = {RequestMethod.GET, RequestMethod.HEAD})
    @Timed
    public void downloadCompanies(HttpServletResponse response) throws URISyntaxException {
        HSSFWorkbook workbook = new HSSFWorkbook();
        HSSFSheet sheet = workbook.createSheet("Sample sheet");

        Map<String, Object[]> data = new HashMap<String, Object[]>();
        data.put("1", new Object[] {"Emp No.", "Name", "Salary"});
        data.put("2", new Object[] {1d, "John", 1500000d});
        data.put("3", new Object[] {2d, "Sam", 800000d});
        data.put("4", new Object[] {3d, "Dean", 700000d});

        Set<String> keyset = data.keySet();
        int rownum = 0;
        for (String key : keyset) {
            Row row = sheet.createRow(rownum++);
            Object [] objArr = data.get(key);
            int cellnum = 0;
            for (Object obj : objArr) {
                Cell cell = row.createCell(cellnum++);
                if(obj instanceof Date)
                    cell.setCellValue((Date)obj);
                else if(obj instanceof Boolean)
                    cell.setCellValue((Boolean)obj);
                else if(obj instanceof String)
                    cell.setCellValue((String)obj);
                else if(obj instanceof Double)
                    cell.setCellValue((Double)obj);
            }
        }

        try {
            ByteArrayOutputStream outByteStream = new ByteArrayOutputStream();
            workbook.write(outByteStream);
            byte [] outArray = outByteStream.toByteArray();
            response.setContentType("application/ms-excel");
            response.setContentLength(outArray.length);
            response.setHeader("Expires:", "0"); // eliminates browser caching
            response.setHeader("Content-Disposition", "attachment; filename=template.xls");
            OutputStream outStream = response.getOutputStream();
            outStream.write(outArray);
            outStream.flush();
            workbook.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

文件内容以不可读格式响应。但该文件不会在浏览器上下载

Angular将仅接收字符序列中的文件内容。您需要从这些字符创建一个文件,并在前端启动浏览器下载

你可以这样做-

var blob = new Blob([data], 
                    {type: 'application/vnd.openxmlformat-officedocument.spreadsheetml.sheet;'});
saveAs(blob, fileName);

其中,
data
是您从API收到的响应。
saveAs
函数是库的一部分。虽然您可以查看如何手动执行此操作,但为什么要重新发明轮子?

使用XHR下载文件是有问题的。只要您只获取请求,就有更简单的方法触发浏览器下载文件

使用JavaScript本机方法
window.open(url)
。 它在包括IE9在内的所有浏览器中都运行良好

在下面的代码中,我使用了
$window
,它是Angular对本机窗口对象的代理

您的代码示例如下所示:

(function() {
'use strict';

angular
    .module('MyApp')
    .factory('CompanyExportService', CompanyExportService);

CompanyExportService.$inject = ['$window'];

function CompanyExportService ($window) {
    var exportUrl = 'api/export-companies';

    return {
        download: download
    }

    function download() {
        $window.open(exportUrl);
    }
}
})();
请注意,此操作超出了Angular的范围,您无法处理错误或等待文件下载。若要生成巨大的Excel文件或API速度较慢,则可能会出现问题

有关更多详细信息,请阅读问题:

更新:

我将
window.location.href
替换为
window.open()
,这似乎是下载文件的更好选择


如果API将抛出错误页面而不是文件,
window.location.href
将替换当前页面(从而丢失其状态)
$window.open()
但是将在新选项卡中打开此错误,而不会丢失应用程序的当前状态。

您可以在新选项卡中下载文件。现代浏览器会在下载完成后自动关闭它们

通过打开新窗口,您可以获得对它的引用,下载完成后,
window.closed
设置为true

不幸的是,您需要不时检查间隔内的此参数

var newWindowRef = $window.open(url, name);
if (newWindowRef) {
    if (newWindowRef.document.body) { // not working on IE
        newWindowRef.document.title = "Downloading ...";
        newWindowRef.document.body.innerHTML = '<h4>Your file is generating ... please wait</h4>';
    }

    var interval = setInterval(function() {
        if (!!newWindowRef.closed) {
            // Downloading completed
            clearInterval(interval);
        }
    }, 1000);
} else {
    $log.error("Opening new window is probably blocked");
}
var newWindowRef=$window.open(url、名称);
if(newWindowRef){
if(newWindowRef.document.body){//不在IE上工作
newWindowRef.document.title=“下载…”;
newWindowRef.document.body.innerHTML='您的文件正在生成…请稍候';
}
var interval=setInterval(函数(){
如果(!!newWindowRef.关闭){
//下载完成
间隔时间;
}
}, 1000);
}否则{
$log.error(“打开新窗口可能被阻止”);
}

在Chrome v52、FF v48和IE 11上测试并运行

我已经完成了相同的代码,但有一个微小的更改响应;它在chrome浏览器中工作,我没有在其他浏览器中测试。请在解决您的问题时将答案标记为已接受。我使用相同的代码保存从rest api响应对象的response.build()方法返回的excel文件。虽然它保存了文件,但是当我试图打开保存的excel文件时,它显示了文件损坏的错误消息。您找到解决方案了吗?我也面临同样的问题,这对我来说很有效。不过只有一个问题。如何保留服务器发送的文件名?也就是说,我不想在
saveAs(blob,fileName)
中提供我自己的
fileName
。谢谢。hi@Piotr Lewandowski请检查我的小提琴,一旦文件下载正确,但excel未在microsoft excel中正确打开,我没有得到单元格,它用白皮书显示数据help@jose您的问题是没有以适当的格式生成内容。您只将HTML保存到文件中,并使用xls扩展名命名它。它不会自动神奇地转换为excel文件格式。问另一个问题,因为描述正确的方法需要更多的空间。@jose我发现你们已经问过这个问题了。我在这里回答:
var newWindowRef = $window.open(url, name);
if (newWindowRef) {
    if (newWindowRef.document.body) { // not working on IE
        newWindowRef.document.title = "Downloading ...";
        newWindowRef.document.body.innerHTML = '<h4>Your file is generating ... please wait</h4>';
    }

    var interval = setInterval(function() {
        if (!!newWindowRef.closed) {
            // Downloading completed
            clearInterval(interval);
        }
    }, 1000);
} else {
    $log.error("Opening new window is probably blocked");
}