file.delete () mengembalikan nilai salah meskipun file.exists (), file.canRead (), file.canWrite (), file.canExecute () semuanya mengembalikan nilai true

90

Saya mencoba menghapus file, setelah menulis sesuatu di dalamnya, dengan FileOutputStream. Ini adalah kode yang saya gunakan untuk menulis:

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

Seperti yang terlihat, saya menyiram dan menutup aliran, tetapi ketika saya mencoba untuk menghapus, file.delete()mengembalikan salah.

Aku diperiksa sebelum penghapusan untuk melihat apakah file ada, dan: file.exists(), file.canRead(), file.canWrite(), file.canExecute()semua return true. Tepat setelah memanggil metode ini saya mencoba file.delete()dan mengembalikan false.

Apakah ada kesalahan yang saya lakukan?

Jenny Smith
sumber
Saya lupa menyebutkan bahwa tidak ada pengecualian yang tertangkap.
Jenny Smith
Apakah Anda yakin file tersebut tidak digunakan oleh proses lain? Apakah kamu menguncinya? Apakah ini berfungsi dengan deleteOnExit + exiting?
contoh saya
OS apa yang Anda jalankan? Bisakah Anda menghapus file secara manual? Sesuatu mungkin memegang pegangan terbuka ke file.
akarnokd
Bagaimana saya bisa mengetahui apakah itu digunakan oleh proses lain? Saya tidak menguncinya. Saya tidak dapat menggunakan metode deleteOnExit, karena saya perlu menghapus file dan melanjutkan pengajuan saya setelah itu. Idenya adalah saya mencoba mengosongkan folder sementara, yang digunakan untuk menyimpan file dari folder berbeda untuk satu sesi. Ketika saya menekan tombol buka pada lamaran saya, itu berarti folder tersebut harus dikosongkan, untuk memberi ruang bagi beberapa file lainnya. Jika saya melewatkan bagian saat saya menulis di file itu, tidak apa-apa, dan saya dapat menghapusnya.
Jenny Smith
1
Bisakah Anda membungkus flush & menutup blok terakhir? Saya tahu Anda telah melakukan ini dan itu tidak berhasil, tetapi itu akan menambah kejelasan pada pertanyaan Anda.
bharal

Jawaban:

100

Bug lain di Jawa. Saya jarang menemukannya, hanya yang kedua dalam 10 tahun karir saya. Ini adalah solusi saya, seperti yang telah disebutkan orang lain. Saya telah menggunakan nether System.gc(). Tapi di sini, dalam kasus saya, ini sangat penting. Aneh? IYA!

finally
{
    try
    {
        in.close();
        in = null;
        out.flush();
        out.close();
        out = null;
        System.gc();
    }
    catch (IOException e)
    {
        logger.error(e.getMessage());
        e.printStackTrace();
    }
}
Da Martin
sumber
7
Meskipun saya tidak membukanya dengan aliran apa pun (hanya melakukan a new File(path)), saya mengalami masalah yang sama dan menambahkan System.gc()sebelum delete()membuatnya berfungsi!
ixM
1
Apa bug lainnya?
bogdan.mustiata
tidak bekerja untuk saya. File yang tidak dapat saya hapus adalah file ZIP dari file downloadFromURL. Yang menyenangkan adalah file yang diunduh akan dihapus. Ada ide?
Andrea_86
Bekerja untuk saya. Dan @andreadi saya tidak pernah menggunakan FileChannel.map dalam kode saya. Tampaknya bug ini ditutup karena tidak dapat diselesaikan yang membuat saya tidak percaya karena System.gctriknya berfungsi setiap saat.
Adam Burley
Single gc () mungkin tidak membantuSystem.gc(); result = file.delete(); if (!result){ Thread.sleep(100); System.gc(); result = file.delete(); }
notes-jj
48

Cukup aneh trik yang berhasil. Masalahnya adalah ketika saya sebelumnya membaca konten file, saya menggunakan BufferedReader. Setelah membaca, saya menutup buffer.

