Làm cách nào để tạo một thư mục / thư mục tạm thời trong Java?


364

Có một cách tiêu chuẩn và đáng tin cậy để tạo một thư mục tạm thời bên trong một ứng dụng Java không? Có một mục trong cơ sở dữ liệu vấn đề của Java , có một chút mã trong các bình luận, nhưng tôi tự hỏi liệu có một giải pháp tiêu chuẩn nào được tìm thấy trong một trong các thư viện thông thường (Apache Commons, v.v.) không?

Câu trả lời:


390

Nếu bạn đang sử dụng JDK 7, hãy sử dụng lớp Files.createTempDirectory mới để tạo thư mục tạm thời.

Path tempDirWithPrefix = Files.createTempDirectory(prefix);

Trước JDK 7, điều này sẽ làm điều đó:

public static File createTempDirectory()
    throws IOException
{
    final File temp;

    temp = File.createTempFile("temp", Long.toString(System.nanoTime()));

    if(!(temp.delete()))
    {
        throw new IOException("Could not delete temp file: " + temp.getAbsolutePath());
    }

    if(!(temp.mkdir()))
    {
        throw new IOException("Could not create temp directory: " + temp.getAbsolutePath());
    }

    return (temp);
}

Bạn có thể tạo ra ngoại lệ tốt hơn (phân lớp IOException) nếu bạn muốn.


12
Điều này nguy hiểm. Java được biết là không xóa các tệp ngay lập tức, vì vậy đôi khi mkdir có thể thất bại
Demiurg

4
@Demiurg Trường hợp duy nhất của một tệp không bị xóa ngay lập tức là trên Windows khi tệp đã được mở (ví dụ, nó có thể được mở bởi trình quét vi-rút). Bạn có tài liệu nào khác để trình bày không (tôi tò mò về những thứ như thế :-)? Nếu nó xảy ra thường xuyên thì mã trên sẽ không hoạt động, nếu hiếm khi xảy ra cuộc gọi đến mã trên cho đến khi xóa xảy ra (hoặc một số lần thử tối đa đạt được) sẽ hoạt động.
TofuBeer

6
@Demiurg Java được biết là không xóa các tệp ngay lập tức. Điều đó đúng, ngay cả khi bạn không mở nó. Vì vậy, một cách an toàn hơn là temp.delete(); temp = new File(temp.getPath + ".d"); temp.mkdir(); ..., temp.delete();.
Xiè Jìléi

102
Mã này có một điều kiện chạy đua giữa delete()mkdir(): Một quy trình độc hại có thể tạo thư mục đích trong thời gian đó (lấy tên của tệp được tạo gần đây). Xem Files.createTempDir()để thay thế.
Joachim Sauer

11
Tôi thích cái! để nổi bật, quá dễ dàng để bỏ lỡ nó. Tôi đã đọc rất nhiều mã được viết bởi các sinh viên ... nếu (! I) đủ phổ biến để gây phiền nhiễu :-)
TofuBeer

182

Thư viện Google Guava có rất nhiều tiện ích hữu ích. Một lưu ý ở đây là lớp Files . Nó có một loạt các phương thức hữu ích bao gồm:

File myTempDir = Files.createTempDir();

Điều này thực hiện chính xác những gì bạn yêu cầu trong một dòng. Nếu bạn đọc tài liệu ở đây, bạn sẽ thấy rằng sự thích ứng được đề xuất File.createTempFile("install", "dir")thường đưa ra các lỗ hổng bảo mật.


Tôi tự hỏi những lỗ hổng mà bạn đề cập đến. Cách tiếp cận này dường như không tạo ra một điều kiện cuộc đua vì File.mkdir () bị thay thế thất bại nếu thư mục đó đã tồn tại (được tạo bởi kẻ tấn công). Tôi cũng không nghĩ rằng cuộc gọi này sẽ đi theo một liên kết độc hại. Bạn có thể làm rõ những gì bạn có ý nghĩa?
abb

3
@abb: Tôi không biết chi tiết về điều kiện cuộc đua được đề cập trong tài liệu về ổi. Tôi nghi ngờ rằng các tài liệu là chính xác cho rằng nó đặc biệt gọi ra vấn đề.
Spina

