2015-07-24 2 views
18

Пожалуйста, рассмотрим следующий пример Java класс (pom.xml ниже):Странное поведение при удалении файлов с Files.delete()

package test.filedelete; 

import java.io.ByteArrayInputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.nio.file.Files; 
import java.nio.file.NoSuchFileException; 
import java.nio.file.Path; 

import org.apache.commons.io.IOUtils; 

public class Main 
{ 
    public static void main(String[] args) throws IOException 
    { 
     byte[] bytes = "testtesttesttesttesttesttesttesttesttest".getBytes(); 
     InputStream is = new ByteArrayInputStream(bytes); 

     Path tempFileToBeDeleted = Files.createTempFile("test", ""); 
     OutputStream os = Files.newOutputStream(tempFileToBeDeleted); 
     IOUtils.copy(is, os); 

     deleteAndCheck(tempFileToBeDeleted); 

     // breakpoint 1 
     System.out.println("\nClosing stream\n"); 

     os.close(); 

     deleteAndCheck(tempFileToBeDeleted); 
    } 

    private static void deleteAndCheck(Path file) throws IOException 
    { 
     System.out.println("Deleting file: " + file); 
     try 
     { 
      Files.delete(file); 
     } 
     catch (NoSuchFileException e) 
     { 
      System.out.println("No such file"); 
     } 
     System.out.println("File really deleted: " + !Files.exists(file)); 

     System.out.println("Recreating deleted file ..."); 
     try 
     { 
      Files.createFile(file); 
      System.out.println("Recreation successful"); 
     } 
     catch (IOException e) 
     { 
      System.out.println("Recreation not possible, exception: " + e.getClass().getName()); 
     } 
    } 
} 

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

При запуске основного метода на Windows 7 она производит следующий вывод:

Deleting file: C:\Users\MSCHAE~1\AppData\Local\Temp\test6100073603559201768 
File really deleted: true 
Recreating deleted file ... 
Recreation not possible, exception: java.nio.file.AccessDeniedException 

Closing stream 

Deleting file: C:\Users\MSCHAE~1\AppData\Local\Temp\test6100073603559201768 
No such file 
File really deleted: true 
Recreating deleted file ... 
Recreation successful 
  • Почему первый вызов Files.delete() не бросить исключение?
  • Почему следующий вызов Files.exist() возвращает false?
  • Почему невозможно создать файл заново?

Что касается последнего вопроса, я заметил, что файл по-прежнему отображается в проводнике при остановке в точке останова 1. Когда вы завершаете JVM, тогда файл будет удален в любом случае. После закрытия потока deleteAndCheck() работает так, как ожидалось.

Мне кажется, что удаление не распространяется на ОС перед закрытием потока, и API файлов не отражает это правильно.

Может кто-нибудь объяснить, что здесь происходит?

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 
    <groupId>test</groupId> 
    <artifactId>filedelete</artifactId> 
    <version>0.0.1-SNAPSHOT</version> 

    <dependencies> 
     <dependency> 
      <groupId>commons-io</groupId> 
      <artifactId>commons-io</artifactId> 
      <version>2.4</version> 
     </dependency> 
    </dependencies> 
</project> 

Обновление для осветления

файл исчезает в Windows Explorer, если поток закрыт и Files.delete() называется - последняя операция триггеры - или если вызван файл.delete(), не закрывая поток, и JVM завершается.

+0

Какая ОС это? Если Unix-like, можете ли вы попробовать и распечатать разрешения родительского каталога? – fge

+1

И, кстати, вы можете напрямую использовать 'Files.copy()' для копирования содержимого 'InputStream' в' Path'; нет необходимости в IOUtils – fge

+0

Попробуйте положить точку останова на 'Files.delete (file);' и затем пройти мимо нее. В этот момент файл фактически исчезает из окна проводника? Я подозрительно, что каким-то образом удаление успешно завершается, но окна хранят файл, потому что есть другая ссылка, открытая для него. Это может быть что-то низкоуровневое в том, как работают окна. Я бы не ожидал такого же в системе Posix (например: Linux). –

ответ

17

Можете ли вы удалить открытый файл?

Совершенно верно для удаления записи в каталоге файла при открытии файла. В Unix это семантика по умолчанию, и Windows ведет себя аналогично, пока FILE_SHARE_DELETE установлен для всех дескрипторов файлов, открытых для этого файла.

[Edit: Благодаря @couling для обсуждения и поправок]

