5

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

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

这样做时我会遇到权限问题吗?

4

6 回答 6

9

虽然您可以使用像JExcelAPI这样的完整库,但如果您将响应 MIME 类型设置为“application/vnd.ms-excel”之类的内容,Excel 还将读取 CSV 和纯 HTML 表格。

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

于 2009-01-25T17:26:52.083 回答
9

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

反过来,CSV 是一种标准格式,它可以毫无问题地从 Excel 中获得默认支持,并且实际上生成起来很容易且节省内存。尽管有库,但实际上您也可以轻松地在不到 20 行中编写一个(对于那些无法抗拒的人来说很有趣)。您只需要遵守RFC 4180规范,它基本上只包含 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 路径信息中,因为由 Redmond 的团队开发的某个网络浏览器不会使用正确的文件名保存下载。

于 2010-01-28T11:53:07.187 回答
3

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

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

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

于 2009-01-25T16:33:12.707 回答
1

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

于 2012-05-18T15:34:02.743 回答
0

也许您应该考虑使用一些报告工具,可以选择将文件导出为 XLS 格式。我的建议是 JasperReports

于 2009-01-25T16:43:09.130 回答
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();

    }
于 2015-07-07T14:15:24.497 回答