Sementara itu saya beralih dan sekarang saya membaca konten menggunakan FileInputStream. Juga setelah selesai membaca, saya menutup aliran. Dan sekarang berhasil.

Masalahnya saya tidak punya penjelasan untuk ini.

Saya tidak tahu BufferedReaderdan FileOutputStreamtidak cocok.

Jenny Smith
sumber
4
Saya berpendapat bahwa ini adalah bug: java.sun.com/javase/6/docs/api/java/io/… Di sana dikatakan bahwa metode tersebut harus menutup aliran dan sumber daya yang terkait dengannya. Saya biasanya suka menutup semua aliran dalam urutan terbalik (yang terakhir dibuka adalah yang pertama ditutup) menggunakan IOUtils.closeQuietly, tetapi cenderung berlebihan.
Ravi Wallau
2
Saya mengalami masalah ini saya dan saya pikir saya akan gila. Terima kasih atas solusinya!
Electrons_Ahoy
14
Ini tahun 2011 dengan JDK 7 dan masalahnya masih belum diperbaiki. Saya sangat senang saya menemukan utas ini - Saya benar-benar tidak tahu apa yang salah ...
David
Saya mengalami masalah yang sangat menjengkelkan dengan file yang saya baca dan tutup dan kemudian coba hapus. Tidak ada yang disebutkan di sini yang membantu. Kemudian setelah satu jam saya menemukan bahwa itu hanya hak file, karena file tersebut telah dibuat oleh pengguna lain :-D
runholen
Hanya menusuk dalam kegelapan ... Bisakah Anda menelepon finalize()untuk memastikan pembersihan? Atau mungkin diatur to = null? Lihat Memaksa Penyelesaian dan Pengumpulan Sampah .
jww
19

Saya mencoba hal sederhana ini dan sepertinya berhasil.

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

Ini bekerja untuk saya.

Jika ini tidak berhasil, coba jalankan aplikasi Java Anda dengan sudo if di linux dan sebagai administrator di windows. Hanya untuk memastikan Java memiliki hak untuk mengubah properti file.

Kislay Sinha
sumber
1
Untuk masalah ini; umumnya orang berbicara tentang pengaturan referensi sebagai null atau memanggil system.gc (); tetapi bagi saya bahkan setelah server restart, file tidak terhapus !! Tapi file.setWritable (true); baru saja bekerja ..
Deepak Singhal
6

Sebelum mencoba menghapus / mengganti nama file apa pun, Anda harus memastikan bahwa semua pembaca atau penulis (misalnya: BufferedReader/ InputStreamReader/ BufferedWriter) ditutup dengan benar.

Ketika Anda mencoba membaca / menulis data Anda dari / ke sebuah file, file tersebut ditahan oleh proses dan tidak dilepaskan sampai eksekusi program selesai. Jika Anda ingin melakukan operasi hapus / ganti nama sebelum program berakhir, maka Anda harus menggunakan close()metode yang disertakan dengan java.io.*kelas.

Sai Manoj
sumber
1
Ini hanya berlaku di Windows. Pada setiap O / S nyata, Anda dapat menghapus dan mengganti nama file yang terbuka.
Archie
3

Seperti komentar Jon Skeet, Anda harus menutup file Anda di blok {...}, untuk memastikan bahwa file itu selalu tertutup. Dan, alih-alih menelan pengecualian dengan e.printStackTrace, cukup jangan menangkap dan menambahkan pengecualian ke tanda tangan metode. Jika Anda tidak bisa karena alasan apa pun, setidaknya lakukan ini:

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

Sekarang, pertanyaan nomor 2:

Bagaimana jika Anda melakukan ini:

...
to.close();
System.out.println("Please delete the file and press <enter> afterwards!");
System.in.read();
...

Apakah Anda dapat menghapus file tersebut?

