file.delete () trả về false mặc dù file.exists (), file.canRead (), file.canWrite (), file.canExecute () đều trả về true


90

Tôi đang cố xóa một tệp, sau khi viết một cái gì đó vào đó, với FileOutputStream. Đây là mã tôi sử dụng để viết:

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

Như đã thấy, tôi xóa và đóng luồng, nhưng khi tôi cố gắng xóa, file.delete()trả về false.

Tôi đã kiểm tra trước khi xóa để xem tập tin tồn tại, và: file.exists(), file.canRead(), file.canWrite(), file.canExecute()tất cả trở thành sự thật. Ngay sau khi gọi các phương thức này, tôi thử file.delete()và trả về false.

Có điều gì tôi đã làm sai không?


Tôi quên đề cập rằng không có ngoại lệ nào bị bắt.
Jenny Smith

Bạn có chắc chắn tệp không bị quá trình khác sử dụng không? Bạn đã khóa nó? Nó có hoạt động với deleteOnExit + thoát không?
instanceof me

Bạn đang chạy hệ điều hành nào? Bạn có thể xóa tệp theo cách thủ công không? Một cái gì đó có thể giữ một chốt mở đối với tệp.
akarnokd

Làm cách nào để tìm hiểu xem nó có được sử dụng bởi một quy trình khác không? Tôi không khóa nó. Tôi không thể sử dụng phương thức deleteOnExit vì tôi cần xóa tệp và tiếp tục ứng dụng của mình sau đó. Ý tưởng là tôi cố gắng tạo một thư mục tạm thời, được sử dụng để lưu trữ tệp từ một thư mục khác trong một phiên. Khi tôi nhấn nút mở trên ứng dụng của mình, điều đó có nghĩa là thư mục phải được làm trống, để nhường chỗ cho một số tệp khác. Nếu tôi bỏ qua phần khi tôi viết trong tệp đó, nó không sao, và tôi có thể xóa nó.
Jenny Smith

1
Bạn có thể vui lòng quấn xả & đóng vào một khối cuối cùng được không? Tôi biết bạn đã làm điều này và nó không hoạt động, nhưng nó sẽ thêm rõ ràng cho câu hỏi của bạn.
bharal

Câu trả lời:


99

Một lỗi khác trong Java. Tôi hiếm khi tìm thấy chúng, chỉ lần thứ hai trong sự nghiệp 10 năm của tôi. Đây là giải pháp của tôi, như những người khác đã đề cập. Tôi đã sử dụng nether System.gc(). Nhưng ở đây, trong trường hợp của tôi, nó hoàn toàn quan trọng. Kỳ dị? ĐÚNG!

finally
{
    try
    {
        in.close();
        in = null;
        out.flush();
        out.close();
        out = null;
        System.gc();
    }
    catch (IOException e)
    {
        logger.error(e.getMessage());
        e.printStackTrace();
    }
}

7
Mặc dù tôi không mở nó bằng bất kỳ loại luồng nào (chỉ thực hiện a new File(path)), tôi đã gặp phải vấn đề tương tự và thêm vào System.gc()trước khi delete()nó hoạt động!
ixM

1
Lỗi khác là gì?
bogdan.mustiata

không thích tôi. Tệp tôi không thể xóa là tệp ZIP của tệpFromURL đã tải xuống. Điều tốt đẹp là tệp đã tải xuống sẽ bị xóa. Bất kỳ ý tưởng?
Andrea_86

Đã làm cho tôi. Và @andreadi Tôi chưa bao giờ sử dụng FileChannel.map trong mã của mình. Có vẻ như lỗi này đã đóng lại là không thể giải quyết được, điều này khiến tôi nghi ngờ vì System.gcthủ thuật này hoạt động lần nào cũng có hiệu quả.
Adam Burley

Gc đơn () có thể không giúp đượcSystem.gc(); result = file.delete(); if (!result){ Thread.sleep(100); System.gc(); result = file.delete(); }
ghi chú-jj

48

Đó là một thủ thuật khá kỳ lạ. Vấn đề là khi trước đó tôi đã đọc nội dung của tệp, tôi đã sử dụng BufferedReader. Sau khi đọc xong, tôi đóng bộ đệm.