1
@abb Bạn nói đúng. Miễn là sự trở lại của mkdir () được kiểm tra thì nó sẽ được bảo mật. Mã Spina trỏ đến sử dụng phương thức mkdir () này. grepcode.com/file/repo1.maven.org/maven2/com.google.guava/guava/ Lần . Đây chỉ là một vấn đề tiềm năng trên các hệ thống Unix khi sử dụng thư mục / tmp vì nó có kích hoạt bit dính.
Sarel Botha

@SarelBotha cảm ơn bạn đã điền vào chỗ trống ở đây. Tôi đã tự hỏi ngớ ngẩn về điều này trong một thời gian khá lâu.
Spina

168

Nếu bạn cần một thư mục tạm thời để thử nghiệm và bạn đang sử dụng jUnit, @Rulecùng với việc TemporaryFoldergiải quyết vấn đề của bạn:

@Rule
public TemporaryFolder folder = new TemporaryFolder();

Từ tài liệu :

Quy tắc tạm thời cho phép tạo các tệp và thư mục được đảm bảo sẽ bị xóa khi phương thức kiểm tra kết thúc (cho dù nó vượt qua hay thất bại)


Cập nhật:

Nếu bạn đang sử dụng JUnit Jupiter (phiên bản 5.1.1 trở lên), bạn có tùy chọn sử dụng JUnit Pioneer là Gói mở rộng JUnit 5.

Sao chép từ tài liệu dự án :

Ví dụ, bài kiểm tra sau đăng ký phần mở rộng cho một phương thức kiểm tra duy nhất, tạo và ghi một tệp vào thư mục tạm thời và kiểm tra nội dung của nó.

@Test
@ExtendWith(TempDirectory.class)
void test(@TempDir Path tempDir) {
    Path file = tempDir.resolve("test.txt");
    writeFile(file);
    assertExpectedFileContent(file);
}

Thông tin thêm trong JavaDocJavaDoc của TempDirectory

Học sinh lớp

dependencies {
    testImplementation 'org.junit-pioneer:junit-pioneer:0.1.2'
}

Maven:

<dependency>
   <groupId>org.junit-pioneer</groupId>
   <artifactId>junit-pioneer</artifactId>
   <version>0.1.2</version>
   <scope>test</scope>
</dependency>

Cập nhật 2:

Các @TempDir chú thích đã được thêm vào 5.4.0 phát hành JUnit Jupiter như một tính năng thử nghiệm. Ví dụ được sao chép từ Hướng dẫn sử dụng JUnit 5 :

@Test
void writeItemsToFile(@TempDir Path tempDir) throws IOException {
    Path file = tempDir.resolve("test.txt");

    new ListWriter(file).write("a", "b", "c");

    assertEquals(singletonList("a,b,c"), Files.readAllLines(file));
}

8
Có sẵn kể từ JUnit 4.7
Eduard Wirch

Không hoạt động trong JUnit 4.8.2 trên Windows 7! (Vấn đề về quyền)
ngoại lệ

2
@CraigRinger: Tại sao lại không dựa vào điều này?
Adam Parkin

2
@AdamParkin Thành thật mà nói, tôi không còn nhớ nữa. Giải thích thất bại!
Craig Ringer

1
Lợi ích chính của phương pháp này là thư mục được quản lý bởi JUnit (được tạo trước khi kiểm tra và bị xóa đệ quy sau khi kiểm tra). Và nó hoạt động. Nếu bạn nhận được "temp dir chưa được tạo", có thể là do bạn quên @Rule hoặc trường không công khai.
Bogdan Calmac

42

Mã được viết rõ ràng để giải quyết vấn đề này phải chịu các điều kiện chủng tộc, bao gồm một số câu trả lời ở đây. Về mặt lịch sử, bạn có thể suy nghĩ cẩn thận về các điều kiện chủng tộc và tự viết hoặc bạn có thể sử dụng thư viện của bên thứ ba như Guava của Google (như câu trả lời của Spina đã đề xuất.) Hoặc bạn có thể viết mã lỗi.

Nhưng kể từ JDK 7, có một tin tốt! Bản thân thư viện chuẩn Java hiện cung cấp một giải pháp hoạt động đúng (không phù hợp) cho vấn đề này. Bạn muốn java.nio.file.Files # createTempDirectory () . Từ tài liệu :

