5

I have this application I'm developing in JSP and I wish to export some data from the database in XLS (MS Excel format).

Is it possible under tomcat to just write a file as if it was a normal Java application, and then generate a link to this file? Or do I need to use a specific API for it?

Will I have permission problems when doing this?

Burhan Ali
  • 2,258
  • 1
  • 28
  • 38
fmsf
  • 36,317
  • 49
  • 147
  • 195
  • BalusC shed some light to my problem, saving HTML into a file with ".xls" extension can work in some Excel versions, but in others it'll prompt a warning. That's why I moved to some 3rd party library ([GemBox.Spreadsheet for Java](https://www.gemboxsoftware.com/spreadsheet-java)) and I'm generating a real Excel file ([as shown here](https://www.gemboxsoftware.com/spreadsheet-java/examples/create-write-excel-file-in-java/402)). – bellpatricia Nov 18 '19 at 09:21

7 Answers7

10

Don't use plain HTML tables with an application/vnd.ms-excel content type. You're then basically fooling Excel with a wrong content type which would cause failure and/or warnings in the latest Excel versions. It will also messup the original HTML source when you edit and save it in Excel. Just don't do that.

CSV in turn is a standard format which enjoys default support from Excel without any problems and is in fact easy and memory-efficient to generate. Although there are libraries out, you can in fact also easily write one in less than 20 lines (funny for ones who can't resist). You just have to adhere the RFC 4180 spec which basically contains only 3 rules:

  1. Fields are separated by a comma.
  2. If a comma occurs within a field, then the field has to be surrounded by double quotes.
  3. If a double quote occurs within a field, then the field has to be surrounded by double quotes and the double quote within the field has to be escaped by another double quote.

Here's a kickoff example:

public static <T> void writeCsv (List<List<T>> csv, char separator, OutputStream output) throws IOException {
    BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output, "UTF-8"));
    for (List<T> row : csv) {
        for (Iterator<T> iter = row.iterator(); iter.hasNext();) {
            String field = String.valueOf(iter.next()).replace("\"", "\"\"");
            if (field.indexOf(separator) > -1 || field.indexOf('"') > -1) {
                field = '"' + field + '"';
            }
            writer.append(field);
            if (iter.hasNext()) {
                writer.append(separator);
            }
        }
        writer.newLine();
    }
    writer.flush();
}

Here's an example how you could use it:

public static void main(String[] args) throws IOException {
    List<List<String>> csv = new ArrayList<List<String>>();
    csv.add(Arrays.asList("field1", "field2", "field3"));
    csv.add(Arrays.asList("field1,", "field2", "fie\"ld3"));
    csv.add(Arrays.asList("\"field1\"", ",field2,", ",\",\",\""));
    writeCsv(csv, ',', System.out);
}

And inside a Servlet (yes, Servlet, don't use JSP for this!) you can basically do:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String filename = request.getPathInfo().substring(1);
    List<List<Object>> csv = someDAO().findCsvContentFor(filename);
    response.setHeader("content-type", "text/csv");
    response.setHeader("content-disposition", "attachment;filename=\"" + filename + "\"");
    writeCsv(csv, ';', response.getOutputStream());
}

Map this servlet on something like /csv/* and invoke it as something like http://example.com/context/csv/filename.csv. That's all.

Note that I added the possiblity to specify the separator character separately, because it may depend on the locale used whether Excel would accept a comma , or semicolon ; as CSV field separator. Note that I also added the filename to the URL pathinfo, because a certain webbrowser developed by a team in Redmond otherwise wouldn't save the download with the proper filename.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • You have experiences with the "Excel would accept a comma or the semicolon" part to share? – Thorbjørn Ravn Andersen Nov 17 '10 at 15:37
  • 1
    @Thor: If the right separator is used, then Excel will open it automagically in the right cells. The separator depends on (default) Excel configuration and locale. In European locales for example, it's very often a semicolon. So it really depends on your target audience which separator to choose during exporting. The enduser has otherwise to manually import it and specify the separator. – BalusC Nov 17 '10 at 16:08
  • The filename does not support chinese , I use a static filename ="下载物品", but the downloaded file name comes the mapping url, say xxx.do in springmvc, or mapping path on servlet. How to support chinese file name? – JaskeyLam Aug 07 '15 at 03:03
  • As answered, specify it as part of URL pathinfo. Otherwise Internet Explorer will indeed behave incorrectly like that. – BalusC Aug 07 '15 at 07:30
10

While you can use a full fledged library like JExcelAPI, Excel will also read CSV and plain HTML tables provided you set the response MIME Type to something like "application/vnd.ms-excel".

Depending on how complex the spreadsheet needs to be, CSV or HTML can do the job for you without a 3rd party library.

Kevin
  • 30,111
  • 9
  • 76
  • 83
3

You will probably need a library to manipulate Excel files, like JExcelAPI ("jxl") or POI. I'm more familiar with jxl and it can certainly write files. You can generate them and store them by serving a URL to them but I wouldn't. Generated files are a pain. They add complication in the form on concurrency, clean-up processes, etc.

If you can generate the file on the fly and stream it to the client through the standard servlet mechanisms.

If it's generated many, may times or the generation is expensive then you can cache the result somehow but I'd be more inclined to keep it in memory than as a file. I'd certainly avoid, if you can, linking directly to the generated file by URL. If you go via a servlet it'll allow you to change your impleemntation later. It's the same encapsualtion concept as in OO dsign.

cletus
  • 616,129
  • 168
  • 910
  • 942
0

POI or JExcel are good APIs. I personally like better POI, plus POI is constantly updated. Furthermore, there are more resources online about POI than JExcel in case you have any questions. However, either of the two does a great job.

Daniel Watkins
  • 1,656
  • 1
  • 15
  • 25
lv10
  • 1,469
  • 7
  • 25
  • 46
0
  try {
            String absoluteDiskPath =  test.xls";
            File f = new File(absoluteDiskPath);
            response.setContentType("application/xlsx");
            response.setHeader("Content-Disposition", "attachment; filename=" + absoluteDiskPath);
            String name = f.getName().substring(f.getName().lastIndexOf("/") + 1, f.getName().length());
            InputStream in = new FileInputStream(f);
            out.clear(); //clear outputStream prevent illegalStateException write binary data to outputStream
            ServletOutputStream outs = response.getOutputStream();
            int bit = 256;
            int i = 0;
            try {
                while ((bit) >= 0) {
                    bit = in.read();
                    outs.write(bit);
                }
                outs.flush();
                outs.close();
                in.close();
            } catch (IOException ioe) {
                ioe.printStackTrace();
            } finally {
                try {
                    if(outs != null)
                        outs.close(); 
                    if(in != null)
                        in.close(); 
                }catch (Exception ioe2) {
                    ioe2.printStackTrace(); 
                }
            }
    } catch (Exception ex) {
        ex.printStackTrace();

    }
Dilip Godhani
  • 2,065
  • 3
  • 18
  • 33
0

maybe you should consider using some reporting tool with an option of exporting files into XLS format. my suggestion is JasperReports

Boris Pavlović
  • 63,078
  • 28
  • 122
  • 148
0

I tried like as below in JSP, it is working fine.

   <% String filename = "xyz.xls"; 

   response.setContentType("application/octet-stream");

   response.setHeader("Content-Disposition","attachment; filename=\"" + filename + "\"");   

   java.io.File excelFile=new java.io.File("C:\\Users\\hello\\Desktop\\xyz.xls");

   java.io.FileInputStream fileInputStream=new java.io.FileInputStream(excelFile);

   byte[] bytes = new byte[(int) excelFile.length()];

   int offset = 0;

   while (offset < bytes.length)

   {

  int result = fileInputStream.read(bytes, offset, bytes.length - offset);
  if (result == -1) {
      break;
  }
  
  offset += result;
 
  }


  javax.servlet.ServletOutputStream outs = response.getOutputStream();
  outs.write(bytes);
  outs.flush();
  outs.close();
  fileInputStream.close();   
  %>
Siva
  • 1
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 29 '22 at 13:32