Selain itu, file akan dibilas saat ditutup. Saya menggunakan IOUtils.closeQuietly (...), jadi saya menggunakan metode flush untuk memastikan bahwa konten file ada sebelum saya mencoba menutupnya (IOUtils.closeQuietly tidak memberikan pengecualian). Sesuatu seperti ini:

...
try {
    ...
    to.flush();
} catch(IOException ex) {
    throw new CannotProcessFileException("whatever", ex);
} finally {
    IOUtils.closeQuietly(to);
}

Jadi saya tahu bahwa isi file tersebut ada di sana. Karena biasanya penting bagi saya bahwa konten file ditulis dan bukan apakah file dapat ditutup atau tidak, tidak masalah apakah file tersebut ditutup atau tidak. Dalam kasus Anda, karena itu penting, saya akan merekomendasikan untuk menutup file sendiri dan memperlakukan setiap pengecualian sesuai.

Ravi Wallau
sumber
Saya mencoba hal pertama - menutup aliran keluaran di blok terakhir. Dan itu tidak berhasil. Apa yang terjadi jika saya mencoba membaca setelah itu adalah saya mendapat pesan yang mengatakan bahwa streaming ditutup. Saat beralih dan membaca file dengan FileInputReader, tidak ada hal "buruk" yang dijelaskan di atas yang terjadi.
Jenny Smith
2

Tidak ada alasan Anda tidak dapat menghapus file ini. Saya akan mencari tahu siapa yang memegang file ini. Di unix / linux, Anda dapat menggunakan utilitas lsof untuk memeriksa proses mana yang memiliki kunci pada file. Di windows, Anda dapat menggunakan proses explorer.

untuk lsof, sesederhana mengatakan:

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

untuk penjelajah proses Anda dapat menggunakan menu temukan dan masukkan nama file untuk menunjukkan kepada Anda pegangan yang akan mengarahkan Anda ke proses penguncian file.

berikut beberapa kode yang menurut saya perlu Anda lakukan:

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

Ini berfungsi dengan baik di OS X. Saya belum mengujinya di windows tetapi saya curiga itu harus berfungsi di Windows juga. Saya juga akan mengakui melihat beberapa perilaku tidak terduga pada penanganan file Windows wrt.

neesh
sumber
1
Untuk jendela, Anda dapat men-download Process Explorer bebas dari MS: technet.microsoft.com/en-us/sysinternals/bb896653.aspx
akarnokd
2

Jika Anda bekerja di Eclipse IDE, itu bisa berarti Anda belum menutup file di peluncuran aplikasi sebelumnya. Ketika saya mendapatkan pesan kesalahan yang sama saat mencoba menghapus file, itulah alasannya. Tampaknya, Eclipse IDE tidak menutup semua file setelah penghentian aplikasi.

Gangnus
sumber
1

Semoga ini bisa membantu. Saya menemukan masalah serupa di mana saya tidak dapat menghapus file saya setelah kode java saya membuat salinan konten ke folder lain. Setelah googling ekstensif, saya secara eksplisit mendeklarasikan setiap variabel terkait operasi file dan memanggil metode close () dari setiap objek operasi file, dan mengaturnya ke NULL. Kemudian, ada fungsi yang disebut System.gc (), yang akan membersihkan pemetaan i / o file (saya tidak yakin, saya hanya memberi tahu apa yang diberikan di situs web).

Ini contoh kode saya:

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();
    }
}
poh
sumber
1

jawabannya adalah ketika Anda memuat file, Anda perlu menerapkan metode "tutup", di baris kode mana pun, berfungsi untuk saya

pengguna3200921
sumber
0

Pernah ada masalah di ruby ​​di mana file di windows membutuhkan "fsync" untuk benar-benar dapat memutar dan membaca kembali file setelah menulis dan menutupnya. Mungkin ini adalah manifestasi serupa (dan jika demikian, saya pikir bug windows, sungguh).

rogerdpack
sumber
0

Tidak ada solusi yang tercantum di sini yang berfungsi dalam situasi saya. Solusi saya adalah menggunakan loop sementara, mencoba menghapus file, dengan batas 5 detik (dapat dikonfigurasi) untuk keamanan.

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

