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


73

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

该文件仅需要在其目录中唯一。

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

需要处理在同一毫秒内创建的两个文件的情况。


5
不要过分注意名称的“ Temp”部分;阅读javadocs,以了解它实际上是关于唯一性的,临时文件通常需要这种唯一性。但不一定仅限于他们。
StaxMan

Answers:


92

好吧,您可以使用3参数版本: File.createTempFile(String prefix, String suffix, File directory)它将让您将其放置在所需的位置。除非您告知,否则Java将不会像对待其他任何文件一样对待它。唯一的缺点是文件名必须保证至少8个字符长(前缀至少3个字符,再加上该函数生成的5个或更多字符)。

如果那对您来说太长了,我想您总是可以从文件名“ a”开始,然后遍历“ b”,“ c”等,直到找到一个不存在的文件名。


2
但这是否可以保证程序多次运行之间的唯一性?
Reddy 2012年

6
根据文档,它将保证(1)返回的抽象路径名表示的文件在调用此方法之前不存在,并且(2)此方法或其任何变体都不会在该方法中再次返回相同的抽象路径名。虚拟机的当前调用。因此,如果您要在同一目录中创建文件-而不是删除它们-是的,它们将是唯一的。
斯派克·威廉姆斯

关闭后会删除文件吗?还是完成后将其删除?
卢克,

您必须删除文件。文件testFile = File.createTempFile(“ MyApp”,“ .tmp”,outputDirectory); testFile.delete();
Alchemistmatt

@Lucke deleteOnExit()
lainatnavi

28

我会使用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个字母,区分大小写),则为62 ^ n,其中n是您的文件名长度。对于长度为8的上述示例,机会为2.183401056×10 -1。
克里斯(Kris)2014年

尽管如此,@ Kris仍然可以用几行代码来100%保证唯一性(或仅使用现有方法之一)。
xorinzor

这不是惯用的方式。你最好避免它。根据建议使用临时文件。
亚历山大·波兹涅夫

在项目中一起包括另一个库只是为了生成随机文件名?我说这不是可行的解决方案。
震惊

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
是的,但是如果在同一秒或毫秒内创建两个文件该怎么办。
杰夫·布鲁姆

1
毫秒不太可能,您可以在其上放置一个计时器以阻止在同一秒内创建文件...
jesses.co.tt 2014年

1
@JeffBloom我遇到了与您所要求的类似的问题。我正在创建一个文件,文件名中包含System.currentTimeMillis()。假设我复制该文件并使用相同的System.currentTimeMillis()粘贴另一个文件,它将不会读取我的下一个文件。我如何不允许用户复制和粘贴具有相同System.currentTimeMillis()的文件。任何帮助将不胜感激

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

// USE:

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

输出文件名应如下所示:

2OoBwH8OwYGKW2QE_4Sep2013061732GMT_1378275452253.Ext

1
这不保证文件名唯一。但是,您可以通过检查文件是否存在来扩展此答案,如果存在则生成另一个随机字符串。
卡雷·亚当斯

感谢@CalebAdams :)在多线程的情况下可能会发生这种情况。更新。
MD。Mohiuddin Ahmed

7

查看File javadoc,方法createNewFile将仅在文件不存在时创建文件,并且将返回一个布尔值以说明文件是否已创建。

您也可以使用exist()方法:

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

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

4

如果您有权访问数据库,则可以在文件名中创建和使用序列。

select mySequence.nextval from dual;

它会被保证是唯一的,并且不会太大(除非您要抽出大量文件)。


2
为什么这在地球上被否决了?尽管显然这不是最优雅的解决方案,但它至少应满足OP的要求。我认为这是一种完全有效的考虑方法,特别是如果OP计划以某种方式将此信息与数据库合并。
Priidu Neemre

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-本地ΕΨΗΕΛΩΝ

@usr-local-ΕΨΗΕΛΩΝ,您还可以附加用户ID,以便该文件名是唯一的,并且我们可以使用rxJava生成线程
LEGEND

