文件未在浏览器下载。我正在准备文件并将其写入输出的响应流。
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 JS):
(function() {
'use strict';
angular
.module('MyApp')
.factory('CompanyExportService', CompanyExportService);
CompanyExportService.$inject = ['$resource'];
function CompanyExportService ($resource) {
var service = $resource('api/export-companies', {}, {
'get': {
method: 'GET',
isArray: false
}
});
return service;
}
})();
文件内容作为不可读格式响应。但是文件不会在浏览器下载。
答案 0 :(得分:5)
Angular将仅接收文件内容的字符序列。您需要从这些字符创建一个文件,并在前端启动浏览器下载。
你可以这样做 -
var blob = new Blob([data],
{type: 'application/vnd.openxmlformat-officedocument.spreadsheetml.sheet;'});
saveAs(blob, fileName);
其中data
是您从API收到的回复。 saveAs
函数是FileSaver.js库的一部分。虽然您可以看看如何手动执行此操作,但为什么要重新发明轮子?
答案 1 :(得分:3)
使用XHR下载文件存在问题。只要您只执行GET请求,就会有更简单的方法来触发浏览器下载文件。
使用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很慢,可能会有问题。
有关详细信息,请参阅问题:Spring - download response as a file
更新
我已将window.location.href
替换为window.open()
,这似乎是下载文件的最佳选择。
如果您的API会抛出错误页面而不是文件,window.location.href
将替换当前页面(从而失去其状态)。但是,$window.open()
会在新选项卡中打开此错误,而不会丢失当前的应用程序状态。
答案 2 :(得分:3)
您可以在新标签中下载文件。现代浏览器在下载完成后自动关闭它们。
通过打开新窗口,您可以参考它,当下载完成后, $(':checkbox').each(function (k, v) {
$(this).onchange = null;//
$(this).off() // works but I do not know how to reactivate onchange event
})
将设置为true。
不幸的是,你需要在时间间隔内不时检查这个参数...
window.closed
经过测试并适用于Chrome v52,FF v48和IE 11