Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang Generate Unique Filename With Extension

I want to generate unique filenames in Golang with extensions. Very much akin to ioutil.TempFile, which only takes prefixes.

This has been brought up many times in the forums https://groups.google.com/forum/#!topic/golang-nuts/PHgye3Hm2_0 and the Go goes seem pretty intent on not adding that functionality to TempFile.

So what's the suggested way to handle this? Should I just copy/paste the TempFile code and add in a suffix parameter?

like image 332
John Montague Avatar asked Jan 18 '15 00:01

John Montague


2 Answers

Update (2020: the original answer is from 2015)

As noted in Lax's answer, Go 1.11 (Apr. 2018) has changed TempFile prefix to a pattern.

See commit 191efbc from CL 105675 after issue 4896

Users of TempFile need to be able to supply the suffix, especially when using operating systems that give semantic meaning to the filename extension such as Windows.
Renaming the file to include an extension after the fact is insufficient as it could lead to race conditions.

If the string given to TempFile includes a "*", the random string replaces the "*".

For example "myname.*.bat" will result in a random filename such as "myname.123456.bat".

If no "*' is included the old behavior is retained, and the random digits are appended to the end.

If multiple "*" are included, the final one is replaced, thus permitting a pathological programmer to create filenames such as "foo*.123456.bat" but not "foo.123456.*.bat"


Original Answer (2015)

Should I just copy/paste the TempFile code and add in a suffix parameter?

That would be one way.
Other way is to make a quick -- crude -- implementation, as in this project:

// TempFileName generates a temporary filename for use in testing or whatever
func TempFileName(prefix, suffix string) string {
    randBytes := make([]byte, 16)
    rand.Read(randBytes)
    return filepath.Join(os.TempDir(), prefix+hex.EncodeToString(randBytes)+suffix)
}

As James Henstridge comments below, this is a crude function:

That function can return file names that already exist, for instance. Such an API should be creating the file by opening it with O_CREAT | O_EXCL to ensure that no one else creates the file between deciding on the name and creating the file.

That crude function above illustrates only the use of rand.Read() to generate a filename.

But the other checks are all in io/ioutil/tempfile.go.
3of3 suggests to use a function in math.rand instead of copying the random number generator in io/ioutil/tempfile.go.

like image 149
VonC Avatar answered Sep 21 '22 15:09

VonC


Starting with Go 1.16 (Feb 2021), you can use os.CreateTemp:

package main
import "os"

func main() {
   dirs := []string{"", "."}
   for _, dir := range dirs {
      f, err := os.CreateTemp(dir, "*.txt")
      if err != nil {
         panic(err)
      }
      defer f.Close()
      println(f.Name())
   }
}

Result:

C:\Windows\TEMP\163991747.txt
.\2499233563.txt

https://godocs.io/os#CreateTemp

like image 29
Zombo Avatar answered Sep 18 '22 15:09

Zombo