จะสร้างไดเรคทอรีชั่วคราว / โฟลเดอร์ใน Java ได้อย่างไร?


364

มีวิธีมาตรฐานและเชื่อถือได้ในการสร้างไดเร็กทอรีชั่วคราวภายในแอ็พพลิเคชัน Java หรือไม่? มีรายการในฐานข้อมูลปัญหาของ Javaซึ่งมีรหัสเล็กน้อยในความคิดเห็น แต่ฉันสงสัยว่ามีวิธีการแก้ปัญหามาตรฐานที่จะพบในหนึ่งในห้องสมุดปกติ (Apache Commons เป็นต้น)?

คำตอบ:


390

หากคุณใช้ JDK 7 ให้ใช้คลาสFiles.createTempDirectoryใหม่เพื่อสร้างไดเรกทอรีชั่วคราว

Path tempDirWithPrefix = Files.createTempDirectory(prefix);

ก่อนที่ JDK 7 ควรทำสิ่งนี้:

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

คุณสามารถทำการยกเว้นที่ดีกว่า (subclass IOException) ได้ถ้าต้องการ


12
สิ่งนี้เป็นอันตราย Java เป็นที่รู้จักกันว่าจะไม่ลบไฟล์ทันทีดังนั้น mkdir อาจล้มเหลวในบางครั้ง
Demiurg

4
@Demiurg เฉพาะกรณีของไฟล์ที่ไม่ถูกลบทันทีที่อยู่บน Windows เมื่อไฟล์นั้นเปิดอยู่แล้ว (มันสามารถเปิดได้โดยเครื่องสแกนไวรัสเป็นต้น) คุณมีเอกสารอื่นที่จะแสดงเป็นอย่างอื่น (ฉันอยากรู้เกี่ยวกับสิ่งต่างๆเช่น :-) ไหม หากเกิดขึ้นเป็นประจำรหัสข้างต้นจะไม่ทำงานถ้าหายากให้โทรไปยังรหัสด้านบนจนกว่าการลบจะเกิดขึ้น
TofuBeer

6
@Demiurg Java รู้ว่าจะไม่ลบไฟล์ทันที นั่นเป็นความจริงแม้ว่าคุณจะไม่เปิดมันก็ตาม temp.delete(); temp = new File(temp.getPath + ".d"); temp.mkdir(); ..., temp.delete();ดังนั้นวิธีที่ปลอดภัยมากขึ้น
XièJìléi

102
รหัสนี้มีสภาพการแย่งชิงระหว่างdelete()และmkdir(): กระบวนการที่เป็นอันตรายสามารถสร้างไดเรกทอรีเป้าหมายได้ในระหว่างนี้ (ใช้ชื่อไฟล์ที่เพิ่งสร้างขึ้น) ดูFiles.createTempDir()ทางเลือกอื่น
Joachim Sauer

11
ฉันชอบ ! โดดเด่นง่ายเกินไปที่จะพลาด ฉันอ่านโค้ดจำนวนมากที่เขียนโดยนักเรียน ... ถ้า (! i) เป็นเรื่องปกติมากพอที่จะทำให้รำคาญ :-)
TofuBeer

182

ห้องสมุด Google Guava มีสาธารณูปโภคที่เป็นประโยชน์มากมาย หนึ่งของโน้ตที่นี่เป็นระดับไฟล์ มันมีวิธีการที่เป็นประโยชน์มากมายเช่น:

File myTempDir = Files.createTempDir();

นี่เป็นสิ่งที่คุณต้องการในหนึ่งบรรทัด หากคุณอ่านเอกสารที่นี่คุณจะเห็นว่าการดัดแปลงที่เสนอFile.createTempFile("install", "dir")โดยทั่วไปจะแนะนำช่องโหว่ด้านความปลอดภัย