Trong khi đó tôi đã chuyển đổi và bây giờ tôi đang đọc nội dung bằng cách sử dụng FileInputStream. Ngoài ra sau khi đọc xong tôi đóng luồng. Và bây giờ nó đang hoạt động.

Vấn đề là tôi không có lời giải thích cho điều này.

Tôi không biết BufferedReaderFileOutputStreamkhông tương thích.


4
Tôi cho rằng đó là một lỗi sau đó: java.sun.com/javase/6/docs/api/java/io/… Nó nói trong đó rằng phương pháp sẽ đóng luồng và mọi tài nguyên liên quan đến nó. Tôi thường muốn đóng tất cả các luồng theo trình tự đảo ngược (những luồng cuối cùng được mở là những luồng đầu tiên bị đóng) bằng cách sử dụng IOUtils.closeQuietly, nhưng nó có xu hướng quá mức cần thiết.
Ravi Wallau

2
Tôi đang gặp vấn đề chính xác này và tôi nghĩ rằng tôi đã phát điên. Cảm ơn vì giải pháp!
Electron_Ahoy

14
Đó là năm 2011 với JDK 7 và sự cố vẫn chưa được khắc phục. Tôi rất vui vì tôi thấy chủ đề này - Tôi chỉ đơn giản là không thể tìm ra những gì đã sai ...
David

Tôi đã gặp sự cố rất khó chịu với một tệp mà tôi đã đọc và đóng lại rồi cố gắng xóa. Không có gì được đề cập ở đây giúp ích. Sau đó, sau một giờ tôi phát hiện ra đó chỉ là quyền tập tin, kể từ khi tập tin đã được tạo ra bởi một người dùng :-D
runholen

Chỉ là một vết đâm trong bóng tối ... Bạn có thể gọi finalize()để đảm bảo dọn dẹp? Hoặc có lẽ được thiết lập to = null? Xem Buộc hoàn thiện và thu gom rác .
jww

19

Tôi đã thử điều đơn giản này và nó dường như đang hoạt động.

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

Nó làm việc cho tôi.

Nếu điều này không hiệu quả, hãy thử chạy ứng dụng Java của bạn với sudo nếu trên linux và với tư cách quản trị viên khi trên windows. Chỉ để đảm bảo Java có quyền thay đổi các thuộc tính tệp.


1
Đối với vấn đề này; nói chung mọi người nói về việc đặt tham chiếu là null hoặc gọi system.gc (); nhưng đối với tôi ngay cả sau khi các tệp khởi động lại máy chủ vẫn không bị xóa !! Nhưng file.setWects (true); vừa mới làm việc ..
Deepak Singhal

6

Trước khi cố gắng xóa / đổi tên bất kỳ tệp nào, bạn phải đảm bảo rằng tất cả trình đọc hoặc người viết (ví dụ: BufferedReader/ InputStreamReader/ BufferedWriter) đã được đóng đúng cách.

Khi bạn cố gắng đọc / ghi dữ liệu của mình từ / vào một tệp, tệp sẽ được quy trình giữ lại và không được phát hành cho đến khi quá trình thực thi chương trình hoàn tất. Nếu bạn muốn thực hiện các thao tác xóa / đổi tên trước khi chương trình kết thúc, thì bạn phải sử dụng close()phương thức đi kèm với các java.io.*lớp.


1
Điều này chỉ đúng trên Windows. Trên bất kỳ O / S thực nào, bạn có thể xóa và đổi tên các tệp đang mở.
Archie

3

Như Jon Skeet đã nhận xét, bạn nên đóng tệp của mình trong khối {...} cuối cùng, để đảm bảo rằng tệp luôn được đóng. Và, thay vì nuốt các ngoại lệ với e.printStackTrace, chỉ cần không bắt và thêm ngoại lệ vào chữ ký phương thức. Nếu bạn không thể vì bất kỳ lý do gì, ít nhất hãy làm điều này:

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

Bây giờ, câu hỏi số 2:

Điều gì xảy ra nếu bạn làm điều này:

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

Bạn có thể xóa tệp không?