public static Path createTempDirectory(Path dir,
                       String prefix,
                       FileAttribute<?>... attrs)
                                throws IOException

Tạo một thư mục mới trong thư mục được chỉ định, sử dụng tiền tố đã cho để tạo tên của nó. Đường dẫn kết quả được liên kết với cùng Hệ thống tệp với thư mục đã cho.

Các chi tiết về cách tên của thư mục được xây dựng phụ thuộc vào việc triển khai và do đó không được chỉ định. Trường hợp có thể tiền tố được sử dụng để xây dựng tên ứng cử viên.

Điều này giải quyết hiệu quả báo cáo lỗi cổ xưa đáng xấu hổ trong trình theo dõi lỗi Mặt trời yêu cầu chức năng như vậy.


35

Đây là mã nguồn của Files.createTempDir () của thư viện Guava. Nó không phức tạp như bạn nghĩ:

public static File createTempDir() {
  File baseDir = new File(System.getProperty("java.io.tmpdir"));
  String baseName = System.currentTimeMillis() + "-";

  for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
    File tempDir = new File(baseDir, baseName + counter);
    if (tempDir.mkdir()) {
      return tempDir;
    }
  }
  throw new IllegalStateException("Failed to create directory within "
      + TEMP_DIR_ATTEMPTS + " attempts (tried "
      + baseName + "0 to " + baseName + (TEMP_DIR_ATTEMPTS - 1) + ')');
}

Theo mặc định:

private static final int TEMP_DIR_ATTEMPTS = 10000;

Xem tại đây


28

Không sử dụng deleteOnExit()ngay cả khi bạn xóa nó một cách rõ ràng sau đó.

Google 'xóaonexit là xấu xa' để biết thêm thông tin, nhưng ý chính của vấn đề là:

  1. deleteOnExit() chỉ xóa để tắt JVM bình thường, không bị sập hoặc làm chết quá trình JVM.

  2. deleteOnExit() chỉ xóa khi tắt JVM - không tốt cho các quy trình máy chủ chạy dài vì:

  3. Tội ác nhất trong tất cả - deleteOnExit()tiêu thụ bộ nhớ cho mỗi mục nhập tệp tạm thời. Nếu quy trình của bạn đang chạy trong nhiều tháng hoặc tạo ra nhiều tệp tạm thời trong một thời gian ngắn, bạn sẽ tiêu thụ bộ nhớ và không bao giờ giải phóng nó cho đến khi JVM tắt.


1
Chúng tôi có một JVM nơi các tệp jar và jar nhận các tệp ẩn bên dưới được tạo bởi JVM và thông tin bổ sung này mất khá nhiều thời gian để xóa. Khi thực hiện các bản hợp đồng nóng trên các thùng chứa web phát nổ WARs, JVM có thể mất vài phút để dọn dẹp sau khi hoàn thành nhưng trước khi thoát khi chạy trong vài giờ.
Thorbjørn Ravn Andersen

20

Kể từ Java 1.7 createTempDirectory(prefix, attrs)createTempDirectory(dir, prefix, attrs)được bao gồm trongjava.nio.file.Files

Thí dụ: File tempDir = Files.createTempDirectory("foobar").toFile();


14

Đây là những gì tôi quyết định làm cho mã của riêng tôi:

/**
 * Create a new temporary directory. Use something like
 * {@link #recursiveDelete(File)} to clean this directory up since it isn't
 * deleted automatically
 * @return  the new directory
 * @throws IOException if there is an error creating the temporary directory
 */
public static File createTempDir() throws IOException
{
    final File sysTempDir = new File(System.getProperty("java.io.tmpdir"));
    File newTempDir;
    final int maxAttempts = 9;
    int attemptCount = 0;
    do
    {
        attemptCount++;
        if(attemptCount > maxAttempts)
        {
            throw new IOException(
                    "The highly improbable has occurred! Failed to " +
                    "create a unique temporary directory after " +
                    maxAttempts + " attempts.");
        }
        String dirName = UUID.randomUUID().toString();
        newTempDir = new File(sysTempDir, dirName);
    } while(newTempDir.exists());

    if(newTempDir.mkdirs())
    {
        return newTempDir;
    }
    else
    {
        throw new IOException(
                "Failed to create temp dir named " +
                newTempDir.getAbsolutePath());
    }
}

