file.delete () возвращает false, даже если file.exists (), file.canRead (), file.canWrite (), file.canExecute () все возвращают true


90

Пытаюсь удалить файл, написав что-то в нем, с расширением 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()все возвращаются правда. Сразу после вызова этих методов я пытаюсь file.delete()вернуть false.

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


Забыл упомянуть, что никаких исключений не обнаружено.
Дженни Смит,

Вы уверены, что файл не используется другим процессом? Вы его заперли? Работает ли с deleteOnExit + выходом?
instanceof me

На какой ОС вы работаете? Вы можете вручную удалить файл? Что-то может удерживать открытый дескриптор файла.
akarnokd

Как я могу узнать, используется ли он другим процессом? Я его не запирал. Я не могу использовать метод deleteOnExit, потому что мне нужно удалить файл и продолжить применение после этого. Идея состоит в том, что я пытаюсь удалить временную папку, которая используется для хранения файлов из другой папки в течение одного сеанса. Когда я нажимаю кнопку «Открыть» на своем приложении, это означает, что папку необходимо очистить, чтобы освободить место для некоторых других файлов. Если я пропущу часть при записи в этот файл, все в порядке, и я могу удалить его.
Дженни Смит,

1
Не могли бы вы заключить флеш-энд-закрытие в блок finally? Я знаю, что вы это сделали, и это не работает, но это внесет ясность в ваш вопрос.
bharal

Ответы:


100

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

finally
{
    try
    {
        in.close();
        in = null;
        out.flush();
        out.close();
        out = null;
        System.gc();
    }
    catch (IOException e)
    {
        logger.error(e.getMessage());
        e.printStackTrace();
    }
}

7
Хотя я не открывал его ни с каким потоком (просто выполнял new File(path)), я столкнулся с той же проблемой и добавлением System.gc()до того, как delete()он заработал!
ixM

1
Какая еще ошибка?
bogdan.mustiata

не работает для меня. Файл, который я не могу удалить, представляет собой ZIP-файл с файлом loadedFromURL. Приятно то, что загруженный файл будет удален. Любая идея?
Andrea_86

Работал у меня. И @andreadi Я никогда не использовал FileChannel.map в своем коде. Кажется, что эта ошибка закрыта как неразрешимая, что вызывает у меня недоверие, потому что System.gcтрюк срабатывает каждый раз.
Адам Берли

Single gc () может не помочьSystem.gc(); result = file.delete(); if (!result){ Thread.sleep(100); System.gc(); result = file.delete(); }
notes-jj

48

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

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

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

Не знаю BufferedReaderи FileOutputStreamбыть несовместимым.


4
Тогда я бы сказал, что это ошибка: java.sun.com/javase/6/docs/api/java/io/… Там говорится, что метод должен закрыть поток и все связанные с ним ресурсы. Обычно мне нравится закрывать все потоки в перевернутой последовательности (последние открываются - закрываются первыми) с помощью IOUtils.closeQuietly, но это, как правило, излишне.
Рави Валлау,

2
У меня была именно такая проблема, я думал, что схожу с ума. Спасибо за решение!
Electrons_Ahoy

14
2011 год с JDK 7, и проблема все еще не решена. Я так рад, что нашел эту ветку - я просто не мог понять, что случилось ...
Дэвид

У меня была очень неприятная проблема с файлом, который я прочитал и закрыл, а затем попытался удалить. Ничего из упомянутого здесь не помогло. Затем, через час, я обнаружил, что это были просто права доступа к файлу, поскольку файл был создан другим пользователем :-D
runholen

Просто удар в темноте ... Вы можете позвонить, finalize()чтобы организовать уборку? Или, может быть, установить to = null? См. Раздел « Принудительная финализация и сборка мусора» .
jww

19

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

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

Меня устраивает.

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


1
Для этой проблемы; обычно люди говорят об установке ссылок как null или вызове system.gc (); но для меня даже после перезагрузки сервера файлы не удалялись !! Но file.setWritable (true); просто сработало ..
Deepak Singhal

6

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

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


1
Это верно только для Windows. На любой реальной ОС вы можете удалять и переименовывать открытые файлы.
Archie

