файл.delete() возвращает false, даже если file.exists(), file.CanRead(), file.CanWrite(), file.CanExecute() все возвращают true
-
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
, или на все будущиеURLConnection
s (глобально) в текущем сеансе 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
);они могут не работать с другими / более поздними версиями.