ฉันสงสัยว่าคุณอ้างถึงช่องโหว่ใด วิธีการนี้ดูเหมือนจะไม่สร้างเงื่อนไขการแย่งชิงเนื่องจาก File.mkdir () ถูกคาดการณ์ว่าจะล้มเหลวหากไดเรกทอรีดังกล่าวมีอยู่แล้ว (สร้างโดยผู้โจมตี) ฉันไม่คิดว่าการโทรนี้จะตามมาด้วยลิงก์ที่เป็นอันตรายเช่นกัน คุณช่วยอธิบายสิ่งที่คุณตั้งใจได้ไหม?
abb

3
@ รับเบอร์: ฉันไม่ทราบรายละเอียดของสภาพการแข่งขันที่ระบุไว้ในเอกสารประกอบของ Guava ฉันสงสัยว่าเอกสารนั้นถูกต้องเนื่องจากเป็นปัญหาที่เฉพาะเจาะจง
Spina

1
@ คุณพูดถูก ตราบใดที่มีการตรวจสอบการส่งคืนของ mkdir () จะปลอดภัย รหัส Spina ชี้ไปที่ใช้วิธีการ mkdir () นี้ grepcode.com/file/repo1.maven.org/maven2/com.google.guava/guava/… . นี่เป็นปัญหาที่เป็นไปได้เฉพาะในระบบ Unix เมื่อใช้ไดเรกทอรี / tmp เนื่องจากมีการเปิดใช้งานบิตเหนียว
Sarel Botha

@SalesBotha ขอบคุณที่เติมช่องว่างที่นี่ ฉันสงสัยอย่างเกียจคร้านเกี่ยวกับเรื่องนี้มาระยะหนึ่งแล้ว
Spina

168

หากคุณต้องการไดเรกทอรีชั่วคราวสำหรับการทดสอบและคุณใช้ jUnit @Ruleร่วมกับTemporaryFolderแก้ไขปัญหาของคุณ:

@Rule
public TemporaryFolder folder = new TemporaryFolder();

จากเอกสาร :

กฎโฟลเดอร์ชั่วคราวช่วยให้การสร้างไฟล์และโฟลเดอร์ที่รับประกันว่าจะถูกลบเมื่อวิธีการทดสอบเสร็จสิ้น (ไม่ว่าจะผ่านหรือล้มเหลว)


ปรับปรุง:

หากคุณกำลังใช้ JUnit Jupiter (เวอร์ชัน 5.1.1 หรือสูงกว่า) คุณมีตัวเลือกในการใช้ JUnit Pioneer ซึ่งเป็น JUnit 5 Extension Pack

คัดลอกมาจากเอกสารโครงการ :

ตัวอย่างเช่นการทดสอบต่อไปนี้ลงทะเบียนส่วนขยายสำหรับวิธีการทดสอบเดียวสร้างและเขียนไฟล์ไปยังไดเรกทอรีชั่วคราวและตรวจสอบเนื้อหา

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

ข้อมูลเพิ่มเติมในJavaDocและJavaDoc ของ TempDirectory

Gradle:

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>

อัปเดต 2:

@TempDirคำอธิบายประกอบถูกเพิ่มลงใน JUnit ดาวพฤหัสบดี 5.4.0 ปล่อยเป็นคุณลักษณะทดลอง ตัวอย่างที่คัดลอกจากคู่มือผู้ใช้ 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
มีให้ตั้งแต่ JUnit 4.7
Eduard Wirch

ใช้งานไม่ได้กับ JUnit 4.8.2 บน Windows 7! (ปัญหาการอนุญาต)
ยกเว้น

2
@ CraigRinger: ทำไมมันไม่ฉลาดที่จะใช้สิ่งนี้?
Adam Parkin

2
@ AdamParkin จริงๆแล้วฉันจำไม่ได้แล้ว คำอธิบายล้มเหลว!
Craig Ringer

