How to copy InMemoryUploadedFile object to disk
Asked Answered
P

6

51

I am trying to catch a file sent with form and perform some operations on it before it will be saved. So I need to create a copy of this file in temp directory, but I don't know how to reach it. Shutil's functions fail to copy this file, since there is no path to it. So is there a way to do this operation in some other way ?

My code :

    image = form.cleaned_data['image']
    temp = os.path.join(settings.PROJECT_PATH, 'tmp')
    sourceFile = image.name # without .name here it wasn't working either
    import shutil
    shutil.copy(sourceFile, temp)

Which raises :

Exception Type: IOError at /
Exception Value: (2, 'No such file or directory')

And the debug :

#  (..)\views.py in function

  67. sourceFile = image.name
  68. import shutil
  69. shutil.copy2(sourceFile, temp) ...

# (..)\Python26\lib\shutil.py in copy2

  92. """Copy data and all stat info ("cp -p src dst").
  93.
  94. The destination may be a directory.
  95.
  96. """
  97. if os.path.isdir(dst):
  98. dst = os.path.join(dst, os.path.basename(src))  
  99. copyfile(src, dst) ... 
 100. copystat(src, dst)
 101.

▼ Local vars
Variable    Value
dst     
u'(..)\\tmp\\myfile.JPG'
src     
u'myfile.JPG'
# (..)\Python26\lib\shutil.py in copyfile

  45. """Copy data from src to dst"""
  46. if _samefile(src, dst):
  47. raise Error, "`%s` and `%s` are the same file" % (src, dst)
  48.
  49. fsrc = None
  50. fdst = None
  51. try:
  52. fsrc = open(src, 'rb') ...
  53. fdst = open(dst, 'wb')
  54. copyfileobj(fsrc, fdst)
  55. finally:
  56. if fdst:
  57. fdst.close()
  58. if fsrc:

▼ Local vars
Variable    Value
dst     
u'(..)\\tmp\\myfile.JPG'
fdst    
None
fsrc    
None
src     
u'myfile.JPG'
Philosophical answered 13/9, 2010 at 16:37 Comment(0)
G
70

This is similar question, it might help.

import os
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile
from django.conf import settings

data = request.FILES['image'] # or self.files['image'] in your form

path = default_storage.save('tmp/somename.mp3', ContentFile(data.read()))
tmp_file = os.path.join(settings.MEDIA_ROOT, path)
Gomel answered 13/9, 2010 at 23:16 Comment(4)
Hii, i think hardcoded the "tmp/name.mp3" it will be an issue in cross platform path, I try to improve your code <pre> from django.conf import settings tmp = os.path.join(settings.MEDIA_ROOT, "tmp", data.name) path = default_storage.save(tmp, ContentFile(data.read()))</pre>Drafty
issues start when u upload many MB in one file. use buffering then. like data.read(BUF_SIZE) in while loop.Hazelton
Many/large is a relative measure and has a number of variables (like, amount of memory, disk size, network type/speed etc), but yes for large files you would probably write to disk in chunks.Gomel
You can avoid hardcoding the name using: FileField(upload_to='data-files-dir').generate_filename(None, data.name)Eastereasterday
T
32

As mentioned by @Sławomir Lenart, when uploading large files, you don't want to clog up system memory with a data.read().

From Django docs :

Looping over UploadedFile.chunks() instead of using read() ensures that large files don't overwhelm your system's memory

from django.core.files.storage import default_storage

filename = "whatever.xyz" # received file name
file_obj = request.data['file']

with default_storage.open('tmp/'+filename, 'wb+') as destination:
    for chunk in file_obj.chunks():
        destination.write(chunk)

This will save the file at MEDIA_ROOT/tmp/ as your default_storage will unless told otherwise.

Torus answered 12/5, 2015 at 15:41 Comment(3)
Like this I get: FileNotFoundError: [Errno 2] No such file or directory: '<storage>/<path>/<my image name>.png'. Ideas?Aphrodisia
@Aphrodisia the file is not where you think it is, and it's irrelevant to this code.Torus
@Emile Bergeron I am using django_storages as.default_storage. How can I write a file to the media root ?Gerhardt
M
8

Here is another way to do it with python's mkstemp:

### get the inmemory file
data = request.FILES.get('file') # get the file from the curl

### write the data to a temp file
tup = tempfile.mkstemp() # make a tmp file
f = os.fdopen(tup[0], 'w') # open the tmp file for writing
f.write(data.read()) # write the tmp file
f.close()

### return the path of the file
filepath = tup[1] # get the filepath
return filepath
Mycorrhiza answered 19/8, 2013 at 23:2 Comment(0)
F
5

Your best course of action is to write a custom Upload handler. See the docs . If you add a "file_complete" handler, you can access the file's content regardless of having a memory file or a temp path file. You can also use the "receive_data_chunck" method and write your copy within it.

Regards

Fillender answered 13/9, 2010 at 16:50 Comment(1)
i was using this for a while until when a duplicate name is uploaded and django automatically appends a suffix to make the filename unique. That renamed filename was not available at the upload handler level. Other than that this worked very cleanly.Krieg
C
5

Using default_storage (as mentioned in this answer) is not a good option (at least to me!). I would rather use FileSystemStorage directly.

from django.core.files.storage import FileSystemStorage
from django.http import HttpResponse


def simple_view(request):
    in_memory_file_obj = request.FILES["file"]
    FileSystemStorage(location="/tmp").save(in_memory_file_obj.name, in_memory_file_obj)
    return HttpResponse("Success")

Notes:

  • The location can be any value that suites for a directory path. It can even be a /tmp.

  • Solution tested with Django 4.1

Clustered answered 3/11, 2022 at 10:3 Comment(1)
Works like a charm. The location parameter is the folder path where you want to save the file.Too
B
3

This is how I tried to save the file locally

    file_object = request.FILES["document_file"]
    file_name = str(file_object)
    print(f'[INFO] File Name: {file_name}')
    with open(file_name, 'wb+') as f:
        for chunk in file_object.chunks():
            f.write(chunk)
Bandog answered 30/5, 2020 at 7:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.