файл.delete() возвращает false, даже если file.exists(), file.CanRead(), file.CanWrite(), file.CanExecute() все возвращают true

StackOverflow https://stackoverflow.com/questions/991489

  •  13-09-2019
  •  | 
  •  

Вопрос

Я пытаюсь удалить файл, предварительно записав в него что-то, с 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.

Есть ли что-нибудь, что я сделал не так?

Это было полезно?

Решение 2

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

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

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

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

Другие советы

Еще одна ошибка в 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();
    }
}

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

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

У меня это работает.

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

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

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

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

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

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

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

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

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

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

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.обработка файлов.

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

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

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

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

Ни одно из перечисленных здесь решений не сработало в моей ситуации.Мое решение состояло в том, чтобы использовать цикл 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 и т.д.

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

Вы должны закрыть все потоки или использовать блок 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;
}

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

У меня была такая же проблема в 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

работает.

ДЛЯ 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");
  }
}

Выходной сигнал:

Удалить

Еще один угловой случай, когда это могло произойти:если вы читаете / записываете 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);
    
  • [ПРЕДУПРЕЖДЕНИЕ О ВЗЛОМЕ!] Вы можете вручную очистить 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);они могут не работать с другими / более поздними версиями.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top