1
ประโยชน์หลักของวิธีนี้คือไดเร็กทอรีนั้นได้รับการจัดการโดย JUnit (สร้างขึ้นก่อนการทดสอบและลบซ้ำหลังจากการทดสอบ) และมันก็ใช้งานได้ หากคุณได้รับ "temp dir ยังไม่ได้สร้าง" อาจเป็นเพราะคุณลืม @Rule หรือฟิลด์ในที่สาธารณะไม่ได้
Bogdan Calmac

42

โค้ดที่เขียนอย่างไร้เดียงสาเพื่อแก้ปัญหานี้ได้รับผลกระทบจากสภาพการแข่งขัน ในอดีตคุณสามารถคิดอย่างรอบคอบเกี่ยวกับสภาพการแข่งขันและเขียนด้วยตัวคุณเองหรือคุณอาจใช้ห้องสมุดบุคคลที่สามเช่น Guava ของ Google (ตามคำแนะนำของ Spina) หรือคุณสามารถเขียนรหัสบั๊กกี้ได้

แต่ตั้งแต่ JDK 7 มีข่าวดี! ขณะนี้ไลบรารีมาตรฐานของ Java เองนั้นมีวิธีแก้ปัญหาที่ทำงานได้อย่างเหมาะสม คุณต้องการjava.nio.file.Files # createTempDirectory () จากเอกสาร :

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

สร้างไดเรกทอรีใหม่ในไดเรกทอรีที่ระบุโดยใช้คำนำหน้าที่กำหนดเพื่อสร้างชื่อ เส้นทางที่ได้จะเชื่อมโยงกับ FileSystem เดียวกันกับไดเรกทอรีที่กำหนด

รายละเอียดเกี่ยวกับวิธีการสร้างชื่อของไดเรกทอรีนั้นขึ้นอยู่กับการใช้งานและไม่ได้ระบุ จะใช้คำนำหน้าเพื่อสร้างชื่อผู้สมัคร

วิธีนี้จะช่วยแก้ไขรายงานบักแบบโบราณที่น่าอับอายในตัวติดตามบั๊ก Sun ซึ่งขอฟังก์ชันดังกล่าว


35

นี่คือซอร์สโค้ดของ Files.createTempDir () ของไลบรารี Guava มันไม่มีอะไรซับซ้อนเท่าที่คุณคิด:

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) + ')');
}

โดยค่าเริ่มต้น:

private static final int TEMP_DIR_ATTEMPTS = 10000;

ดูที่นี่


28

อย่าใช้deleteOnExit()แม้ว่าคุณจะลบในภายหลังอย่างชัดเจน

Google 'deleteonexit นั้นชั่วร้าย'สำหรับข้อมูลเพิ่มเติม แต่ส่วนสำคัญของปัญหาคือ:

  1. deleteOnExit() ลบเฉพาะสำหรับการปิด JVM ปกติไม่ล่มหรือฆ่ากระบวนการ JVM

  2. deleteOnExit() ลบเฉพาะในการปิด JVM - ไม่ดีสำหรับกระบวนการเซิร์ฟเวอร์ที่รันนานเพราะ:

  3. ความชั่วร้ายที่สุดของทั้งหมด - deleteOnExit()ใช้หน่วยความจำสำหรับแต่ละรายการไฟล์ชั่วคราว หากกระบวนการของคุณกำลังทำงานเป็นเวลาหลายเดือนหรือสร้างไฟล์ชั่วคราวจำนวนมากในเวลาอันสั้นคุณจะใช้หน่วยความจำและไม่ปล่อยจนกว่าจะปิด JVM


1
เรามี JVM ซึ่งไฟล์ class และ jar ได้รับไฟล์ที่ซ่อนอยู่ที่สร้างโดย JVM และข้อมูลเพิ่มเติมนี้ใช้เวลาค่อนข้างนานในการลบ เมื่อทำการ redeploys ที่ร้อนแรงบนเว็บคอนเทนเนอร์ที่ระเบิดสงคราม JVM สามารถใช้เวลาทำความสะอาดหลังจากจบ แต่ใช้เวลาไม่กี่นาที
Thorbjørn Ravn Andersen

