file.delete () возвращает false, хотя file.exists (), file.canRead (), file.canWrite (), file.canExecute () все возвращают true - PullRequest
86 голосов
/ 14 июня 2009

Я пытаюсь удалить файл, после записи чего-либо в нем, с FileOutputStream. Это код, который я использую для написания:

private void writeContent(File file, String fileContent) {
    FileOutputStream to;
    try {
        to = new FileOutputStream(file);
        to.write(fileContent.getBytes());
        to.flush();
        to.close();
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

Как видно, я очищаю и закрываю поток, но когда я пытаюсь удалить, file.delete() возвращает false.

Я проверил перед удалением, чтобы увидеть, существует ли файл, и: file.exists(), file.canRead(), file.canWrite(), file.canExecute() все возвращают true. Сразу после вызова этих методов я пытаюсь file.delete() и возвращает false.

Что-то я сделал не так?

Ответы [ 17 ]

94 голосов
/ 18 ноября 2010

Еще одна ошибка в Java. Я редко нахожу их, только мой второй в моей 10-летней карьере. Это мое решение, как уже упоминали другие. Я не использовал System.gc(). Но здесь, в моем случае, это абсолютно важно. Weird? ДА!

finally
{
    try
    {
        in.close();
        in = null;
        out.flush();
        out.close();
        out = null;
        System.gc();
    }
    catch (IOException e)
    {
        logger.error(e.getMessage());
        e.printStackTrace();
    }
}
45 голосов
/ 14 июня 2009

Это был довольно странный трюк, который сработал. Дело в том, что когда я ранее прочитал содержимое файла, я использовал BufferedReader. После прочтения я закрыл буфер.

Тем временем я переключился и теперь читаю контент, используя FileInputStream. Также после окончания чтения я закрываю поток. И теперь это работает.

Проблема в том, что у меня нет объяснения этому.

Я не знаю, BufferedReader и FileOutputStream несовместимы.

17 голосов
/ 22 мая 2011

Я попробовал эту простую вещь, и она, кажется, работает.

file.setWritable(true);
file.delete();

Это работает для меня.

Если это не работает, попробуйте запустить приложение Java с помощью sudo, если в Linux, и в качестве администратора, если в Windows. Просто чтобы убедиться, что у Java есть права на изменение свойств файла.

5 голосов
/ 27 февраля 2014

Прежде чем пытаться удалить / переименовать любой файл, вы должны убедиться, что все устройства чтения или записи (например, BufferedReader / InputStreamReader / BufferedWriter) правильно закрыты.

Когда вы пытаетесь читать / записывать ваши данные из / в файл, файл удерживается процессом и не освобождается до завершения выполнения программы. Если вы хотите выполнить операции удаления / переименования до завершения программы, вы должны использовать метод close(), который поставляется с классами java.io.*.

3 голосов
/ 14 июня 2009

Как прокомментировал Джон Скит, вы должны закрыть свой файл в блоке finally {...}, чтобы он всегда был закрыт. И вместо того, чтобы проглотить исключения с помощью e.printStackTrace, просто не перехватывайте и не добавляйте исключение в сигнатуру метода. Если вы не можете по какой-либо причине, по крайней мере, сделайте следующее:

catch(IOException ex) {
    throw new RuntimeException("Error processing file XYZ", ex);
}

Теперь вопрос № 2:

Что если вы сделаете это:

...
to.close();
System.out.println("Please delete the file and press <enter> afterwards!");
System.in.read();
...

Сможете ли вы удалить файл?

Кроме того, файлы сбрасываются при закрытии. Я использую IOUtils.closeQuietly (...), поэтому я использую метод flush, чтобы убедиться, что содержимое файла есть, прежде чем пытаться его закрыть (IOUtils.closeQuietly не выдает исключений). Примерно так:

...
try {
    ...
    to.flush();
} catch(IOException ex) {
    throw new CannotProcessFileException("whatever", ex);
} finally {
    IOUtils.closeQuietly(to);
}

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

2 голосов
/ 04 апреля 2013

Если вы работаете в Eclipse IDE, это может означать, что вы не закрывали файл при предыдущем запуске приложения. Когда у меня появилось то же сообщение об ошибке при попытке удалить файл, это было причиной. Похоже, Eclipse IDE не закрывает все файлы после завершения приложения.

2 голосов
/ 14 июня 2009

Нет причин, по которым вы не сможете удалить этот файл. Я хотел бы посмотреть, кто держит этот файл. В unix / linux вы можете использовать утилиту lsof, чтобы проверить, какой процесс заблокировал файл. В windows вы можете использовать Process Explorer.

для lsof, это так же просто, как сказать:

lsof /path/and/name/of/the/file

для Process Explorer вы можете использовать меню поиска и ввести имя файла, чтобы показать вам дескриптор, который укажет вам на процесс, блокирующий файл.

вот код, который делает то, что, я думаю, вам нужно сделать:

FileOutputStream to;

try {
    String file = "/tmp/will_delete.txt";
    to = new FileOutputStream(file );
    to.write(new String("blah blah").getBytes());
    to.flush();
    to.close();
    File f = new File(file);
    System.out.print(f.delete());
} catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

Он отлично работает на OS X. Я не тестировал его на Windows, но подозреваю, что он должен работать и на Windows. Я также признаю, что видел некоторые неожиданные действия на Windows w.r.t. обработка файлов.

1 голос
/ 16 января 2014

ответ: когда вы загружаете файл, вам нужно применить метод close в любой строке кода, который мне подходит

1 голос
/ 10 ноября 2009

Надеюсь, это поможет. Я столкнулся с подобной проблемой, когда не мог удалить свой файл после того, как мой java-код скопировал содержимое в другую папку. После интенсивного поиска в Google я явно объявил переменные, связанные с каждой отдельной файловой операцией, и вызвал метод close () для каждого объекта файловой операции и установил для них значение NULL. Затем есть функция System.gc (), которая очистит файл ввода / вывода (я не уверен, я просто расскажу, что указано на веб-сайтах).

Вот мой пример кода:

public void start() {
    File f = new File(this.archivePath + "\\" + this.currentFile.getName());
    this.Copy(this.currentFile, f);

    if(!this.currentFile.canWrite()){
        System.out.println("Write protected file " +
           this.currentFile.getAbsolutePath());

        return;
    }


    boolean ok = this.currentFile.delete();
    if(ok == false){
        System.out.println("Failed to remove " + this.currentFile.getAbsolutePath());
        return;
    }
}

private void Copy(File source, File dest) throws IOException {
    FileInputStream fin;
    FileOutputStream fout;
    FileChannel cin = null, cout = null;
    try {
        fin = new FileInputStream(source);
        cin = fin.getChannel();
        fout = new FileOutputStream(dest);
        cout = fout.getChannel();

        long size = cin.size();
        MappedByteBuffer buf = cin.map(FileChannel.MapMode.READ_ONLY, 0, size);

        cout.write(buf);
        buf.clear();
        buf = null;

        cin.close();
        cin = null;

        fin.close();
        fin = null;

        cout.close();
        cout = null;

        fout.close();
        fout = null;

        System.gc();

    } catch (Exception e){
        this.message = e.getMessage();
        e.printStackTrace();
    }
}
0 голосов
/ 20 февраля 2019

Другой случай, когда это может произойти: если вы читаете / записываете файл JAR через URL, а затем пытаетесь удалить тот же файл в том же сеансе JVM.

File f = new File("/tmp/foo.jar");
URL j = f.toURI().toURL();

URL u = new URL("jar:" + j + "!/META-INF/MANIFEST.MF");
URLConnection c = u.openConnection();

// open a Jar entry in auto-closing manner
try (InputStream i = c.getInputStream()) {

    // just read some stuff; for demonstration purposes only
    byte[] first16 = new byte[16];
    i.read(first16);
    System.out.println(new String(first16));
}

// ...

// i is now closed, so we should be good to delete the jar; but...
System.out.println(f.delete());     // says false!

Причина в том, что внутренняя логика обработки файлов JAR Java имеет тенденцию кешировать JarFile записей:

// inner class of `JarURLConnection` that wraps the actual stream returned by `getInputStream()`

class JarURLInputStream extends FilterInputStream {
    JarURLInputStream(InputStream var2) {
        super(var2);
    }

    public void close() throws IOException {
        try {
            super.close();
        } finally {

            // if `getUseCaches()` is set, `jarFile` won't get closed!

            if (!JarURLConnection.this.getUseCaches()) {
                JarURLConnection.this.jarFile.close();
            }
        }
    }
}

И каждый JarFile (точнее, базовая ZipFile структура) будет содержать дескриптор файла, начиная с момента создания и до вызова close():

public ZipFile(File file, int mode, Charset charset) throws IOException {
    // ...

    jzfile = open(name, mode, file.lastModified(), usemmap);

    // ...
}

// ...

private static native long open(String name, int mode, long lastModified,
                                boolean usemmap) throws IOException;

Есть хорошее объяснение этой проблемы NetBeans .


Видимо, есть два способа «исправить» это:

  • Вы можете отключить кэширование файла JAR - для текущего URLConnection или для всех будущих URLConnection с (глобально) в текущем сеансе JVM:

    URL u = new URL("jar:" + j + "!/META-INF/MANIFEST.MF");
    URLConnection c = u.openConnection();
    
    // for only c
    c.setUseCaches(false);
    
    // globally; for some reason this method is not static,
    // so we still need to access it through a URLConnection instance :(
    c.setDefaultUseCaches(false);
    
  • [HACK WARNING!] Вы можете вручную удалить JarFile из кэша, когда закончите с ним. Диспетчер кэша sun.net.www.protocol.jar.JarFileFactory является частным пакетом, но некоторая магия отражения может выполнить работу за вас:

    class JarBridge {
    
        static void closeJar(URL url) throws Exception {
    
            // JarFileFactory jarFactory = JarFileFactory.getInstance();
            Class<?> jarFactoryClazz = Class.forName("sun.net.www.protocol.jar.JarFileFactory");
            Method getInstance = jarFactoryClazz.getMethod("getInstance");
            getInstance.setAccessible(true);
            Object jarFactory = getInstance.invoke(jarFactoryClazz);
    
            // JarFile jarFile = jarFactory.get(url);
            Method get = jarFactoryClazz.getMethod("get", URL.class);
            get.setAccessible(true);
            Object jarFile = get.invoke(jarFactory, url);
    
            // jarFactory.close(jarFile);
            Method close = jarFactoryClazz.getMethod("close", JarFile.class);
            close.setAccessible(true);
            //noinspection JavaReflectionInvocation
            close.invoke(jarFactory, jarFile);
    
            // jarFile.close();
            ((JarFile) jarFile).close();
        }
    }
    
    // and in your code:
    
    // i is now closed, so we should be good to delete the jar
    JarBridge.closeJar(j);
    System.out.println(f.delete());     // says true, phew.
    

Обратите внимание: Все это основано на кодовой базе Java 8 (1.8.0_144); они могут не работать с другими / более поздними версиями.

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