java Spring MVC,Excel文件下载,文件损坏
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27017495/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring MVC, Excel file download, corrupts file
提问by user3734130
I'am working on an excel export functionality in one of my webapps. I set up a little test case and got the download working, but the xlsx file is corrupted and don't know what else I could try. If I write the excel to file it opens without problem, so the error must occure when downloading.
我正在我的一个 web 应用程序中处理 excel 导出功能。我设置了一个小测试用例并使下载工作,但 xlsx 文件已损坏,不知道我还能尝试什么。如果我将excel写入文件,它打开没有问题,所以下载时一定会发生错误。
The setup:
设置:
spring-mvc 3.2.7 poi 3.10.1 Tomcat 8.0
弹簧 mvc 3.2.7 poi 3.10.1 Tomcat 8.0
Controller method:
控制器方法:
@RequestMapping(value = "/download", method = RequestMethod.GET)
public ModelAndView downloadExcel() {
// create some sample data
List<Book> listBooks = new ArrayList<Book>();
listBooks.add(new Book("Effective Java", "Joshua Bloch", "0321356683",
"May 28, 2008", 38.11F));
listBooks.add(new Book("Head First Java", "Kathy Sierra & Bert Bates",
"0596009208", "February 9, 2005", 30.80F));
listBooks.add(new Book("Java Generics and Collections",
"Philip Wadler", "0596527756", "Oct 24, 2006", 29.52F));
listBooks.add(new Book("Thinking in Java", "Bruce Eckel", "0596527756",
"February 20, 2006", 43.97F));
listBooks.add(new Book("Spring in Action", "Craig Walls", "1935182358",
"June 29, 2011", 31.98F));
// return a view which will be resolved by an excel view resolver
return new ModelAndView(new ExcelBuilder(listBooks));
}
Abstract Custom View:
抽象自定义视图:
public abstract class AbstractPOIExcelView extends AbstractView {
private static final String CONTENT_TYPE_XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
public AbstractPOIExcelView() {
}
@Override
protected boolean generatesDownloadContent() {
return true;
}
@Override
protected final void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
XSSFWorkbook workbook = new XSSFWorkbook();
buildExcelDocument(model, workbook, request, response);
ByteArrayOutputStream baos = createTemporaryOutputStream();
response.setHeader("Content-Disposition", "attachment;filename=filename.xlsx");
response.setContentType(CONTENT_TYPE_XLSX);
workbook.write(baos);
writeToResponse(response, baos);
}
protected abstract void buildExcelDocument(Map<String, Object> model, XSSFWorkbook workbook,
HttpServletRequest request, HttpServletResponse response) throws Exception;
}
ExcelBuilder:
ExcelBuilder:
public class ExcelBuilder extends AbstractPOIExcelView {
private List<Book> listBooks;
public ExcelBuilder(List<Book> books) {
this.listBooks = books;
}
@Override
protected void buildExcelDocument(Map<String, Object> model, XSSFWorkbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception {
Sheet sheet = workbook.createSheet("Java Books");
sheet.setDefaultColumnWidth(30);
Row header = sheet.createRow(0);
header.createCell(0).setCellValue("Book Title");
header.createCell(1).setCellValue("Author");
header.createCell(2).setCellValue("ISBN");
header.createCell(3).setCellValue("Published Date");
header.createCell(4).setCellValue("Price");
// create data rows
int rowCount = 1;
for (Book aBook : listBooks) {
Row aRow = sheet.createRow(rowCount++);
aRow.createCell(0).setCellValue(aBook.getTitle());
aRow.createCell(1).setCellValue(aBook.getAuthor());
aRow.createCell(2).setCellValue(aBook.getIsbn());
aRow.createCell(3).setCellValue(aBook.getPublishedDate());
aRow.createCell(4).setCellValue(aBook.getPrice());
}
}
}
Response Header:
响应头:
Cache-Control:private, must-revalidate
Content-Disposition:attachment;filename="filename.xlsx"
Content-Language:de-DE
Content-Length:3778
Content-Type:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=ISO-8859-1
Date:Wed, 19 Nov 2014 12:52:05 GMT
Expires:Thu, 01 Jan 1970 00:00:00 GMT
Pragma:private
Set-Cookie:JSESSIONID=07F50FF2B63D4003311DE222782C4E89; Path=/abc/; HttpOnly
X-Content-Type-Options:nosniff
X-Frame-Options:DENY
X-XSS-Protection:1; mode=block
It confuses me that the charset will be set, when this is binary data. Could that be the problem?
当这是二进制数据时,将设置字符集让我感到困惑。这可能是问题吗?
回答by StanislavL
Don't return ModelAndView but just write the excel file to the response's outputStream
不要返回 ModelAndView 而只是将 excel 文件写入响应的 outputStream
@RequestMapping(value = "/download", method = RequestMethod.GET)
@ResponseBody
public Object downloadExcel(HttpServletResponse response) {
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition",
"attachment; filename=" + theFileNameString + ".xls");
try {
generateExcel(response.getOutputStream());
} catch (IOException e) {
System.out.println("ERROR: " + e);
}
return null;
}
Check all the streams flushed/closed
检查所有已刷新/关闭的流
回答by betatester07
I would suggest to use existing solution rather than trying to deal with the response stream by yourself.
我建议使用现有的解决方案,而不是尝试自己处理响应流。
I would use AbstractExcelViewinstead of your AbstractPOIExcelView
to do the job. Check this tutorial using AbstractExcelViewfor inspiration.
我会使用AbstractExcelView而不是你AbstractPOIExcelView
来完成这项工作。使用 AbstractExcelView查看本教程以获得灵感。
For Spring 4.2 or newer use AbstractXlsView(or AbstractXlsxView) because the original AbstractExcelView
is deprecated.
对于 Spring 4.2 或更新版本,请使用AbstractXlsView(或AbstractXlsxView),因为AbstractExcelView
不推荐使用原始版本。