file.delete () повертає false, хоча file.exists (), file.canRead (), file.canWrite (), file.canExecute () all повертає 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.

Щось я зробив неправильно?


Я забув згадати, що жодного винятку не ловлять.
Jenny Smith

Ви впевнені, що файл не використовується іншим процесом? Ти замкнув? Чи працює це з deleteOnExit + виходом?
instanceof me

На якій ОС ви працюєте? Чи можете ви вручну видалити файл? Щось може містити відкритий дескриптор файлу.
akarnokd

Як я можу дізнатися, чи використовується іншим процесом? Я не замикав його. Я не можу використовувати метод deleteOnExit, тому що мені потрібно видалити файл і продовжити свою заявку після цього. Ідея полягає в тому, що я намагаюся звільнити тимчасову папку, яка використовується для зберігання файлів з іншої папки протягом одного сеансу. Коли я натискаю кнопку відкриття у своєму застосунку, це означає, що папку потрібно очистити, щоб звільнити місце для деяких інших файлів. Якщо я пропускаю частину, коли пишу у цей файл, це нормально, і я можу її видалити.
Jenny Smith

1
Чи можете ви, будь ласка, обернути свій флеш і закрити нарешті блок? Я знаю, що ви це зробили, і це не працює, але це додасть ясності вашому питанню.
bharal

Відповіді:


99

Чергова помилка в 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
Хоча я не відкривав його за допомогою будь-якого потоку (просто виконуючи a new File(path)), я зіткнувся з тією ж проблемою та додав, System.gc()перш ніж delete()змусив це запрацювати!
ixM

1
Яка ще помилка?
bogdan.mustiata

не хвилюється для мене. Файл, який я не можу видалити, - це ZIP-файл завантаженого із URL-файлу. Приємно те, що завантажений файл буде видалено. Будь-яка ідея?
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
Для цієї проблеми; зазвичай люди говорять про встановлення посилань як нульових чи викликів system.gc (); але для мене навіть після перезапуску сервера файли не видалялися !! Але file.setWritable (true); щойно працював ..
Діпак Сінгаль

6

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

Коли ви намагаєтеся прочитати / записати дані з / у файл, файл утримується процесом і не випускається до завершення виконання програми. Якщо ви хочете виконати операції видалення / перейменування до закінчення програми, тоді ви повинні використовувати close()метод, що постачається з java.io.*класами.


1
Це справедливо лише для Windows. На будь-якому реальному O / S ви можете видаляти та перейменовувати відкриті файли.
Арчі

3

Як прокоментував Джон Скіт, вам слід закрити файл у блоці нарешті {...}, щоб він завжди був закритим. І замість того, щоб проковтнути винятки за допомогою e.printStackTrace, просто не ловіть і не додайте виняток до підпису методу. Якщо з якихось причин ви не можете, принаймні зробіть так:

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

Тепер питання No2:

Що, якщо ви зробите це:

...
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);
}

Тож я знаю, що вміст файлу там. Оскільки для мене зазвичай важливо, щоб вміст файлу було написано, а не якщо файл можна було закрити чи ні, насправді неважливо, закритий файл чи ні. У вашому випадку, оскільки це має значення, я б рекомендував закрити файл самостійно та обробляти будь-які винятки відповідно.


Я спробував перше - закрити вихідний потік у блоці нарешті. І це не спрацювало. Що сталося, якщо я спробував прочитати після цього, це те, що я отримав повідомлення про те, що потік закрито. При перемиканні та читанні файлу за допомогою FileInputReader не сталося жодного з «поганих» речей, описаних вище.
Jenny Smith

2

Немає жодної причини, через яку ви не зможете видалити цей файл. Я хотів би подивитися, хто затримав цей файл. У unix / linux ви можете використовувати утиліту lsof, щоб перевірити, який процес має блокування у файлі. У вікнах ви можете використовувати провідник процесів.

для 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. Я також зізнаюся, що бачу деяку несподівану поведінку при обробці файлів wrt Windows.


1
Для вікон, ви можете скачати безкоштовно Process Explorer від MS: technet.microsoft.com/en-us/sysinternals/bb896653.aspx
akarnokd

2

Якщо ви працюєте в Eclipse IDE, це може означати, що ви не закривали файл під час попереднього запуску програми. Коли у мене з’явилося таке саме повідомлення про помилку при спробі видалити файл, це було причиною. Здається, Eclipse IDE не закриває всі файли після припинення роботи програми.


1

Сподіваємось, це допоможе. Я зіткнувся з подібною проблемою, коли я не міг видалити свій файл після того, як мій код Java зробив копію вмісту в іншу папку. Після екстенсивного гуглювання я явно оголосив кожну змінну, пов'язану з операцією файлу, і викликав метод 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

відповідь полягає в тому, що коли ви завантажуєте файл, вам потрібно застосувати метод "закрити" в будь-якому рядку коду, який працює для мене


0

Одного разу виникла проблема в ruby, коли файли у вікнах потребували "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--;
}

Використання вищезазначеного циклу спрацювало без будь-якого ручного збору сміття чи встановлення потоку на нуль тощо.


Що ТО ви використовуєте? Якщо у вас є доступ до потоків, закрийте їх, це спрацює. Але якщо файл "уже" заблоковано, у вас проблема.
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. Раніше я читав файл у масштабі, рядок за рядком

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 через 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або для всіх майбутніх URLConnections (глобально) у поточному сеансі 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 ПОПЕРЕДЖЕННЯ!] Ви можете очистити 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.