121

I have a python web form with two options - File upload and textarea. I need to take the values from each and pass them to another command-line program. I can easily pass the file name with file upload options, but I am not sure how to pass the value of the textarea.

I think what I need to do is:

  1. Generate a unique file name
  2. Create a temporary file with that name in the working directory
  3. Save the values passed from textarea into the temporary file
  4. Execute the commandline program from inside my python module and pass it the name of the temporary file

I am not sure how to generate a unique file name. Can anybody give me some tips on how to generate a unique file name? Any algorithms, suggestions, and lines of code are appreciated.

Thanks for your concern

culix
  • 10,188
  • 6
  • 36
  • 52
MysticCodes
  • 3,092
  • 5
  • 25
  • 33
  • 1
    I edited your question to try and make it more clear. Let me know if I interpreted something incorrectly! – culix Sep 01 '12 at 10:12

9 Answers9

178

I didn't think your question was very clear, but if all you need is a unique file name...

import uuid

unique_filename = str(uuid.uuid4())
Kostanos
  • 9,615
  • 4
  • 51
  • 65
EnigmaCurry
  • 5,597
  • 2
  • 23
  • 15
  • Sorry i am working on windows platform so dont know how to handle subprocess – MysticCodes Jun 03 '10 at 10:04
  • uuid seems to create a long unique string. I dont think its better to have file name with long string and UUID, () in it. – MysticCodes Jun 03 '10 at 10:37
  • This mechanism of creating temporary files is similar to using [tempfile.mktemp](https://docs.python.org/2/library/tempfile.html#tempfile.mktemp), and because of that, also vulnerable to exploits: _"By the time you get around to doing anything with the file name it returns, someone else may have beaten you to the punch."_ It is recommended to use the other methods in [tempfile module](https://docs.python.org/2/library/tempfile.html). – Simón Feb 20 '15 at 22:47
  • 11
    I think `uuid.uuid4().hex` would be a better choice, see detail [here](https://stackoverflow.com/a/44992275/5511849). – Grey Li Jul 09 '17 at 01:55
  • 4
    @ToloPalmer: It's more likely that your computer's CPU has a processing error that causes it to load the wrong file than it is a generated UUID collides with any existing value. UUID produces a unique name in a model of computing that understands not all computation is pure mathematics. – GManNickG Jul 09 '17 at 02:05
  • 2
    Pardon my ignorance old comment... Indeed is not unique but very unlikely to collide, so good choice ;) – Tolo Palmer Jul 09 '17 at 09:25
57

If you want to make temporary files in Python, there's a module called tempfile in Python's standard libraries. If you want to launch other programs to operate on the file, use tempfile.mkstemp() to create files, and os.fdopen() to access the file descriptors that mkstemp() gives you.

Incidentally, you say you're running commands from a Python program? You should almost certainly be using the subprocess module.

So you can quite merrily write code that looks like:

import subprocess
import tempfile
import os

(fd, filename) = tempfile.mkstemp()
try:
    tfile = os.fdopen(fd, "w")
    tfile.write("Hello, world!\n")
    tfile.close()
    subprocess.Popen(["/bin/cat", filename]).wait()        
finally:
    os.remove(filename)

Running that, you should find that the cat command worked perfectly well, but the temporary file was deleted in the finally block. Be aware that you have to delete the temporary file that mkstemp() returns yourself - the library has no way of knowing when you're done with it!

(Edit: I had presumed that NamedTemporaryFile did exactly what you're after, but that might not be so convenient - the file gets deleted immediately when the temp file object is closed, and having other processes open the file before you've closed it won't work on some platforms, notably Windows. Sorry, fail on my part.)

Richard Barrell
  • 4,361
  • 2
  • 22
  • 16
  • using **NamedTemporaryFile** is probably what they want (unless they want it to stay on the server, and then they can use "tempfile.NamedTemporaryFile(delete=False)") – Terence Honles Jun 02 '10 at 21:24
  • Can i make that temporary file name unique too ? so i can save it later when subprocess is completed with unique name – MysticCodes Jun 02 '10 at 21:25
  • @Terence Honles: I'd suggested tempfile.NamedTemporaryFile() originally, but you can't really use that to make temporary files that other processes can access on Windows. NamedTemporaryFile(delete=False) certainly is *cleaner*, though. @user343934: tempfile.mkstemp() is guaranteed to give you a unique name each time it's called - it generates the names randomly and it uses the OS facilities (O_EXCL, if you're wondering) to avoid collisions. – Richard Barrell Jun 02 '10 at 21:33
  • wow I didn't know it doesn't work on windows... fail :( ...I guess that's good to know – Terence Honles Jun 04 '10 at 06:30
  • @Terence Honles: NamedTemporaryFile() doesn't actually fail on Windows (as far as I know), but you can't close file without deleting it too, and (as I understand file semantics on Windows) no other program can open the file while you have it open. I might be wrong; the semantics for having multiple processes sharing a file under Windows might have changed since I last checked. – Richard Barrell Jun 04 '10 at 11:43
  • @ Richard, it says access denied when it tried to provide "C:/wamp/www/project" instead of "/bin/cat" – MysticCodes Jun 04 '10 at 12:48
  • @user343934 /bin/cat is the name of a program, which always exists on Unix. I put that in because I forgot that people use computers that aren't Macs ;) You could replace it with the name of a command that will word on Windows. For instance, replace the whole subprocess.Popen line with: ` subprocess.Popen(["cmd.exe", "/c", "type", filename]).wait()` I believe that that will work, but I don't have a Windows box with Python handy to test with. – Richard Barrell Jun 07 '10 at 10:17
54

The uuid module would be a good choice, I prefer to use uuid.uuid4().hex as random filename because it will return a hex string without dashes.

import uuid
filename = uuid.uuid4().hex

The outputs should like this:

>>> import uuid
>>> uuid.uuid()
UUID('20818854-3564-415c-9edc-9262fbb54c82')
>>> str(uuid.uuid4())
'f705a69a-8e98-442b-bd2e-9de010132dc4'
>>> uuid.uuid4().hex
'5ad02dfb08a04d889e3aa9545985e304'  # <-- this one
Grey Li
  • 11,664
  • 4
  • 54
  • 64
  • 3
    What's the problem of having dashes? – David Lopez Mar 25 '20 at 20:39
  • 1
    Is it aesthetics or there's another reason for adding `.hex`? – simanacci Feb 06 '21 at 08:07
  • 3
    Normally you would use the dashes to separate words in a filename (e.g. `my-favorite-shoes.jpg`). However, if the filename is randomly generated, I would prefer a filename without dashes, it's more beautiful, and the dashes here are meaningless. – Grey Li Feb 07 '21 at 06:04
19

Maybe you need unique temporary file?

import tempfile

f = tempfile.NamedTemporaryFile(mode='w+b', delete=False)

print f.name
f.close()

f is opened file. delete=False means do not delete file after closing.

If you need control over the name of the file, there are optional prefix=... and suffix=... arguments that take strings. See https://docs.python.org/3/library/tempfile.html.

Wandering Logic
  • 3,323
  • 1
  • 20
  • 25
  • This is great if you don't need to control the name of the file. – hiwaylon Oct 28 '14 at 18:34
  • 1
    It should be tmpfile.NamedTemporaryFile not just NamedTemporaryFile. – user1993015 Jul 15 '17 at 03:19
  • `w+b` is the default `mode`. Using any `tempfile` functionality has the disadvantage of incorrect file access permissions: `tempfile` documents to use `os.O_TMPFILE` as mask, but normal file creation respects `os.umask()`. – m8mble Apr 04 '18 at 14:44
14

You can use the datetime module

import datetime
uniq_filename = str(datetime.datetime.now().date()) + '_' + str(datetime.datetime.now().time()).replace(':', '.')

Note that: I am using replace since the colons are not allowed in filenames in many operating systems.

That's it, this will give you a unique filename every single time.

Community
  • 1
  • 1
Nimish Agrawal
  • 149
  • 1
  • 4
  • 4
    Unless the file names are created immediately after each other (e.g. in a loop). Then they're the same. – skjerns Nov 19 '18 at 12:48
5

In case you need short unique IDs as your filename, try shortuuid, shortuuid uses lowercase and uppercase letters and digits, and removing similar-looking characters such as l, 1, I, O and 0.

>>> import shortuuid
>>> shortuuid.uuid()
'Tw8VgM47kSS5iX2m8NExNa'
>>> len(ui)
22

compared to

>>> import uuid
>>> unique_filename = str(uuid.uuid4())
>>> len(unique_filename)
36
>>> unique_filename
'2d303ad1-79a1-4c1a-81f3-beea761b5fdf'
Milind Deore
  • 2,887
  • 5
  • 25
  • 40
1

I came across this question, and I will add my solution for those who may be looking for something similar. My approach was just to make a random file name from ascii characters. It will be unique with a good probability.

from random import sample
from string import digits, ascii_uppercase, ascii_lowercase
from tempfile import gettempdir
from os import path

def rand_fname(suffix, length=8):
    chars = ascii_lowercase + ascii_uppercase + digits

    fname = path.join(gettempdir(), 'tmp-'
                + ''.join(sample(chars, length)) + suffix)

    return fname if not path.exists(fname) \
                else rand_fname(suffix, length)
behzad.nouri
  • 74,723
  • 18
  • 126
  • 124
  • 1
    The obvious answer for the question had to do with the uuid package. However my target server has python 2.4, no uuid package and upgrading was not authorized by server owner due to legacy software incompatibilities, so this answer works for me. – Alberto Gaona Oct 03 '14 at 18:04
  • 1
    I particularly like this answer: can be easily tweaked to project specs. – swdev Apr 29 '15 at 02:41
  • 1
    1) There's no reason to use recursion here, especially unbounded. 2) There exists race condition between the time that `path.exists()` returns `False` and the time that a consumer actually opens the file. – Jonathon Reinhart Apr 08 '17 at 22:21
1

To create a unique file path if its exist, use random package to generate a new string name for file. You may refer below code for same.

import os
import random
import string

def getUniquePath(folder, filename):    
    path = os.path.join(folder, filename)
    while os.path.exists(path):
         path = path.split('.')[0] + ''.join(random.choice(string.ascii_lowercase) for i in range(10)) + '.' + path.split('.')[1]
    return path

Now you can use this path to create file accordingly.

RoboMex
  • 828
  • 12
  • 13
0

This can be done using the unique function in ufp.path module.

import ufp.path
ufp.path.unique('./test.ext')

if current path exists 'test.ext' file. ufp.path.unique function return './test (d1).ext'.

bradym
  • 4,880
  • 1
  • 31
  • 36
c2o93y50
  • 211
  • 2
  • 4