3

Как прокомментировал Джон Скит, вам следует закрыть файл в блоке 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);
}

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


Я попробовал первое - закрыть выходной поток в блоке finally. И это не сработало. Что произошло, если я попытался прочитать после этого, так это то, что я получил сообщение о том, что поток был закрыт. При переключении и чтении файла с помощью FileInputReader ничего из описанного выше "плохого" не произошло.
Дженни Смит,

2

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

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

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

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

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

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.


1
Для Windows вы можете бесплатно загрузить Process Explorer с сайта
akarnokd

2

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


1

Надеюсь, это поможет. Я столкнулся с аналогичной проблемой, когда я не мог удалить свой файл после того, как мой 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();
    }
}

1

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


0

Однажды в Ruby возникла проблема, когда файлам в Windows требовался "fsync", чтобы на самом деле иметь возможность развернуть и перечитать файл после его записи и закрытия. Возможно, это аналогичное проявление (и если да, то я действительно думаю, что это ошибка Windows).


0

Ни одно из перечисленных здесь решений не помогло в моей ситуации. Мое решение состояло в том, чтобы использовать цикл while, пытаясь удалить файл с 5-секундным (настраиваемым) пределом для безопасности.

File f = new File("/path/to/file");

int limit = 20; //Only try for 5 seconds, for safety
while(!f.delete() && limit > 0){
    synchronized(this){
        try {
            this.wait(250); //Wait for 250 milliseconds
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    limit--;
}

Использование вышеуказанного цикла работало без необходимости выполнять сборку мусора вручную или устанавливать для потока значение null и т. Д.


Что ТАК вы используете? Если у вас есть доступ к ПОТОКАМ, закройте их, все заработает. Но если файл «уже» заблокирован, значит, у вас проблема.
marcolopes

1
-1 цикл в вашем коде для удаления файла - не лучшая идея. Почему бы просто не использовать работающую опцию gc ()?
bharal

@bharal Примечание. Выше я заявляю, что ни одно из других решений (включая gc ()) не сработало в моем конкретном случае. Хотя я согласен, что использование цикла while не идеально, в определенных обстоятельствах это может быть подходящим решением. Добавление дополнительной проверки в приведенный выше код, такой как тайм-аут или переменная максимального числа итераций, было бы хорошим способом сделать цикл while более безопасным.
etech

@etech, если ваш файл никогда не существовал, вы только что создали бесконечный цикл.
bharal

0

Проблема может заключаться в том, что файл все еще рассматривается как открытый и заблокированный программой; или, возможно, это компонент вашей программы, в котором он был открыт, поэтому вы должны убедиться, что используете dispose()метод для решения этой проблемы. т.е.JFrame frame; .... frame.dispose();


0

Вы должны закрыть все потоки или использовать блок try-with-resource

static public String head(File file) throws FileNotFoundException, UnsupportedEncodingException, IOException
{
    final String readLine;
    try (FileInputStream fis = new FileInputStream(file);
            InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
            LineNumberReader lnr = new LineNumberReader(isr))
    {
        readLine = lnr.readLine();
    }
    return readLine;
}

0

если file.delete () отправляет false, то в большинстве случаев ваш дескриптор Bufferedreader не будет закрыт. Просто закройтесь и у меня вроде нормально работает.


0

У меня была такая же проблема в Windows. Раньше я читал файл в scala построчно с

Source.fromFile(path).getLines()

Сейчас читаю целиком с

import org.apache.commons.io.FileUtils._

// encoding is null for platform default
val content=readFileToString(new File(path),null.asInstanceOf[String])

который закрывает файл после чтения и теперь

new File(path).delete

работает.


0

ДЛЯ Eclipse / NetBeans

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

Вот мой код:

File file = new File("file-path");
if(file.exists()){
  if(file.delete()){
     System.out.println("Delete");
  }
  else{

       System.out.println("not delete");
  }
}

Выход:

удалять


0

Другой угловой случай, когда это могло произойти: если вы читаете / записываете файл JAR через a 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); они могут не работать с другими / более поздними версиями.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.