views:

562

answers:

1

I'd like to store uploaded files into a specific directory that depends on the URI of the POST request. Perhaps, I'd also like to rename the file to something fixed (the name of the file input for example) so I have an easy way to grep the file system, etc. and also to avoid possible security problems.

What's the preferred way to do this in Django?

Edit: I should clarify that I'd be interested in possibly doing this as a file upload handler to avoid writing a large file twice to the file system.

Edit2: I suppose one can just 'mv' the tmp file to a new location. That's a cheap operation if on the same file system.

+1  A: 

Django gives you total control over where (and if) you save files. See: http://docs.djangoproject.com/en/dev/topics/http/file-uploads/

The below example shows how to combine the URL and the name of the uploaded file and write the file out to disk:

upload(request):
    folder = request.path.replace("/", "_")
    uploaded_filename = request.FILES['file'].name

    # create the folder if it doesn't exist.
    try:
        os.mkdir(os.path.join(BASE_PATH, folder))
    except:
        pass

    # save the uploaded file inside that folder.
    full_filename = os.path.join(BASE_PATH, folder, uploaded_filename)
    fout = open(full_filename, 'wb+')
    for chunk in f.chunks():
        fout.write(chunk)
    fout.close()

Edit: How to do this with a FileUploadHandler? It traced down through the code and it seems like you you need to do four things to repurpose the TemporaryFileUploadHandler to save outside of FILE_UPLOAD_TEMP_DIR:

  1. extend TemporaryUploadedFile and override init() to pass through a different directory to NamedTemporaryFile. It can use the try-mkdir-except-pass I showed above.

  2. extend TemporaryFileUploadHandler and override new_file() to use the above class.

  3. also extend init() to accept the directory where you want the folder to go.

  4. Dynamically add the request handler, passing through a directory determined from the URL:

    request.upload_handlers = [ProgressBarUploadHandler(request.path.replace('/', '_')]

While non-trivial, it's still easier than writing a handler from scratch: In particular, you won't have to write a single line of error-prone buffered reading. Steps 3 and 4 are necessary because FileUploadHandlers are not passed request information by default, I believe, so you'll have to tell it separately if you want to use the URL somehow.

I can't really recommend writing a custom FileUploadHandler for this. It's really mixing layers of responsibility. Relative to the speed of uploading a file over the internet, doing a local file copy is insignificant. And if the file's small, Django will just keep it in memory without writing it out to a temp file. I have a bad feeling that you'll get all this working and find you can't even measure the performance difference.

olooney
Sure, I was looking for something that is not redundant with django.core.files.uploadhandler.TemporaryFileUploadHandler
kmt
Alright. I'll take that as *the* answer. I was thinking along the same lines, but, being very new to Django, I wanted to check with someone. BTW, where did the ProgressBarUploadHandler come from? I guess, copy-paste from somewhere? Fair enough. I appreciate the effort of replying to this question.
kmt
Although, on a second though, I'd really want some more discussion on this. For example, should the stuff in django/core/files/storage.py be used? What is that about?
kmt