Однако есть небольшая разница: Unix удаляет файл имя сразу, в то время как для Windows удаляет только имя файла, когда последний ручка закрыта. Однако он не позволяет открывать файл с тем же именем, пока последний дескриптор (удаленный) файл не будет закрыт.

Перейти фигура ...

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

Экскурсия: Windows

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

Ситуация на самом деле не улучшается за счет того, что документация Windows API кажется расплывчатым на процесс (сознательно?):

CreateFile():

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

В противном случае другие процессы не смогут открыть файл или устройство, если они запрашивают доступ к удалению.

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

DeleteFile():

Функция DeleteFile помечает файл для удаления при закрытии. Поэтому удаление файла не происходит до тех пор, пока последний дескриптор файла не будет закрыт. Последующие вызовы CreateFile для открытия файла не выполняются с помощью ERROR_ACCESS_DENIED.

Наличие открытого дескриптора файла без имени является одним из наиболее типичных способов создания неназванных временных файлов. Создайте новый файл, откройте его, удалите файл. Теперь у вас есть дескриптор файла, который никто не может открыть. В Unix имя файла действительно исчезло, и в Windows вы не можете открыть файл с тем же именем.

Вопрос теперь:

ли Files.newOutputStream() установить FILE_SHARE_DELETE?

Глядя на the source, вы можете видеть, что shareDelete действительно по умолчанию true. Единственный способ сбросить его - использовать нестандартный ExtendedOpenOptionNOSHARE_DELETE.

Да, вы можете удалить открытые файлы на Java, если они явно не заблокированы.

Почему я не могу повторно создать удаленный файл?

Ответ на этот вопрос скрыт в документации DeleteFile(): Файл помечен для удаления, файл все еще существует.В Windows вы не можете создать файл с именем файла, помеченного для удаления, до тех пор, пока файл не будет правильно удален, то есть все дескрипторы файла будут закрыты.

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

Почему Files.exists()false?

Files.exists() в омут на Windows, открывает этот файл в какой-то момент, и мы уже знаем, что мы не можем повторно открыть удаленный-но все еще открыт файл на Windows,.

Подробно: Java-код вызывает FileSystemProvider.checkAccess()) без аргументов, который вызывает WindowsFileSystemProvider.checkReadAccess(), который сразу пытается открыть файл и, следовательно, не работает. Из того, что я могу сказать, это путь, который вы делаете, когда звоните Files.exist().

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

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

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

Такое поведение является более или менее последовательно, потому что с помощью GetFileAttributes(), чтобы проверить, существует ли файл является фактически файл доступность чек, который интерпретируется как файл существует. FindFirstFile() (используется проводником Windows для определения списка файлов) находит файл имена, но ничего не сообщает о доступности имен.

Добро пожаловать в еще несколько странных петель в вашей голове.

+0

Я не уверен в ваших аргументах относительно реализации Windows здесь и ее сходства с Linux. Ваша справочная информация касается разрешений на открытие, но не будет ли фактическая операция удаления успешной. Мне нужно было бы увидеть пример низкого уровня (написанный на С или аналогичный), чтобы доказать это. Обычно окна имеют гораздо более прочную связь между именем и файлом, чем системы POSIX. Обычно файлы не существуют в Windows, если имя отсутствует. –

+0

@couling Прежде чем я докажу чайник, что, по-вашему, для FILE_SHARE_DELETE'? – dhke

+0

его ошибка руководства для того, чтобы быть неясным. Извините, что я педантичен, но то, что он говорит, - «В противном случае другие процессы не могут открыть файл или устройство, если они запрашивают доступ к доступу». Это говорит о «открытой» функции, а не о «удалении» функции и явно не говорит что удаление может произойти, пока файл открыт в другом месте. –

2

Если Files.delete не выбрасывает исключение, это означает, что он удалил файл. Files.delete javadoc говорит, что «на некоторых операционных системах может быть невозможно удалить файл, когда он открыт и используется этой виртуальной машиной Java или другими программами».

+0

Но разве он не должен бросать IO/Access Exception? - Я имею в виду, теперь он говорит «хорошо, удалил», фактически не удалив его. Удаление происходит в тот момент, когда выходной поток закрыт. Таким образом, вы могли бы теоретически продолжить писать контент, не зная, что удаление произойдет в конце ... – dognose

+2

@dognose Поскольку файл * имя * ушел, то есть удаление было успешным.Однако файл все еще существует (пока вы не закроете ему последний дескриптор). Приложения Windows часто заставляют вас думать, что вы не можете удалять открытые файлы, но это на самом деле не так. – dhke

Смежные вопросы