【问题标题】:How / when to delete a file in java?如何/何时删除java中的文件?
【发布时间】:2010-11-17 07:59:38
【问题描述】:

问题是,用户在 JSP 中单击一个按钮,将导出显示的数据。所以我正在做的是,创建一个临时。文件并将内容写入其中 [ resultSet >> xml >> csv ],然后将内容写入 ServletResponse。关闭响应输出流后,我尝试删除文件,但每次都返回 false。

代码;

public static void writeFileContentToResponse ( HttpServletResponse response , String fileName ) throws IOException{

        ServletOutputStream responseoutputStream = response.getOutputStream();
        File file = new File(fileName);
        if (file.exists()) {
            file.deleteOnExit();

            DataInputStream dis = new DataInputStream(new FileInputStream(
                    file));

            response.setContentType("text/csv");
            int size = (int) file.length();
            response.setContentLength(size);
            response.setHeader("Content-Disposition",
                    "attachment; filename=\"" + file.getName() + "\"");         
            response.setHeader("Pragma", "public");
            response.setHeader("Cache-control", "must-revalidate");

            if (size > Integer.MAX_VALUE) {

            }
            byte[] bytes = new byte[size];

            dis.read(bytes);
            FileCopyUtils.copy(bytes, responseoutputStream );
        }
        responseoutputStream.flush();
        responseoutputStream.close();
        file.delete();
    }

我使用过'file.deleteOnExit();'和 file.delete();但它们都不起作用。

【问题讨论】:

  • 不能导出到内存缓冲区,直接将其内容写入输出流吗?
  • CSV 文件太小了,不好好保存在内存中
  • Rakesh,您可能想尝试将这个问题归结为您正在尝试做的事情,即。询问如何从 Web 应用程序以 CSV 格式提供大型结果集,而不是如何删除文件。

标签: java file jsp jakarta-ee


【解决方案1】:

file.deleteOnExit() 不会在这里产生你想要的结果——它的目的是在 JVM 退出时删除文件——如果这是从 servlet 调用的,这意味着在服务器关闭时删除文件下来。

至于为什么 file.delete() 不起作用 - 我在这段代码中看到的只是从文件中读取并写入 servlet 的输出流 - 当您将数据写入您离开的文件时是否有可能文件的输入流打开了吗?当前正在使用的文件不会被删除。

此外,即使您的方法抛出 IOException,如果在访问文件时出现异常,您仍然需要清理内容 - 将文件操作放在 try 块中,并将 stream.close() 放入 finally 块中。

【讨论】:

  • 即使服务器关闭,文件仍然存在
  • 这是在 Windows 上吗?我发现了一个错误报告 - bugs.sun.com/bugdatabase/view_bug.do?bug_id=4171239 - 指出如果 JVM 退出时文件仍然打开,则在 windows 上 file.deleteOnExit() 不起作用。由于 file.delete() 不起作用 - 该文件仍以某种方式打开。我不确定当前 JVM 中此错误的状态...它被标记为“报告针对”直到 1.3,但我在“已修复版本”中看不到任何内容...然后作为副本关闭,但在错误中看不到修复,它与任何一个都重复...
【解决方案2】:

不要创建那个文件。 将数据直接从结果集中写入 CSV responseoutputStream。 这样可以节省时间、内存、磁盘空间和头痛。

如果您真的需要它,请尝试使用 File.createTempFile() 方法。 如果之前没有删除这些文件,那么当您的 VM 正常停止时,这些文件将被删除。

【讨论】:

  • 如果这是在服务器端,那么这些临时文件可能真的会随着 JVM 一直在运行而构建。
【解决方案3】:

我假设您在这里遇到了某种并发问题。考虑将此方法设为非静态方法,并为您的临时文件使用唯一名称(例如附加当前时间,或使用 guid 作为文件名)。可能是您打开文件,然后其他人打开它,所以第一次删除失败。