20

ตั้งแต่ Java 1.7 createTempDirectory(prefix, attrs)และcreateTempDirectory(dir, prefix, attrs)รวมอยู่ในjava.nio.file.Files

ตัวอย่าง: File tempDir = Files.createTempDirectory("foobar").toFile();


14

นี่คือสิ่งที่ฉันตัดสินใจที่จะทำเพื่อรหัสของตัวเอง:

/**
 * 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
สิ่งนี้ไม่ปลอดภัย ดูความคิดเห็นโดย Joachim Sauer ในตัวเลือกแรก (ไม่ปลอดภัยเท่ากัน) วิธีที่เหมาะสมในการตรวจสอบการมีอยู่ของไฟล์หรือ dir และนอกเหนือจากการคว้าชื่อไฟล์แบบอะตอมคือการสร้างไฟล์หรือ dir
zbyszek

1
@zbyszek javadocs พูดว่า "UUID ถูกสร้างขึ้นโดยใช้ตัวสร้างตัวเลขสุ่มหลอกแบบเข้ารหัสลับ" ระบุว่ากระบวนการที่เป็นอันตรายจะสร้าง dir ด้วยชื่อเดียวกันระหว่างที่มีอยู่ () และ mkdirs () ได้อย่างไร ในความเป็นจริงเมื่อมองดูสิ่งนี้ในตอนนี้ฉันคิดว่าการทดสอบ () ที่ฉันมีอยู่อาจจะโง่ไปหน่อย
Keith

Keith: UUID ปลอดภัยหรือไม่สำคัญในกรณีนี้ เพียงพอสำหรับข้อมูลเกี่ยวกับชื่อที่คุณสอบถามว่า "รั่วไหล" อย่างใด ตัวอย่างเช่นสมมติว่าไฟล์ที่สร้างขึ้นนั้นอยู่ในระบบไฟล์ NFS และผู้โจมตีสามารถฟัง (แบบพาสซีฟ) ไปยังแพ็กเก็ตได้ หรือสถานะเครื่องกำเนิดไฟฟ้าแบบสุ่มรั่วไหลออกมา ในความคิดเห็นของฉันฉันบอกว่าวิธีแก้ปัญหาของคุณไม่ปลอดภัยเท่าคำตอบที่ยอมรับ แต่นี่ไม่ยุติธรรม: คำตอบที่ได้รับการยอมรับนั้นไม่สำคัญที่จะเอาชนะด้วยการไม่พูดถึงและสิ่งนี้ยากที่จะเอาชนะได้ อย่างไรก็ตามในบางสถานการณ์เป็นไปได้อย่างแน่นอน
zbyszek

2
ฉันมีความคิดเดียวกันและนำวิธีแก้ปัญหามาใช้โดยใช้บิต UUID แบบสุ่มเช่นนี้ ไม่มีการตรวจสอบอยู่มีเพียงหนึ่งความพยายามที่จะสร้าง - RNG ที่แข็งแกร่งที่ใช้โดยวิธี randomUUID รับประกันได้ว่าไม่มีการชนกันมาก (สามารถใช้สำหรับการสร้างคีย์หลักในตารางฐานข้อมูลทำได้ด้วยตัวเองและไม่เคยรู้จักการปะทะกัน) หากใครไม่แน่ใจลองดูstackoverflow.com/questions/2513573/…
brabster

หากคุณดูที่การนำ Java ไปใช้พวกเขาจะสร้างชื่อแบบสุ่มจนกว่าจะไม่มีการชนกัน ความพยายามสูงสุดของพวกเขาไม่มีที่สิ้นสุด ดังนั้นหากใครบางคนประสงค์ร้ายก็ต้องเดาชื่อไฟล์ / ไดเรกทอรีของคุณมันจะวนซ้ำตลอดไป นี่คือลิงค์ไปยังแหล่งที่มา: hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/9fb81d7a2f16/src/share/ ในทางใดทางหนึ่งฉันคิดว่ามันสามารถสร้างระบบไฟล์ได้ สร้างไดเรกทอรี แต่ฉันเดาว่าจะไม่ทำตามรหัสแหล่งที่มา
dosentmatter

5

ดี "createTempFile" สร้างไฟล์จริง เหตุใดจึงไม่เพียงลบออกก่อนจากนั้นจึงทำ mkdir กับมัน


1
คุณควรตรวจสอบค่าที่ส่งคืนสำหรับ mkdir () เสมอ หากเป็นเท็จแสดงว่ามีไดเรกทอรีอยู่แล้ว ซึ่งอาจทำให้เกิดปัญหาด้านความปลอดภัยดังนั้นให้พิจารณาว่าควรเพิ่มข้อผิดพลาดในแอปพลิเคชันของคุณหรือไม่
Sarel Botha

1
ดูหมายเหตุเกี่ยวกับสภาพการแข่งขันในคำตอบอื่น ๆ
Volker Stolz

ฉันชอบนี้ยกเว้นการแข่งขัน
Martin Wickman

4

รหัสนี้ควรใช้งานได้ดีพอสมควร:

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
เกิดอะไรขึ้นถ้าไดเรกทอรีมีอยู่แล้วและคุณไม่มีสิทธิ์อ่าน / เขียนเข้าถึงหรือถ้ามันเป็นไฟล์ปกติ คุณยังมีสภาพการแข่งขันที่นั่น
Jeremy Huiskamp

2
นอกจากนี้ deleteOnExit จะไม่ลบไดเรกทอรีที่ไม่ว่างเปล่า
เทรนตัน

3

ตามที่กล่าวไว้ในRFE นี้และความคิดเห็นของมันคุณสามารถโทรได้tempDir.delete()ก่อน หรือคุณสามารถใช้System.getProperty("java.io.tmpdir")และสร้างไดเรกทอรีที่นั่น ไม่ว่าจะด้วยวิธีใดคุณควรจำไว้ว่าจะโทรไม่tempDir.deleteOnExit()เช่นนั้นไฟล์จะไม่ถูกลบหลังจากเสร็จสิ้น


คุณสมบัตินี้เรียกว่า "java.io.tmpdir" ไม่ใช่ "... temp" ใช่หรือไม่ ดูjava.sun.com/j2se/1.4.2/docs/api/java/io/File.html
Andrew Swan

ค่อนข้างมาก ฉันควรจะตรวจสอบก่อนที่จะทำซ้ำสิ่งที่ฉันอ่าน
Michael Myers

java.io.tmpdir มีการแชร์ไว้ดังนั้นคุณต้องทำ voodoo ปกติทั้งหมดเพื่อหลีกเลี่ยงการเหยียบลงบนนิ้วเท้าของคนอื่น
Thorbjørn Ravn Andersen

3

เพิ่งจะเสร็จสมบูรณ์นี่คือรหัสจากห้องสมุด Google guava มันไม่ใช่รหัสของฉัน แต่ฉันคิดว่ามันมีค่าที่จะแสดงไว้ที่นี่ในกระทู้นี้

  /** 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

ฉันได้รับปัญหาเดียวกันดังนั้นนี่เป็นเพียงคำตอบสำหรับผู้ที่สนใจและมันก็คล้ายกับข้อใดข้อหนึ่งข้างต้น:

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

และสำหรับแอปพลิเคชันของฉันฉันตัดสินใจที่จะเพิ่มตัวเลือกในการล้างtemp on exit ดังนั้นฉันจึงเพิ่มลงใน hook shut-down:

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

วิธีการย่อยและไฟล์ทั้งหมดก่อนที่จะลบtempโดยไม่ต้องใช้ callstack (ซึ่งเป็นทางเลือกโดยสิ้นเชิงและคุณสามารถทำได้ด้วยการเรียกซ้ำในตอนนี้) แต่ฉันต้องการอยู่ในด้านที่ปลอดภัย


2

อย่างที่คุณเห็นในคำตอบอื่น ๆ ไม่มีวิธีการมาตรฐานเกิดขึ้น ดังนั้นคุณพูดถึง Apache Commons แล้วฉันเสนอวิธีการต่อไปนี้โดยใช้ FileUtils จาก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;

}

สิ่งนี้เป็นที่ต้องการเนื่องจาก apache ใช้ไลบรารีที่ใกล้เคียงที่สุดกับ "มาตรฐาน" ที่ถามและทำงานกับทั้ง JDK 7 และรุ่นที่เก่ากว่า สิ่งนี้จะส่งคืนอินสแตนซ์ไฟล์ "เก่า" (ซึ่งเป็นแบบสตรีม) และไม่ใช่อินสแตนซ์พา ธ "ใหม่" (ซึ่งเป็นบัฟเฟอร์ตามและจะเป็นผลลัพธ์ของเมธอด getTemporaryDirectory () ของ JDK7 -> ดังนั้นจึงส่งคืนสิ่งที่คนส่วนใหญ่ต้องการเมื่อ พวกเขาต้องการสร้างไดเรกทอรีชั่วคราว


1

ฉันชอบความพยายามหลายครั้งในการสร้างชื่อที่ไม่ซ้ำกัน แต่วิธีนี้ไม่ได้ออกกฎการแย่งชิง กระบวนการอื่นสามารถส่งหลังจากการทดสอบexists()และif(newTempDir.mkdirs())การเรียกใช้เมธอด ฉันมีความคิดวิธีการที่จะทำให้สมบูรณ์ปลอดภัยนี้โดยไม่ต้อง resorting File.createTempFile()รหัสพื้นเมืองซึ่งผมคิดว่าเป็นสิ่งที่ฝังอยู่ภายใน


1

ก่อนหน้า Java 7 คุณสามารถ:

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

1
รหัสที่ดี แต่น่าเสียดาย "deleteOnExit ()" จะไม่ทำงานเนื่องจาก Java ไม่สามารถลบโฟลเดอร์ทั้งหมดในครั้งเดียว คุณต้องลบไฟล์ทั้งหมดซ้ำ: /
Adam Taras

1

ลองตัวอย่างเล็ก ๆ นี้:

รหัส:

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


การนำเข้า:
java.io.IOException
java.nio.file.Files
java.nio.file.Path

คอนโซลเอาต์พุตบนเครื่อง Windows:
C: \ Users \ ชื่อผู้ใช้ \ AppData \ Local \ Temp \ tmpDir2908538301081367877

ความคิดเห็น:
Files.createTempDirectory สร้าง ID เฉพาะ atomatically - 2908538301081367877

หมายเหตุ:
อ่านต่อไปนี้สำหรับการลบไดเรกทอรีซ้ำ:
ลบไดเรกทอรีซ้ำใน Java


0

การใช้File#createTempFileและdeleteเพื่อสร้างชื่อที่ไม่ซ้ำสำหรับไดเรกทอรีดูเหมือนว่าใช้ได้ คุณควรเพิ่ม a ShutdownHookเพื่อลบไดเร็กทอรี (เรียกซ้ำ) ในการปิด JVM


ขอปิดเครื่องยุ่งยาก ไฟล์ # deleteOnExit จะใช้งานไม่ได้หรือไม่
Daniel Hiller

2
#deleteOnExit ไม่ทำงานสำหรับฉัน - ฉันเชื่อว่ามันจะไม่ลบไดเรกทอรีที่ไม่ว่างเปล่าออก
muriloq

ฉันนำการทดสอบแบบรวดเร็วไปใช้กับ Java 8 แต่ไม่ได้ลบโฟลเดอร์ชั่วคราวออกโปรดดูpastebin.com/mjgG70KG
geri
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.