Ngoài ra, các tệp được xóa khi chúng được đóng. Tôi sử dụng IOUtils.closeQuietly (...), vì vậy tôi sử dụng phương pháp xả để đảm bảo rằng nội dung của tệp ở đó trước khi tôi cố gắng đóng nó (IOUtils.closeQuietly không ném ngoại lệ). Một cái gì đó như thế này:

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

Vì vậy, tôi biết rằng nội dung của tệp nằm trong đó. Đối với tôi, điều quan trọng là nội dung của tệp được ghi chứ không phải là tệp có thể được đóng hay không, nó thực sự không quan trọng nếu tệp đã được đóng hay chưa. Trong trường hợp của bạn, vì nó quan trọng, tôi khuyên bạn nên tự đóng tệp và xử lý mọi ngoại lệ theo.


Tôi đã thử điều đầu tiên - đóng luồng đầu ra trong khối cuối cùng. Và nó đã không hoạt động. Điều gì đã xảy ra nếu tôi cố đọc sau đó thì tôi nhận được thông báo rằng luồng đã bị đóng. Khi chuyển đổi và đọc tệp bằng FileInputReader, không có điều gì "tồi tệ" được mô tả ở trên xảy ra.
Jenny Smith

2

Không có lý do gì bạn không thể xóa tệp này. Tôi sẽ tìm xem ai có tài liệu này. Trong unix / linux, bạn có thể sử dụng tiện ích lsof để kiểm tra quá trình nào có khóa trên tệp. Trong cửa sổ, bạn có thể sử dụng trình khám phá quy trình.

đối với lsof, nó đơn giản như nói:

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

đối với trình khám phá quy trình, bạn có thể sử dụng menu tìm và nhập tên tệp để hiển thị cho bạn xử lý sẽ trỏ bạn đến quá trình khóa tệp.

đây là một số mã thực hiện những gì tôi nghĩ bạn cần làm:

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

Nó hoạt động tốt trên OS X. Tôi chưa thử nghiệm nó trên windows nhưng tôi nghi ngờ nó cũng hoạt động trên Windows. Tôi cũng thừa nhận đã thấy một số hành vi không mong muốn khi xử lý tệp wrt của Windows.


1
Đối với windows, bạn có thể tải xuống Process Explorer miễn phí từ MS: technet.microsoft.com/en-us/sysinternals/bb896653.aspx
akarnokd

2

Nếu bạn đang làm việc trong Eclipse IDE, điều đó có thể có nghĩa là bạn chưa đóng tệp trong lần khởi chạy ứng dụng trước. Khi tôi gặp phải thông báo lỗi tương tự khi cố gắng xóa tệp, đó là lý do. Có vẻ như, Eclipse IDE không đóng tất cả các tệp sau khi kết thúc ứng dụng.


1

Hy vọng rằng điều này sẽ giúp ích. Tôi đã gặp sự cố tương tự, trong đó tôi không thể xóa tệp của mình sau khi mã java của tôi tạo bản sao nội dung vào thư mục khác. Sau khi googling mở rộng, tôi đã khai báo rõ ràng mọi biến liên quan đến hoạt động tệp và gọi phương thức close () của từng đối tượng hoạt động tệp và đặt chúng thành NULL. Sau đó, có một hàm gọi là System.gc (), sẽ xóa tệp ánh xạ i / o (tôi không chắc, tôi chỉ nói những gì được cung cấp trên các trang web).

Đây là mã ví dụ của tôi:

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

câu trả lời là khi bạn tải tệp, bạn cần áp dụng phương pháp "đóng", trong bất kỳ dòng mã nào, phù hợp với tôi


0

Đã từng xảy ra sự cố trong ruby ​​khi các tệp trong windows cần "fsync" để thực sự có thể quay và đọc lại tệp sau khi ghi và đóng tệp. Có thể đây là một biểu hiện tương tự (và nếu vậy, tôi nghĩ là một lỗi windows, thực sự).


0

Không có giải pháp nào được liệt kê ở đây hiệu quả trong tình huống của tôi. Giải pháp của tôi là sử dụng vòng lặp while, cố gắng xóa tệp, với giới hạn 5 giây (có thể định cấu hình) để đảm bảo an toàn.

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