【讨论】:

    【解决方案4】:

    正如我所见,您没有关闭 DataInputStream dis - 当您确实要删除文件时,这会导致错误状态。此外,您应该在 try-catch-finally 块中处理流并在 finally 内关闭它们。代码有点粗糙,但是很安全:

    DataInputStream dis = null;
        try
        {
            dis = new DataInputStream(new FileInputStream(
                    file));
            ... // your other code
        }
        catch(FileNotFoundException P_ex)
        {
            // catch only Exceptions you want, react to them
        }
        finally
        {
            if(dis != null)
            {
                try
                {
                    dis.close();
                }
                catch (IOException P_ex) 
                {
                    // handle exception, again react only to exceptions that must be reacted on
                }
            }
        }
    

    【讨论】:

      【解决方案5】:

      你是如何创建文件的。您可能需要使用createTempFile

      您应该能够很好地删除临时文件(无需 deleteOnExit)。当您尝试删除该文件时,您确定该文件未在使用中吗?您应该为每个用户请求一个文件(这是您应该避免使用临时文件并将所有内容存储在内存中的另一个原因)。

      【讨论】:

        【解决方案6】:

        你可以试试piped inputpiped output stream。这些缓冲区需要两个线程,一个用于提供管道(导出器),另一个(servlet)用于使用来自管道的数据并将其写入响应输出流

        【讨论】:

          【解决方案7】:

          您真的不想为请求创建临时文件。尽可能将生成的 CSV 保存在内存中。

          您可能需要将文件的写入直接与输出联系起来。因此解析结果集的一行,将其写入响应流,解析下一行等等。这样,您一次只能在内存中保留一行。问题是响应可能超时。

          如果你想要一个快捷的方法,看看Display tag library。它可以很容易地在表格中显示一堆结果,然后将预先构建的export options 添加到所述表格中。 CSV 就是其中一种选择。

          【讨论】:

          • 其实我们之前使用的是DisplayTag,但现在因为尺寸限制而避免了。我的内存中不能有 CSV,这可能非常大。
          【解决方案8】:

          您不需要临时文件。您根据文件大小在此处创建的字节缓冲区也可能导致OutOfMemoryError。这一切都是低效的。

          只需将ResultSet 的数据立即写入 HTTP 响应,同时遍历行。基本上:writer.write(resultSet.getString("columnname"))。这样您就不需要将其写入临时文件或吞噬 Java 内存中的所有内容。

          此外,默认情况下,大多数 JDBC 驱动程序会在向ResultSet#next() 提供任何内容之前将所有内容缓存在 Java 内存中。这也是低效的。您想让它通过设置Statement#setFetchSize() 立即逐行提供数据。如何正确执行取决于使用的 JDBC 驱动程序。例如 MySQL,您可以在其JDBC driver documentation 中阅读它。

          这是一个启动示例,假设您使用的是 MySQL:

          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
              response.setContentType("text/csv");
              response.setCharacterEncoding("UTF-8");
          
              Connection connection = null;
              Statement statement = null;
              ResultSet resultSet = null;
              PrintWriter writer = response.getWriter();
          
              try {
                  connection = database.getConnection();
                  statement = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
                  statement.setFetchSize(Integer.MIN_VALUE);
                  resultSet = statement.executeQuery("SELECT col1, col2, col3 FROM tbl");
          
                  while (resultSet.next()) {
                      writer.append(resultSet.getString("col1")).append(',');
                      writer.append(resultSet.getString("col2")).append(',');
                      writer.append(resultSet.getString("col3")).println();
                      // Note: don't forget to escape quotes/commas as per RFC4130.
                  }
              } catch (SQLException e) {
                  throw new ServletException("Retrieving CSV rows from DB failed", e);
              } finally { 
                  if (resultSet != null) try { resultSet.close; } catch (SQLException logOrIgnore) {}
                  if (statement != null) try { statement.close; } catch (SQLException logOrIgnore) {}
                  if (connection != null) try { connection.close; } catch (SQLException logOrIgnore) {}
              }
          }
          

          就是这样。这种方式实际上只有一个数据库行一直保存在内存中。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-09-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-11-04
            • 2012-04-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多