0

So I have a method that is correctly creating a CSV file with a bunch of data when called by doGet(HttpServletRequest request, HttpServletResponse response) which is called when I create a button like this:

link = new HtmlOutputLink();
HtmlGraphicImage img = new HtmlGraphicImage(); 
img.setStyle("background-color: #FFFFFF;"); 
img.setTitle("Click to Export these requests to csv file"); 
img.setValue("../images/Export.PNG"); 
link.getChildren().add(img);
link.setValue(resp.encodeURL(Constants.TXT_ALL_DIV_TEAM_EXPORT_LINK)); 
cell = new DataTableCell();
cell.setType(CellType.DATA); 
cell.setFormat(new Format(Format.CENTER)); 
cell.addElement(link);
headerRow.addElement(cell);

When the button is clicked the doGet method then calls the method which creates the CSV file (which, again, is working correctly called from doGet).

However, I have to change this from an image to a CommandButton, the command button is a custom class that extends the javax.faces.component.html.HtmlCommandButton package, so now I have this:

HtmlOutputLink link = new HtmlOutputLink();
CommandButton alertsButton = new CommandButton();

alertsButton.setId(UI_EXPORT_ID);
alertsButton.setValue(UI_EXPORT_TXT);
alertsButton.setOnclick("javascript:showWaitLayer();jsCBDupdateComponent('" + "form" + "', this );");
alertsButton.setBlockSubmit(true);
alertsButton.setImmediate(true);
alertsButton.addActionListener(this);

link.getChildren().add(alertsButton);
cell = new DataTableCell();
cell.setType(CellType.DATA); 
cell.setFormat(new Format(Format.CENTER)); 
cell.addElement(link);
headerRow.addElement(cell);

When this button is clicked it calls processAction() in which I instantiate the HttpServletResponse used to pass in to the working method.

FacesContext context = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();
response.setContentType("application/octet-stream");
HomeController homeController = (HomeController) context.getApplication().createValueBinding("#{HomeController}").getValue(context);
homeController.createExportFile(response);

EDIT: Adding the createExportFile method with lots taken out for readability.

EDIT2: I've changed the createExportFile so that no HttpServletResponse needs to be passed in. This way the method doesn't have any dependencies on passed in parameters. Both of the buttons (the one with the img and the one that is the CommandButton) call this method and run through without errors in the exact same method. Only the img button creates the excel file though.

public void createExportFile()
        throws IOException, PersistenceException, SQLException {
    FacesContext context = FacesContext.getCurrentInstance();
    HttpServletResponse resp = (HttpServletResponse) context.getExternalContext().getResponse();
    resp.setContentType("application/octet-stream");
    resp.setContentLength(500 * this.getWorkAllDivDeptList().size());
    resp.setHeader("Content-Disposition", "attachment; filename=\""
            + "AllDivTeam.csv" + "\"");             Map<String, HashSet<String>> stateDateMap = new HashMap<String, HashSet<String>>();

    ArrayList<DynamicFieldInfo> txtFieldAllList = new ArrayList<DynamicFieldInfo>();
    RequestReader kanbanReader;

    try {
        //Get all of the data from the DB
    } catch (MidTierException mte) {
        mte.printStackTrace();
    }

    String rowTxt = getExportRowHdrTxt(txtFieldAllList, addlColCnt);
    response.getOutputStream().write(rowTxt.getBytes(), 0, rowTxt.length());
    kanbanReader = new RequestReader("");

    for (AllActiveWorkListInfo bwi : (ArrayList<AllActiveWorkListInfo>) this
            .getFilteredAllDivDeptList()) {
        HashSet<String> set = (HashSet<String>) stateDateMap.get(bwi.getMID());
        if (null != set && !set.isEmpty()) {
            Iterator<String> itr = set.iterator();
            while (itr.hasNext()) {
                rowTxt = getExportRowTxt(bwi, txtFieldAllList,
                        kanbanReader, (String) itr.next());
                response.getOutputStream().write(rowTxt.getBytes(), 0,
                        rowTxt.length());
            }
        } else {
            rowTxt = getExportRowTxt(bwi, txtFieldAllList, kanbanReader, "");
            response.getOutputStream().write(rowTxt.getBytes(), 0,
                    rowTxt.length());
        }

        if (count++ == 200) {
            response.getOutputStream().flush();
        }

    }
    response.getOutputStream().flush();
    response.getOutputStream().close();
}

