I have a spring java config based web app with (jsp) view resolver. Now i want to show a excel sheet with some data when user clicks on excel icon in app. All over internet i only found xml based spring config for excel view with which i am not familiar with. I decoded to some extent and came pretty close to get my task done. Below is what i got.
I have similar controller and Homepage following the below link:
http://static.springsource.org/spring/docs/3.0.0.M3/reference/html/ch17s06.html
Controlle Code:
@Controller
public class ExcelController extends AbstractController {
@Override
@RequestMapping(value = "/Excel", method = RequestMethod.POST)
protected ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception {
BufferedReader in = null;
try {
URL oracle = new URL("example.com");
URLConnection yc =null;
yc = oracle.openConnection();
in = new BufferedReader(
new InputStreamReader(
yc.getInputStream()));
}
catch(Exception e){
System.err.println(e);
}
Map map = new HashMap();
map.put("input", in);
return new ModelAndView("xl", map);
}
}
View Code:
public class ExcelReportView extends AbstractExcelView{
@Override
protected void buildExcelDocument(Map model, HSSFWorkbook workbook,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
HSSFSheet sheet;
HSSFRow row;
HSSFCell cell;
try {
BufferedReader in = (BufferedReader) model.get("input");
sheet=workbook.createSheet("spring");
String inputLine;
int rowNum =0;
while ((inputLine = in.readLine()) != null) {
row = sheet.createRow(rowNum++);
String[] coloumns = inputLine.split("\t");
int cellNum =0;
for(String coloumn: coloumns){
cell = row.createCell(cellNum++);
cell.setCellValue(coloumn);
}
System.out.println(inputLine);
}
in.close();
System.out.println("Excel written successfully..");
} catch (IOException e) {
e.printStackTrace();
}
} }
view.properties
xl.class=package.ExcelReportView
WebAppConfig.java
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "package")
public class WebAppContextConfig extends WebMvcConfigurerAdapter {
// Resolve logical view names to .jsp resources in /WEB-INF/views directory
@Bean
public InternalResourceViewResolver configureInternalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
return resolver;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/scripts/**").addResourceLocations(
"/scripts/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/img/**").addResourceLocations("/img/");
}
}
Front end Code:
function AjaxCallForExcel(){
$.ajax({
type: 'POST',
url: location.href + '/Excel',
data: ({name:name })
});
}
Below is what i see in logs:
DispatcherServlet with name 'appServlet' processing POST request for [/App/Excel]
Looking up handler method for path /App/Excel
Returning handler method [protected org.springframework.web.servlet.ModelAndView package.ExcelController.handleRequestInternal(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws java.lang.Exception]
Returning cached instance of singleton bean 'excelController'
Invoking afterPropertiesSet() on bean with name 'xl'
Rendering view [org.springframework.web.servlet.view.JstlView: name 'xl'; URL [**/WEB-INF/jsp/xl.jsp**]] in DispatcherServlet with name 'appServlet'
Added model object 'org.springframework.validation.BindingResult.input' of type [org.springframework.validation.BeanPropertyBindingResult] to request in view with name 'xl'
Added model object 'input' of type [java.io.BufferedReader] to request in view with name 'xl'
Forwarding to resource [/WEB-INF/jsp/xl.jsp] in InternalResourceView 'xl'
Successfully completed request
I dont know how to avoid it from forwarding it to xl.jsp. I am sure view resolver is making it into jsp view. Can someone point how can i fix it.
EDIT
I saw this xml equivalent config online. Not sure how to make it java config:
<bean id="excelViewResolver" class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="order" value="1"/>
<property name="location" value="/WEB-INF/views.xml"/>
</bean>
i tried converting it the below way:
@Bean
public XmlViewResolver configureXmlViewResolver(){
XmlViewResolver resolver = new XmlViewResolver();
resolver.setOrder(1);
resolver.setLocation(**WHAT SHOULD BE HERE**);
}
I dont know what to put in location. I cant give string. i dont have views.xml as i am use java configs
Edit(Here is my code after making changes as you said)
public class ExcelReportView extends AbstractExcelView{
BufferedReader in;
ExcelReportView(BufferedReader in){
this.in = in;
}
@Override
protected void buildExcelDocument(Map model, HSSFWorkbook workbook,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
HSSFSheet sheet;
HSSFRow row;
HSSFCell cell;
response.setHeader("Content-Type", "application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=MyExcelSpreadsheet.xls");
try {
//BufferedReader in = (BufferedReader) model.get("input");
sheet=workbook.createSheet("spring");
String inputLine;
int rowNum =0;
while ((inputLine = in.readLine()) != null) {
row = sheet.createRow(rowNum++);
String[] coloumns = inputLine.split("\t");
int cellNum =0;
for(String coloumn: coloumns){
cell = row.createCell(cellNum++);
cell.setCellValue(coloumn);
}
System.out.println(inputLine);
}
in.close();
System.out.println("Excel written successfully..");
} catch (IOException e) {
e.printStackTrace();
}
OutputStream outStream = null;
try {
outStream = response.getOutputStream();
workbook.write(outStream);
outStream.flush();
} finally {
outStream.close();
}
}
}
Controller Code:
@Controller
public class ExcelController {
@RequestMapping(value = "/Excel", method = RequestMethod.POST)
protected ModelAndView generateCSV(HttpServletRequest request,
HttpServletResponse response) throws Exception {
BufferedReader in = null;
try {
URL oracle = new URL("http://service.com");
URLConnection yc =null;
yc = oracle.openConnection();
in = new BufferedReader(
new InputStreamReader(
yc.getInputStream()));
}
catch(Exception e){
System.err.println(e);
}
ModelAndView mav = new ModelAndView();
mav.setView(new ExcelReportView( in));
return mav;
}
}
Log output:
DispatcherServlet with name 'appServlet' processing POST request for [/App/Excel]
Looking up handler method for path /App/Excel
Returning handler method [protected org.springframework.web.servlet.ModelAndView com.package.ExcelController.generateCSV(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws java.lang.Exception]
Returning cached instance of singleton bean 'excelController'
Rendering view [com.package.controllers.ExcelReportView: unnamed] in DispatcherServlet with name 'appServlet'
Created Excel Workbook from scratch
Title Id required
Excel written successfully..
Successfully completed request
EDIT:
Response Header:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Pragma: private
Cache-Control: private, must-revalidate
Content-Disposition: attachment; filename=MyExcelSpreadsheet.xls
Content-Type: application/octet-stream;charset=ISO-8859-1
Content-Language: en-US
Transfer-Encoding: chunked
Date: Tue, 12 Mar 2013 16:36:52 GMT
You can return a ModelAndView from your controller method, setting the View to an instance of AbstractExcelView. Then you don't need to mess with your XML at all.
Edit: Add some additional info: I have done the custom View approach many times to handle CSV downloads.
First, you need to create an instantiation of the AbstractExcelView. You would do so by overwriting the buildExcelDeocument() method. You will need the POI libraries, as I believe it is required. For example:
@Override
public void buildExcelDocument(Map<String, Object> model, HSSFWorkbook workbook,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
// Set the headers
response.setHeader("Content-Type", "application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=MyExcelSpreadsheet.xls");
// Here is where you will want to put the code to build the Excel spreadsheet
OutputStream outStream = null;
try {
outStream = response.getOutputStream();
workbook.write(outStream);
outStream.flush();
} finally {
outStream.close();
}
}
Next you need to modify the Controller method
@RequestMapping(params = "actionMethod="+Constants.ACTION_METHOD_REPORT)
public ModelAndView generateCSV(
@ModelAttribute(Constants.REPORT_FORMBEAN_MODEL_ATTRIBUTE) FormBean formBean,
ModelAndView mav,
HttpServletRequest request,
HttpServletResponse response) {
mav.setView(new MyExcelView( /* modify your constructor to pass in your data so the view can build the output */ ));
return mav;
}
There is no need to edit your context XML, modify how your beans are wired together, create any services, or anything. Simply create an instance of your custom View, pass in whatever you need to in order to build the spreadsheet, then set the view on the ModelAndView. Simple as that.
EDIT - You need to do this...
You need to change your AJAX call so that you can tell if its done or had an error. You are flying blind right now:
function AjaxCallForExcel(){
$.ajax({
type: 'POST',
url: location.href + '/Excel',
data: ({name:name }),
complete: function(jqXHR,textStatus ) {
alert("Complete: "+textStatus );
},
error: function(jqXHR,textStatus,errorThrown ) {
alert("Status: "+textStatus+"\nerror: "+errorThrown );
}
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With