代码之家  ›  专栏  ›  技术社区  ›  fmsf

JSP生成Excel电子表格(XLS)以供下载

  •  5
  • fmsf  · 技术社区  · 17 年前

    我有一个用JSP开发的应用程序,我希望以XLS(MS Excel格式)从数据库中导出一些数据。

    在tomcat下,是否可以像编写普通Java应用程序一样编写一个文件,然后生成指向该文件的链接?还是需要使用特定的API?

    执行此操作时,我会遇到权限问题吗?

    7 回复  |  直到 14 年前
        1
  •  9
  •   Kevin    17 年前

    虽然你可以使用一个功能齐全的库,比如 JExcelAPI ,Excel还将读取CSV和纯HTML表,前提是您将响应MIME类型设置为“application/vnd.ms-Excel”。

    根据电子表格的复杂程度,CSV或HTML可以在没有第三方库的情况下为您完成这项工作。

        2
  •  9
  •   BalusC    13 年前

    不要将纯HTML表与 application/vnd.ms-excel 内容类型。然后,您基本上是在用错误的内容类型欺骗Excel,这会在最新的Excel版本中导致失败和/或警告。当您在Excel中编辑和保存原始HTML源代码时,它也会将其弄乱。请不要那样做。

    反过来,CSV是一种标准格式,它享有Excel的默认支持,没有任何问题,事实上生成起来既简单又节省内存。虽然有库,但事实上,你也可以在不到20行的时间里轻松地写一个(对于那些无法抗拒的人来说很有趣)。你只需要坚持 RFC 4180 spec基本上只包含3条规则:

    1. 字段用逗号分隔。
    2. 如果字段内出现逗号,则该字段必须用双引号括起来。
    3. 如果字段内出现双引号,则字段必须被双引号包围,字段内的双引号必须被另一个双引号转义。

    以下是一个启动示例:

    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();
    }
    

    以下是一个如何使用它的示例:

    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);
    }
    

    在Servlet内部(是的,Servlet,不要使用JSP!),你基本上可以做到:

    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());
    }
    

    将此servlet映射到类似以下内容上 /csv/* 并像这样调用它 http://example.com/context/csv/filename.csv 仅此而已。

    请注意,我添加了单独指定分隔符的可能性,因为Excel是否接受逗号可能取决于所使用的区域设置 , 或分号 ; 作为CSV字段分隔符。请注意,我还将文件名添加到了URL路径信息中,因为雷德蒙德团队开发的某个浏览器不会使用正确的文件名保存下载。

        3
  •  3
  •   cletus    17 年前

    您可能需要一个库来操作Excel文件,例如 JExcelAPI (“jxl”)或 POI 。我更熟悉jxl,它当然可以写文件。你可以通过向它们提供URL来生成和存储它们,但我不会。生成的文件很麻烦。它们在并发性、清理过程等方面增加了复杂性。

    如果您可以动态生成文件并通过标准servlet机制将其流式传输到客户端。

    如果它是多次生成的,或者生成成本很高,那么你可以以某种方式缓存结果,但我更倾向于将其保存在内存中而不是作为文件。如果可以的话,我当然会避免通过URL直接链接到生成的文件。如果你通过servlet访问,它将允许你稍后更改你的实现。它与OO dsign中的封装概念相同。

        4
  •  1
  •   Daniel Watkins    13 年前

    也许你应该考虑使用一些带有将文件导出为XLS格式选项的报告工具。我的建议是JasperReports

        5
  •  0
  •   Boris Pavlović    17 年前

    POI或JExcel是很好的API。我个人喜欢更好的POI,而且POI会不断更新。此外,如果您有任何疑问,网上关于POI的资源比JExcel更多。然而,两者都做得很好。

        6
  •  0
  •   Dilip Godhani    10 年前
      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();
    
        }
    
    推荐文章