Java中生成唯一且简短的文件名的最佳方法是什么?

79

我不一定想使用UUID,因为它们相当长。

文件只需要在其目录中是唯一的。

我想到一个方法是使用File.createTempFile(String前缀,String后缀),但这似乎是错误的,因为文件不是临时文件。

必须处理同一毫秒创建的两个文件的情况。


5
不要太关注名称中的“Temp”部分;阅读Javadocs可以看出它更多地涉及独特性,这通常在临时文件中需要。但不一定仅限于此。 - StaxMan
16个回答

98

你可以使用具有3个参数的版本: File.createTempFile(String prefix, String suffix, File directory),它将允许你把临时文件放在你想要的位置。除非您告诉它,否则Java不会将其与任何其他文件区别对待。唯一的缺点是文件名保证至少有8个字符长(前缀至少3个字符,加上函数生成的5个或更多字符)。

如果这对你来说太长了,那么你可以从文件名"a"开始,并循环遍历"b"、"c"等,直到找到一个不存在的文件名为止。


3
但是它能保证程序多次运行之间的唯一性吗? - Reddy
7
根据文档,它将确保:(1)在调用此方法之前,由返回的抽象路径名表示的文件不存在,(2)在当前虚拟机的调用中,此方法或其任何变体都不会再次返回相同的抽象路径名。因此,如果您在同一目录中创建文件 - 而不删除它们 - 那么是唯一的。 - Spike Williams
关闭后它会删除文件吗?还是你需要在使用完后手动删除它? - Lucke
你必须删除这个文件。File testFile = File.createTempFile("MyApp", ".tmp", outputDirectory); testFile.delete(); - Alchemistmatt
@Lucke deleteOnExit() - lainatnavi

33

我会使用Apache Commons Lang库(http://commons.apache.org/lang)。

这里有一个类org.apache.commons.lang.RandomStringUtils,可以用来生成给定长度的随机字符串。 不仅适用于文件名生成,非常方便!

下面是示例:

String ext = "dat";
File dir = new File("/home/pregzt");
String name = String.format("%s.%s", RandomStringUtils.randomAlphanumeric(8), ext);
File file = new File(dir, name);

7
@marcolopes:但是两个文件的名称完全相同的机会非常小。假设我们有62个不同的字符(我不知道RandomStringUtils使用多少;大小写敏感情况下,我猜测为62),则文件名长度为n时,可能性为62的n次方。对于长度为8的上述示例,机会将是2.183401056×10¹⁴。 - Kris
@Kris,不过用几行代码,你就可以百分之百保证独一无二(或者只需使用现有的方法之一)。 - xorinzor
这不是一种惯用的方式。你最好避免使用它。使用临时文件,就像建议的那样 - Alexander Pozdneev
在项目中包含另一个库只为了生成随机文件名??我认为这不是一个可行的解决方案。 - Stunner

13

我使用时间戳

new File( simpleDateFormat.format( new Date() ) );

并将simpleDateFormat初始化为类似以下方式:

new SimpleDateFormat("File-ddMMyy-hhmmss.SSS.txt");

编辑

那么这个怎么办?

new File(String.format("%s.%s", sdf.format( new Date() ),
                                random.nextInt(9)));

除非在同一秒内创建的文件数量过多。

如果是这种情况并且名称不重要。

 new File( "file."+count++ );

:P


12
如果在同一秒或毫秒内创建了两个文件,那该怎么办? - Jeff Bloom
2
毫秒级不太可能,您可以在上面设置一个计时器,以防止在同一秒钟创建文件... - jesses.co.tt
1
@JeffBloom 我遇到了一个类似于你所问的问题。我正在创建一个文件,文件名中包含System.currentTimeMillis()。假设我复制该文件并粘贴另一个具有相同System.currentTimeMillis()的文件,则它将无法读取我的下一个文件。如何不允许用户复制和粘贴具有相同System.currentTimeMillis()的文件?任何帮助都将不胜感激。 - user8412632

10

这对我很有效:

String generateUniqueFileName() {
    String filename = "";
    long millis = System.currentTimeMillis();
    String datetime = new Date().toGMTString();
    datetime = datetime.replace(" ", "");
    datetime = datetime.replace(":", "");
    String rndchars = RandomStringUtils.randomAlphanumeric(16);
    filename = rndchars + "_" + datetime + "_" + millis;
    return filename;
}

// 使用:

String newFile;
do{
newFile=generateUniqueFileName() + "." + FileExt;
}
while(new File(basePath+newFile).exists());

输出的文件名应该看起来像:

2OoBwH8OwYGKW2QE_4Sep2013061732GMT_1378275452253.Ext

1
这并不保证一个唯一的文件名。然而,你可以通过检查文件是否存在,如果存在则生成另一个随机字符串来扩展这个答案。 - Caleb Adams
谢谢@CalebAdams :) 这可能会在多线程的情况下发生。已更新。 - MD. Mohiuddin Ahmed

8
请查看File javadoc,其中的createNewFile方法仅在文件不存在时创建文件,并返回一个布尔值以指示文件是否已创建。
您也可以使用exists()方法:
int i = 0;
String filename = Integer.toString(i);
File f = new File(filename);
while (f.exists()) {
    i++;
    filename = Integer.toString(i);
    f = new File(filename);
}
f.createNewFile();
System.out.println("File in use: " + f);

1
我不知道是否想太多了。如果在while循环之后,在f.createNewFile()调用之前,另一个进程创建了f会发生什么? - Guangliang

3
    //Generating Unique File Name
    public String getFileName() {
        String timeStamp = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss").format(new Date());
        return "PNG_" + timeStamp + "_.png";
    }

1
上述方法并不能保证两次快速调用(或同时进行的两个不同线程)返回唯一结果。 - usr-local-ΕΨΗΕΛΩΝ
@usr-local-ΕΨΗΕΛΩΝ,你也可以附加用户ID,以便文件名是唯一的。关于线程,我们可以使用RxJava来处理。 - LEGEND MORTAL
抱歉,我必须强烈反对。来自同一用户的两个线程将发生冲突。而且您的API必须要求实现者收集用户ID。Java以可移植性而闻名,如果您的语音运行在无用户的IoT设备上会怎样呢?正确的方法是执行一个for循环,附加一个递增的后缀,直到找不到具有该后缀的文件,并立即创建该文件。这与File.createTempFile在幕后所做的相同。 - usr-local-ΕΨΗΕΛΩΝ

3
如果您可以访问数据库,您可以在文件名中创建并使用序列。
select mySequence.nextval from dual;

保证唯一性并且不会太大(除非你正在生成大量文件)。


2
为什么这个被踩了呢?虽然这显然不是最优雅的解决方案,但至少应该能满足OP的要求。我认为这是一个完全有效的方法,特别是如果OP计划将这些信息与数据库相结合。 - Priidu Neemre

3

我使用当前毫秒数和随机数字

例如

Random random=new Random();
String ext = ".jpeg";
File dir = new File("/home/pregzt");
String name = String.format("%s%s",System.currentTimeMillis(),random.nextInt(100000)+ext);
File file = new File(dir, name);

2

结合其他答案,为什么不使用带有随机值附加的ms时间戳;重复直到没有冲突,在实践中几乎永远不会发生。

例如:文件-ccyymmdd-hhmmss-mmm-rrrrrr.txt


1
问题是同步。将冲突区域分离出来。 将文件命名为:(服务器名称)_(线程/进程名称)_(毫秒/时间戳).(扩展名) 例如:aws1_t1_1447402821007.png

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接