Adding Headers:

(Request-Line)  POST /kanban/faces/kanbanRepAllDivDeptTickets HTTP/1.1
Accept  */*
Accept-Encoding gzip, deflate
Accept-Language en-us
Cache-Control   no-cache
Connection  Keep-Alive
Content-Length  530
Content-Type    application/x-www-form-urlencoded
Cookie  _cbdModemCheck=false; JSESSIONID=08ADA3D60982F9D13478AF729D6E5205; s_fid=24245A567AE4BB33-0F8E3B5CF3FBEED7
Host    localhost:8080
Referer http://localhost:8080/kanban/faces/kanbanRepAllDivDeptTickets
User-Agent  Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.3)

When I debug it goes through all of the process to create the csv exactly like when called from doGet but it never opens the dialog to download or cancel. It's throwing no exceptions and I'm completely out of ideas.

Does anyone see where I'm making an incorrect assumption?

Thanks for your time, Mike

michaelp
  • 353
  • 6
  • 24
  • Have you already seen http://stackoverflow.com/questions/9391838/how-to-stream-a-file-download-in-a-jsf-backing-bean? This thing will fail if you don't tell JSF that you've already completed the response and/or if the client is sending an ajax request. – BalusC Sep 18 '14 at 19:26
  • @BalusC That was definitely informative, and I added the context.responseComplete() to my code, but it still has not fixed the problem. Because no force a Save As dialog has appeared it leads me to believe that the client is sending an ajax request somehow. The only difference was turning an HtmlGraphicImage into the CommandButton that extends HtmlCommandButton. – michaelp Sep 19 '14 at 12:20
  • 1
    Then it must be sending an ajax request. Checking browser's HTTP traffic monitor should give clues. ICEfaces for example is known to unaskingly convert standard command buttons to ajax command buttons. – BalusC Sep 19 '14 at 12:45
  • @BalusC Checking httpwatch, it's definitely returning the right data but not making a save as dialog. It's making a POST and then returning the data, but I'm not sure how to determine whether it's an ajax request or not. Is there any way to force the command button to a non-ajax request? – michaelp Sep 19 '14 at 14:25
  • Check request headers. E.g. `Faces-Request=partial/ajax` or `X-Requested-With=XMLHttpRequest`. – BalusC Sep 19 '14 at 14:29
  • @BalusC I've added the headers to the question and it doesn't have the x-requested-with XMLHttpRequest so I don't actually believe it's ajax. The data is definitely being returned correctly though. – michaelp Sep 19 '14 at 14:44
  • The part `resp.setContentLength(500 * this.getWorkAllDivDeptList().size());` doesn't make sense. Better delete that line and retry. The container will automatically switch to chunked encoding. The content type is also not right, it should be `text/csv`. As to request headers, it's indeed not an ajax request. Could you share the response headers as well? Have you also checked the JavaScript console? – BalusC Sep 19 '14 at 17:11

1 Answers1

0

You are no where writing the file to the output stream. You should write your file to the output stream for that. This is the sample code worked for me. Hope it helps you.

int BUFSIZE = 4096;
int length = 0;
ServletOutputStream outStream = response.getOutputStream();
String mimeType = "text/csv";
response.setContentType(mimeType);
response.setContentLength((int)document.length());
String documentName = document.getName();
response.setHeader("Content-Disposition", "attachment; filename=\"" + yourCsvFileName +  "\"");

byte[] byteBuffer = new byte[BUFSIZE];
DataInputStream in = new DataInputStream(new FileInputStream(document));

while((null != in) && ((length = in.read(byteBuffer)) != -1)) {
    outStream.write(byteBuffer, 0, length);
}
in.close();
outStream.close();
Govind
  • 311
  • 1
  • 12
  • I just didn't add the part where I was originally writing to the output stream since that part was working originally and it's not where I thought I had made my mistake. I've added it now though for clarity. Thanks! – michaelp Sep 16 '14 at 18:45