Menggunakan loop di atas bekerja tanpa harus melakukan pengumpulan sampah manual atau mengatur aliran ke null, dll.

etech
sumber
SO apa yang Anda gunakan? Jika Anda dapat memiliki akses ke STREAMS, tutup mereka, itu akan berhasil. Tetapi jika file tersebut "sudah" terkunci, maka Anda mengalami masalah.
marcolopes
1
-1 perulangan dalam kode Anda untuk menghapus file bukanlah ide yang bagus. Mengapa tidak hanya menggunakan opsi gc () yang berfungsi?
bharal
@bharal Perhatikan Saya menyatakan di atas bahwa tidak ada solusi lain (termasuk gc ()) yang berfungsi dalam kasus khusus saya. Meskipun saya setuju menggunakan loop sementara tidak ideal, ini mungkin solusi yang tepat dalam keadaan tertentu. Menambahkan pemeriksaan tambahan ke kode di atas, seperti variabel timeout atau max iterations, akan menjadi cara yang baik untuk membuat while loop lebih aman.
etech
@etech jika file Anda tidak pernah ada, Anda baru saja membuat loop tak terbatas.
bharal
0

Masalahnya mungkin file tersebut masih dilihat sebagai dibuka dan dikunci oleh program; atau mungkin itu adalah komponen dari program Anda yang telah dibuka, jadi Anda harus memastikan Anda menggunakan dispose()metode untuk menyelesaikan masalah itu. yaituJFrame frame; .... frame.dispose();

Winnifred
sumber
0

Anda harus menutup semua aliran atau menggunakan blok coba-dengan-sumber daya

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;
}
feech
sumber
0

jika file.delete () mengirimkan false maka dalam banyak kasus pegangan Bufferedreader Anda tidak akan ditutup. Cukup dekat dan tampaknya berfungsi untuk saya secara normal.

Piyush
sumber
0

Saya memiliki masalah yang sama di Windows. Saya biasa membaca file dalam skala demi baris

Source.fromFile(path).getLines()

Sekarang saya membacanya secara keseluruhan dengan

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

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

yang menutup file dengan benar setelah membaca dan sekarang

new File(path).delete

bekerja.

sbtpr
sumber
0

UNTUK Eclipse / NetBeans

Restart IDE Anda dan jalankan kode Anda lagi ini hanya trik bekerja untuk saya setelah perjuangan panjang satu jam.

Ini kode saya:

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

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

Keluaran:

Menghapus

Omore
sumber
0

Kasus sudut lain bahwa hal ini dapat terjadi: jika Anda membaca / menulis file JAR melalui a URLdan kemudian mencoba menghapus file yang sama dalam sesi JVM yang sama.

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!

Alasannya adalah bahwa logika penanganan file JAR internal Java, cenderung menyimpan JarFileentri cache :

// 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();
            }
        }
    }
}

Dan masing-masing JarFile(lebih tepatnya, ZipFilestruktur yang mendasarinya ) akan memegang pegangan ke file, langsung dari saat pembuatan hingga close()dipanggil:

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;

Ada penjelasan bagus tentang masalah NetBeans ini .


Ternyata ada dua cara untuk "memperbaiki" ini:

  • Anda dapat menonaktifkan cache file JAR - untuk saat ini URLConnection, atau untuk semua yang akan datang URLConnection(secara global) di sesi JVM saat ini:

    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);
    
  • [PERINGATAN HACK!] Anda dapat membersihkan JarFilecache secara manual setelah selesai. Manajer cache sun.net.www.protocol.jar.JarFileFactorybersifat pribadi-paket, tetapi beberapa keajaiban refleksi dapat menyelesaikan pekerjaan untuk Anda:

    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.
    

Harap diperhatikan: Semua ini didasarkan pada basis kode Java 8 ( 1.8.0_144); mereka mungkin tidak bekerja dengan versi lain / yang lebih baru.

Janaka Bandara
sumber