Как / когда удалить файл в Java? - PullRequest
1 голос
/ 06 августа 2009

Проблема в том, что пользователь нажимает кнопку в 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 (); но никто из них не работает.

Ответы [ 8 ]

5 голосов
/ 06 августа 2009

file.deleteOnExit () не даст желаемого результата - его целью является удаление файла при выходе из JVM - если это вызывается из сервлета, это означает удаление файла при завершении работы сервера вниз.

Что касается того, почему file.delete () не работает - все, что я вижу в этом коде, - это чтение из файла и запись в выходной поток сервлета - возможно ли это, когда вы записали данные в файл, который вы оставили входной поток файла открыт? Файлы не будут удалены, если они используются в данный момент.

Кроме того, несмотря на то, что ваш метод выдает IOException, вам все равно нужно очистить вещи, если при обращении к файлу возникает исключение - поместите файловые операции в блок try и поместите stream.close () в блок finally.

1 голос
/ 01 марта 2011

как я понимаю, вы не закрываете 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
            }
        }
    }
1 голос
/ 06 августа 2009

Я предполагаю, что у вас здесь есть какая-то проблема параллелизма. Попробуйте сделать этот метод нестатичным и использовать уникальное имя для временного файла (например, добавить текущее время или использовать guid для имени файла). Скорее всего, вы открываете файл, а затем его открывает кто-то другой, поэтому первое удаление не удается.

1 голос
/ 06 августа 2009

Не создавайте этот файл. Запишите ваши данные непосредственно из вашего набора результатов в CSV responseoutputStream. Это экономит время, память, дисковое пространство и головную боль.

Если вам это действительно нужно, попробуйте использовать метод File.createTempFile (). Эти файлы будут удалены, когда ваша виртуальная машина остановится обычно, если они не были удалены ранее.

0 голосов
/ 01 марта 2011

Вам не нужен временный файл. Байт-буфер, который вы создаете там в зависимости от размера файла, также может вызвать OutOfMemoryError. Это все просто неэффективно.

Просто запишите данные ResultSet немедленно в ответ HTTP, итерируя по строкам. В основном: writer.write(resultSet.getString("columnname")). Таким образом, вам не нужно записывать его во временный файл или сожрать все в памяти Java.

Кроме того, большинство драйверов JDBC по умолчанию кэшируют все в памяти Java, прежде чем что-либо выдать ResultSet#next(). Это тоже неэффективно. Вы бы хотели, чтобы он давал данные сразу построчно, устанавливая Statement#setFetchSize(). Как это сделать правильно, зависит от используемого драйвера JDBC. В случае, например, MySQL, вы можете прочитать его в документации драйвера JDBC .

Вот начальный пример, предполагающий, что вы используете 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) {}
    }
}

Вот и все. Таким образом, только одна строка базы данных постоянно сохраняется в памяти.

0 голосов
/ 06 августа 2009

вы можете попробовать конвейерный ввод и конвейерный поток . этим буферам нужны два потока, один для подачи в канал (экспортер), а другой (сервлет) для получения данных из канала и записи их в поток вывода ответа

0 голосов
/ 06 августа 2009

Как вы создаете файл. Вам, вероятно, нужно использовать createTempFile .

Вы должны быть в состоянии удалить временный файл просто отлично (нет необходимости в deleteOnExit). Вы уверены, что файл не используется, когда вы пытаетесь удалить его? У вас должен быть один файл на запрос пользователя (это еще одна причина, по которой вам следует избегать временных файлов и хранить все в памяти).

0 голосов
/ 06 августа 2009

Вы действительно не хотите создавать временный файл для запроса. Сохраните полученный CSV в памяти, если это вообще возможно.

Возможно, вам придется связать запись файла непосредственно с выводом. Итак, проанализируйте строку набора результатов, запишите ее в поток ответов, проанализируйте следующую строку и так далее. Таким образом, вы сохраняете только одну строку в памяти за раз. Проблема в том, что время ответа может истечь.

Если вы хотите использовать ярлык, взгляните на Показать библиотеку тегов . Это позволяет очень просто показать кучу результатов в таблице, а затем добавить предварительно созданные параметры экспорта в указанную таблицу. CSV является одним из таких вариантов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...