/**
 * Recursively delete file or directory
 * @param fileOrDir
 *          the file or dir to delete
 * @return
 *          true iff all files are successfully deleted
 */
public static boolean recursiveDelete(File fileOrDir)
{
    if(fileOrDir.isDirectory())
    {
        // recursively delete contents
        for(File innerFile: fileOrDir.listFiles())
        {
            if(!FileUtilities.recursiveDelete(innerFile))
            {
                return false;
            }
        }
    }

    return fileOrDir.delete();
}

2
Điều này là không an toàn. Xem bình luận của Joachim Sauer trong tùy chọn đầu tiên (không an toàn). Cách thích hợp để kiểm tra sự tồn tại của một tập tin hoặc thư mục và hơn là lấy tên tệp, về cơ bản, là bằng cách tạo tệp hoặc thư mục.
zbyszek

1
@zbyszek javadocs nói "UUID được tạo bằng cách sử dụng trình tạo số ngẫu nhiên giả mã hóa mạnh." Cho rằng làm thế nào để một quá trình độc hại tạo ra một thư mục có cùng tên giữa tồn tại () và mkdirs (). Trong thực tế nhìn vào điều này bây giờ tôi nghĩ rằng bài kiểm tra tồn tại () của tôi có thể hơi ngớ ngẩn.
Keith

Keith: UUID an toàn hay không không quan trọng trong trường hợp này. Nó là đủ cho các thông tin về tên mà bạn truy vấn bằng cách nào đó "rò rỉ". Ví dụ: giả sử rằng tệp được tạo nằm trong hệ thống tệp NFS và kẻ tấn công có thể lắng nghe (thụ động) các gói. Hoặc trạng thái máy phát ngẫu nhiên đã bị rò rỉ. Trong nhận xét của tôi, tôi đã nói rằng giải pháp của bạn không an toàn như câu trả lời được chấp nhận, nhưng điều này không công bằng: câu trả lời là không quan trọng để đánh bại với inotify, và điều này khó đánh bại hơn nhiều. Tuy nhiên, trong một số tình huống chắc chắn là có thể.
zbyszek

2
Tôi đã có cùng suy nghĩ và thực hiện một giải pháp sử dụng bit UUID ngẫu nhiên như thế này. Không có kiểm tra tồn tại, chỉ cần một nỗ lực để tạo - RNG mạnh được sử dụng bởi phương thức RandomUUID, đảm bảo không có va chạm (có thể được sử dụng để tạo khóa chính trong các bảng DB, tự thực hiện và không bao giờ biết xung đột), nên rất tự tin. Nếu ai đó không chắc chắn, hãy xem stackoverflow.com/questions/2513573/ từ
brabster

Nếu bạn nhìn vào triển khai của Java, họ chỉ tạo các tên ngẫu nhiên cho đến khi không có xung đột. Nỗ lực tối đa của họ là vô hạn. Vì vậy, nếu ai đó độc hại cứ đoán tên tệp / thư mục của bạn, nó sẽ lặp lại mãi mãi. Dưới đây là một liên kết đến các nguồn: hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/9fb81d7a2f16/src/share/... Tôi đã suy nghĩ rằng nó bằng cách nào đó có thể khóa các hệ thống tập tin để nó nguyên tử có thể tạo ra một tên duy nhất và tạo thư mục, nhưng tôi đoán nó không làm điều đó theo mã nguồn.
dosentmatter

5

Chà, "createdTempFile" thực sự tạo tập tin. Vậy tại sao không xóa nó trước, và sau đó thực hiện mkdir trên nó?


1
Bạn phải luôn luôn kiểm tra giá trị trả về cho mkdir (). Nếu đó là sai thì có nghĩa là thư mục đã tồn tại. Điều này có thể gây ra vấn đề bảo mật, vì vậy hãy xem xét liệu điều này có gây ra lỗi trong ứng dụng của bạn hay không.
Sarel Botha

1
Xem ghi chú về điều kiện cuộc đua trong câu trả lời khác.
Volker Stolz

Điều này tôi thích, chặn cuộc đua
Martin Wickman

4

Mã này sẽ hoạt động hợp lý tốt:

