2009-08-06 4 views
1

문제는 사용자가 JSP에서 버튼을 클릭하여 표시된 데이터를 내보내는 것입니다. 그래서 제가하고있는 일은 임시 직원을 만드는 것입니다. 파일을 만들고 [resultSet >> xml >> csv]에 내용을 쓰고 그 내용을 ServletResponse에 씁니다. 응답 출력 스트림을 닫은 후에는 파일을 삭제하려고 시도하지만 false가 반환 될 때마다 삭제하려고합니다.어떻게/java에서 파일을 삭제할 수 있습니까?

코드;

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(); 그러나 그들 중 누구도 일하고 ​​있지 않습니다.

+0

메모리 버퍼로 내보내고 해당 내용을 출력 스트림에 직접 쓸 수 없습니까? –

+0

** CSV ** 파일은 매우 유용하기 때문에 메모리에 보관하는 것이 좋지 않습니다. –

+0

s/huse/huge : D –

답변

0

정말 요청에 대한 임시 파일을 만들고 싶지 않습니다. 가능한 경우 CSV를 메모리에 보관하십시오.

파일 출력을 출력물과 직접 묶어야 할 수도 있습니다. 따라서 결과 집합의 행을 구문 분석하고 응답 스트림에 기록하고 다음 행을 구문 분석하는 등의 작업을 수행 할 수 있습니다. 그렇게하면 한 번에 한 행만 메모리에 유지할 수 있습니다. 문제가 발생하여 응답 시간이 초과 될 수 있습니다.

바로 가기 방법을 원하시면 Display tag library을보십시오. 테이블에 많은 결과를 표시하고 사전 작성된 export options을 상기 테이블에 추가하는 것은 매우 쉽습니다. CSV는 이러한 옵션 중 하나입니다.

+0

실제로 우리는 이전에 DisplayTag를 사용하고 있었지만 이제는 크기 제한으로 인해 피할 수 있습니다. 나는 ** CSV **를 기억으로 가질 수 없다. 이것은 매우 거대 할 수도있다. –

5

file.deleteOnExit()는 원하는 결과를 생성하지 않습니다. 목적은 JVM이 종료 될 때 파일을 삭제하는 것입니다. 서블릿에서이 파일을 삭제하면 서버를 삭제할 수 있습니다. 닥쳐.

file.delete()가 작동하지 않는 이유는이 코드에서 파일에서 읽고 서블릿의 출력 스트림에 쓰는 것입니다. 남은 파일에 데이터를 쓸 때 가능합니까? 파일의 입력 스트림이 열렸습니까? 현재 사용중인 파일은 삭제되지 않습니다.

또한 메서드가 IOException을 던져도 파일에 액세스하는 동안 예외가있는 경우 정리해야합니다. 파일 작업을 try 블록에 넣고 finally.close()를 finally 블록에 넣습니다.

+0

서버가 종료 된 경우에도 파일이 여전히 남아 있습니다. –

+1

Windows에 있습니까? 버그 보고서 - http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4171239 - Windows에서 file.deleteOnExit()이 작동하지 않는다는 사실을 알았습니다. JVM이 종료 될 때 파일이 열려 있으면 여전히 작동하지 않습니다. file.delete()가 작동하지 않기 때문에 파일은 여전히 ​​어떤 식 으로든 열려 있습니다. 현재 JVM에서이 버그의 상태에 대해 확신하지 못합니다. 1.3까지 '보고 된'것으로 표시되었지만 '릴리스 수정'에서 아무 것도 볼 수 없습니다. 복제본을 만들었지 만 버그가 수정 된 것을 보지 못했습니다.이 중 하나의 중복입니다 ... – Nate

1

해당 파일을 만들지 마십시오. 결과 세트의 데이터를 직접 CSV responseoutputStream에 씁니다. 시간, 메모리, 디스크 공간 및 두통을 줄여줍니다.

정말 필요한 경우 File.createTempFile() 메소드를 사용해보십시오. VM이 이전에 삭제되지 않은 경우 정상적으로 중지되면이 파일이 삭제됩니다.

+1

이것이 서버 측에 있다면 JVM이 항상 실행 중일 때 임시 파일이 실제로 생성 될 수 있습니다. –

0

파일을 어떻게 작성하고 있습니까? 아마도 createTempFile을 사용해야합니다.

임시 파일을 삭제할 수 있어야합니다 (deleteOnExit 필요 없음). 파일을 삭제하려고 할 때 파일을 사용하고 있지 않습니까? 사용자 요청 당 하나의 파일을 가져야합니다 (이는 임시 파일을 피하고 모든 것을 메모리에 저장해야하는 또 다른 이유입니다).

1

여기에 일종의 동시성 문제가 있다고 가정합니다. 이 메소드를 정적이 아니게 만들고 임시 파일에 고유 한 이름 (예 : 현재 시간을 추가하거나 파일 이름에 guid를 사용)을 사용하십시오. 파일을 열 때 다른 사람이 파일을 열 때 첫 번째 삭제가 실패 할 가능성이 있습니다.

0

piped inputpiped output stream을 사용해보세요.그 버퍼는 pipe (exporter)와 pipe (pipe)에 데이터를 보내고 응답 출력 스트림에 쓰는 두 개의 쓰레드를 필요로한다.

1

DataInputStream dis - 파일을 삭제하려고 할 때 false 상태가됩니다. 또한 try-catch-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 
      } 
     } 
    } 
0

임시 파일이 필요하지 않습니다. 파일 크기를 기반으로 작성중인 바이트 버퍼는 OutOfMemoryError이 될 수도 있습니다. 모두 비효율적입니다.

행을 반복하면서 HTTP 응답에 ResultSet의 데이터를 즉시으로 작성하면됩니다. 기본적으로 : 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) {} 
    } 
} 

합니다. 이렇게하면 항상 하나의 데이터베이스 행만 메모리에 보관됩니다.

관련 문제