抱歉,我必须强烈不同意。来自同一用户的两个线程将发生冲突。而且您的API必须要求实施者收集用户ID。Java以可移植性而闻名,如果您的语音在无用户的IoT设备上运行怎么办?正确的方法是执行for循环,添加一个增加的后缀,直到找不到具有该后缀的文件,然后立即创建该文件。这是相同的File.createTempFile不引擎盖下
USR-本地ΕΨΗΕΛΩΝ

2

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

例如:File-ccyymmdd-hhmmss-mmm-rrrrrr.txt


1

为什么不只使用基于时间戳的内容呢?


1
如果在同一毫秒内创建两个文件怎么办?
杰夫·布鲁姆

2
重试失败,新的时间戳将有所不同
JRL

2
@杰夫 只需检测冲突,然后重试直到没有冲突即可;实际上,这种情况很少见。
劳伦斯·多尔

1
如果您仍然要检测到冲突,只需生成一个随机文件名而不必担心时间-例如,请参阅我的答案。在()中生成(例如)8个字符的相同文件名仍然非常罕见:
Jon Skeet

1

问题是同步。分开冲突地区。

将该文件命名为: (server-name)_(thread/process-name)_(millisecond/timestamp).(extension)
示例:aws1_t1_1447402821007.png


0

如何根据四舍五入到最接近毫秒数的时间戳或您需要的精度来生成...,然后使用锁来同步对函数的访问。

如果存储了最后生成的文件名,则可以根据需要在其后附加连续字母或其他数字以使其唯一。

或者,如果您希望不带锁,则使用时间步长加上线程ID,并确保该函数花费的时间超过一毫秒,或者等待它完成。


对这样的事情使用锁同步几乎总是一个可怕的想法-互斥锁可以很好地保护程序的内部存储器,而不是保护任何外部资源(例如数据库,文件系统)。
MK。

0

看起来您已经找到了一些创建唯一文件名的解决方案,所以我将不理它。我将以这种方式测试文件名:

    String filePath;
    boolean fileNotFound = true;
    while (fileNotFound) {
        String testPath = generateFilename();

        try {
            RandomAccessFile f = new RandomAccessFile(
                new File(testPath), "r");
        } catch (Exception e) {
            // exception thrown by RandomAccessFile if 
            // testPath doesn't exist (ie: it can't be read)

            filePath = testPath;
            fileNotFound = false;
        }
    }
    //now create your file with filePath


0

我知道我现在回答这个问题为时已晚。但是我认为我应该说这个,因为它似乎与其他解决方案有所不同。

我们可以将线程名和当前timeStamp连接为文件名。但这有一个问题,例如某些线程名包含特殊字符(例如“ \”),这会在创建文件名时产生问题。因此我们可以从线程名称中删除特殊字符,然后连接线程名称和时间戳

fileName = threadName(after removing special charater) + currentTimeStamp

0

为什么不使用同步来处理多线程。这是我的解决方案,它可以生成一个短文件名,并且是唯一的。

private static synchronized String generateFileName(){
    String name = make(index);
    index ++;
    return name;
}
private static String make(int index) {
    if(index == 0) return "";
    return String.valueOf(chars[index % chars.length]) + make(index / chars.length);
}
private static int index = 1;
private static char[] chars = {'a','b','c','d','e','f','g',
        'h','i','j','k','l','m','n',
        'o','p','q','r','s','t',
        'u','v','w','x','y','z'};

吹是测试的主要功能,它的工作。

public static void main(String[] args) {
    List<String> names = new ArrayList<>();
    List<Thread> threads = new ArrayList<>();
    for (int i = 0; i < 100; i++) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    String name = generateFileName();
                    names.add(name);
                }
            }
        });
        thread.run();
        threads.add(thread);
    }

    for (int i = 0; i < 10; i++) {
        try {
            threads.get(i).join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    System.out.println(names);
    System.out.println(names.size());

}

0

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

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);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.