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

IT小君   2021-10-21T01:56:42

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

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

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

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

评论(16)
IT小君

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

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

2021-10-21T01:56:42   回复
IT小君

我会使用 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);
2021-10-21T01:56:42   回复
IT小君

我使用时间戳

IE

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

2021-10-21T01:56:43   回复
IT小君

这对我有用:

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
2021-10-21T01:56:43   回复
IT小君

查看文件 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);
2021-10-21T01:56:43   回复
IT小君

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

select mySequence.nextval from dual;

它将保证是唯一的并且不应变得太大(除非您要输出大量文件)。

2021-10-21T01:56:43   回复
IT小君
    //Generating Unique File Name
    public String getFileName() {
        String timeStamp = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss").format(new Date());
        return "PNG_" + timeStamp + "_.png";
    }
2021-10-21T01:56:43   回复
IT小君

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

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

2021-10-21T01:56:43   回复
IT小君

我使用随机数的当前毫秒

IE

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);
2021-10-21T01:56:43   回复
IT小君

为什么不使用基于时间戳的东西..?

2021-10-21T01:56:44   回复
IT小君

问题是同步。分离出冲突区域。

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

2021-10-21T01:56:44   回复
IT小君

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

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

或者,如果您更愿意在没有锁的情况下执行此操作,请使用时间步长加上线程 ID,并确保该函数花费的时间超过一毫秒,或者等待以使其执行。

2021-10-21T01:56:44   回复
IT小君

看起来您有一些用于创建唯一文件名的解决方案,所以我将不理会它。我会这样测试文件名:

    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
2021-10-21T01:56:44   回复
IT小君

这也有效

String logFileName = new SimpleDateFormat("yyyyMMddHHmm'.txt'").format(new Date());

logFileName = "loggerFile_" + logFileName;
2021-10-21T01:56:45   回复
IT小君

我知道我已经来不及回答这个问题了。但我认为我应该把它放在那里,因为它似乎与其他解决方案有所不同。

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

fileName = threadName(after removing special charater) + currentTimeStamp
2021-10-21T01:56:45   回复
IT小君

为什么不使用synchronized来处理多线程。这是我的解决方案,它可以生成一个短文件名,而且它是独一无二的。

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

blew 是 test 的主要功能,它的工作。

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

}
2021-10-21T01:56:45   回复