public static File createTempDir() {
    final String baseTempPath = System.getProperty("java.io.tmpdir");

    Random rand = new Random();
    int randomInt = 1 + rand.nextInt();

    File tempDir = new File(baseTempPath + File.separator + "tempDir" + randomInt);
    if (tempDir.exists() == false) {
        tempDir.mkdir();
    }

    tempDir.deleteOnExit();

    return tempDir;
}

3
Điều gì xảy ra nếu thư mục đã tồn tại và bạn không có quyền truy cập đọc / ghi vào nó hoặc nếu đó là một tệp thông thường thì sao? Bạn cũng có một điều kiện cuộc đua ở đó.
Jeremy Huiskamp

2
Ngoài ra, xóaOnExit sẽ không xóa các thư mục không trống.
Trenton

3

Như đã thảo luận trong RFE này và các bình luận của nó, bạn có thể gọi tempDir.delete()trước. Hoặc bạn có thể sử dụng System.getProperty("java.io.tmpdir")và tạo một thư mục ở đó. Dù bằng cách nào, bạn nên nhớ gọi tempDir.deleteOnExit(), hoặc tập tin sẽ không bị xóa sau khi bạn hoàn thành.


Không phải tài sản này được gọi là "java.io.tmpdir", không phải "... temp"? Xem java.sun.com/j2se/1.4.2/docs/api/java/io/File.html
Andrew Swan

Khá là vậy. Tôi nên đã xác minh trước khi lặp lại những gì tôi đọc.
Michael Myers

Java.io.tmpdir được chia sẻ, do đó bạn cần phải thực hiện tất cả các voodoo thông thường để tránh giẫm lên ngón chân của ai đó.
Thorbjørn Ravn Andersen

3

Chỉ cần hoàn thành, đây là mã từ thư viện ổi của google. Nó không phải là mã của tôi, nhưng tôi nghĩ nó có giá trị để hiển thị nó ở đây trong chuỗi này.

  /** Maximum loop count when creating temp directories. */
  private static final int TEMP_DIR_ATTEMPTS = 10000;

  /**
   * Atomically creates a new directory somewhere beneath the system's temporary directory (as
   * defined by the {@code java.io.tmpdir} system property), and returns its name.
   *
   * <p>Use this method instead of {@link File#createTempFile(String, String)} when you wish to
   * create a directory, not a regular file. A common pitfall is to call {@code createTempFile},
   * delete the file and create a directory in its place, but this leads a race condition which can
   * be exploited to create security vulnerabilities, especially when executable files are to be
   * written into the directory.
   *
   * <p>This method assumes that the temporary volume is writable, has free inodes and free blocks,
   * and that it will not be called thousands of times per second.
   *
   * @return the newly-created directory
   * @throws IllegalStateException if the directory could not be created
   */
  public static File createTempDir() {
    File baseDir = new File(System.getProperty("java.io.tmpdir"));
    String baseName = System.currentTimeMillis() + "-";

    for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
      File tempDir = new File(baseDir, baseName + counter);
      if (tempDir.mkdir()) {
        return tempDir;
      }
    }
    throw new IllegalStateException(
        "Failed to create directory within "
            + TEMP_DIR_ATTEMPTS
            + " attempts (tried "
            + baseName
            + "0 to "
            + baseName
            + (TEMP_DIR_ATTEMPTS - 1)
            + ')');
  }

2

Tôi có cùng một vấn đề vì vậy đây chỉ là một câu trả lời khác cho những người quan tâm và nó tương tự như một trong những điều trên:

public static final String tempDir = System.getProperty("java.io.tmpdir")+"tmp"+System.nanoTime();
static {
    File f = new File(tempDir);
    if(!f.exists())
        f.mkdir();
}

Và đối với ứng dụng của mình, tôi đã quyết định rằng thêm vào một tùy chọn để xóa temp khi thoát để tôi thêm vào một móc tắt:

Runtime.getRuntime().addShutdownHook(new Thread() {
        @Override
        public void run() {
            //stackless deletion
            String root = MainWindow.tempDir;
            Stack<String> dirStack = new Stack<String>();
            dirStack.push(root);
            while(!dirStack.empty()) {
                String dir = dirStack.pop();
                File f = new File(dir);
                if(f.listFiles().length==0)
                    f.delete();
                else {
                    dirStack.push(dir);
                    for(File ff: f.listFiles()) {
                        if(ff.isFile())
                            ff.delete();
                        else if(ff.isDirectory())
                            dirStack.push(ff.getPath());
                    }
                }
            }
        }
    });