Sử dụng vòng lặp trên đã hoạt động mà không cần phải thực hiện bất kỳ thao tác thu gom rác thủ công nào hoặc đặt luồng thành null, v.v.


SO để bạn sử dụng? Nếu bạn có thể có quyền truy cập vào STREAMS, hãy đóng chúng lại, nó sẽ hoạt động. Nhưng nếu tệp "đã" bị khóa, thì bạn có vấn đề.
marcolopes

1
-1 vòng lặp trong mã của bạn để xóa một tệp không phải là một ý tưởng tuyệt vời. Tại sao không chỉ sử dụng tùy chọn gc () hoạt động?
bharal

@bharal Thông báo Tôi đã nêu ở trên rằng không có giải pháp nào khác (bao gồm gc ()) hoạt động trong trường hợp cụ thể của tôi. Mặc dù tôi đồng ý sử dụng vòng lặp while không phải là lý tưởng, nhưng nó có thể là một giải pháp thích hợp trong một số trường hợp nhất định. Thêm một kiểm tra bổ sung vào mã ở trên, chẳng hạn như biến thời gian chờ hoặc biến số lần lặp tối đa, sẽ là một cách tốt để làm cho vòng lặp while an toàn hơn.
etech

@etech nếu tệp của bạn chưa từng tồn tại thì bạn vừa tạo một vòng lặp vô hạn.
bharal

0

Vấn đề có thể là tệp vẫn được xem là được mở và khóa bởi một chương trình; hoặc có thể nó là một thành phần từ chương trình của bạn mà nó đã được mở, vì vậy bạn phải đảm bảo rằng bạn sử dụng dispose()phương pháp để giải quyết vấn đề đó. I EJFrame frame; .... frame.dispose();


0

Bạn phải đóng tất cả các luồng hoặc sử dụng khối thử với tài nguyên

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

nếu file.delete () đang gửi sai thì trong hầu hết các trường hợp, xử lý Bộ đệm của bạn sẽ không bị đóng. Chỉ cần đóng và nó dường như hoạt động đối với tôi bình thường.


0

Tôi đã gặp vấn đề tương tự trên Windows. Tôi đã từng đọc tệp theo tỉ lệ từng dòng với

Source.fromFile(path).getLines()

Bây giờ tôi đã đọc toàn bộ với

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

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

đóng tệp đúng cách sau khi đọc và bây giờ

new File(path).delete

làm.


0

ĐỐI VỚI Eclipse / NetBeans

Khởi động lại IDE của bạn và chạy lại mã của bạn, đây chỉ là mẹo nhỏ đối với tôi sau một giờ đấu tranh.

Đây là mã của tôi:

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

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

Đầu ra:

Xóa bỏ


0

Một trường hợp góc khác mà điều này có thể xảy ra: nếu bạn đọc / ghi một tệp JAR thông qua URLvà sau đó cố gắng xóa cùng một tệp trong cùng một phiên 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!

Lý do là logic xử lý tệp JAR bên trong của Java, có xu hướng lưu JarFilecác mục vào bộ nhớ 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();
            }
        }
    }
}

Và mỗi JarFile(đúng hơn là ZipFilecấu trúc bên dưới ) sẽ giữ một phần xử lý đối với tệp, ngay từ khi xây dựng cho đến khi close()được gọi:

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;

Có một lời giải thích tốt về vấn đề NetBeans này .


Rõ ràng có hai cách để "sửa chữa" điều này:

  • Bạn có thể tắt bộ nhớ đệm tệp JAR - cho hiện tại URLConnectionhoặc cho tất cả các tương lai URLConnection(trên toàn cầu) trong phiên JVM hiện tại:

    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);
  • [CẢNH BÁO HACK!] Bạn có thể xóa thủ công JarFilekhỏi bộ nhớ cache khi bạn hoàn thành việc này. Trình quản lý bộ nhớ cache sun.net.www.protocol.jar.JarFileFactorylà gói riêng tư, nhưng một số phép thuật phản chiếu có thể hoàn thành công việc cho bạn:

    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.

Xin lưu ý: Tất cả điều này dựa trên Java 8 codebase ( 1.8.0_144); chúng có thể không hoạt động với các phiên bản khác / mới hơn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.