Phương thức xóa tất cả các thư mục con và tệp trước khi xóa temp , mà không sử dụng lệnh gọi (điều này là hoàn toàn tùy chọn và bạn có thể thực hiện với đệ quy tại thời điểm này), nhưng tôi muốn ở bên an toàn.


2

Như bạn có thể thấy trong các câu trả lời khác, không có cách tiếp cận tiêu chuẩn nào phát sinh. Do đó bạn đã đề cập đến Apache Commons, tôi đề xuất cách tiếp cận sau bằng FileUtils từ Apache Commons IO :

/**
 * Creates a temporary subdirectory in the standard temporary directory.
 * This will be automatically deleted upon exit.
 * 
 * @param prefix
 *            the prefix used to create the directory, completed by a
 *            current timestamp. Use for instance your application's name
 * @return the directory
 */
public static File createTempDirectory(String prefix) {

    final File tmp = new File(FileUtils.getTempDirectory().getAbsolutePath()
            + "/" + prefix + System.currentTimeMillis());
    tmp.mkdir();
    Runtime.getRuntime().addShutdownHook(new Thread() {

        @Override
        public void run() {

            try {
                FileUtils.deleteDirectory(tmp);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    });
    return tmp;

}

Điều này được ưa thích vì apache bắt đầu thư viện gần nhất với "tiêu chuẩn" được hỏi và hoạt động với cả JDK 7 và các phiên bản cũ hơn. Điều này cũng trả về một cá thể Tệp "cũ" (dựa trên luồng) và không phải là một cá thể Đường dẫn "mới" (dựa trên bộ đệm và sẽ là kết quả của phương thức getT tạm thời () của JDK7) -> Do đó, nó trả về những gì hầu hết mọi người cần khi họ muốn tạo một thư mục tạm thời.


1

Tôi thích nhiều nỗ lực tạo ra một tên duy nhất nhưng ngay cả giải pháp này cũng không loại trừ điều kiện chủng tộc. Một quy trình khác có thể trượt vào sau khi thử nghiệm exists()if(newTempDir.mkdirs())gọi phương thức. Tôi không biết làm thế nào để hoàn toàn an toàn mà không cần dùng đến mã nguồn gốc, mà tôi cho rằng đó là những gì được chôn giấu bên trong File.createTempFile().


1

Trước Java 7, bạn cũng có thể:

File folder = File.createTempFile("testFileUtils", ""); // no suffix
folder.delete();
folder.mkdirs();
folder.deleteOnExit();

1
Mã đẹp. Nhưng không may "xóaOnExit ()" sẽ không hoạt động, vì Java không thể xóa toàn bộ thư mục cùng một lúc. Bạn phải xóa tất cả các tệp đệ quy: /
Adam Taras

1

Hãy thử ví dụ nhỏ này:

Mã số:

try {
    Path tmpDir = Files.createTempDirectory("tmpDir");
    System.out.println(tmpDir.toString());
    Files.delete(tmpDir);
} catch (IOException e) {
    e.printStackTrace();
}


Nhập khẩu:
java.io.IOException
java.nio.file.Files
java.nio.file.Path

Đầu ra bảng điều khiển trên máy Windows:
C: \ Users \ userName \ AppData \ Local \ Temp \ tmpDir2908538301081367877

Nhận xét:
Files.createTempDirectory tạo ID duy nhất một cách tự nhiên - 2908538301081367877.

Lưu ý:
Đọc phần sau để xóa các thư mục đệ quy:
Xóa các thư mục đệ quy trong Java


0

Sử dụng File#createTempFiledeleteđể tạo một tên duy nhất cho thư mục có vẻ ok. Bạn nên thêm một ShutdownHookđể xóa thư mục (đệ quy) khi tắt máy JVM.


Một cái móc tắt máy là cồng kềnh. Tập tin # xóaOnExit cũng không hoạt động?
Daniel Hiller

2
#deleteOnExit không hoạt động với tôi - Tôi tin rằng nó sẽ không xóa các thư mục không trống.
muriloq

Tôi đã triển khai thử nghiệm nhanh chạy với Java 8, nhưng thư mục tạm thời không bị xóa, xem pastebin.com/mjgG70KG
geri
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.