40

I want to get the size of uploading image to control if it is greater than max file upload limit. I tried this one:

@app.route("/new/photo",methods=["POST"])
def newPhoto():

    form_photo = request.files['post-photo']
    print form_photo.content_length

It printed 0. What am I doing wrong? Should I find the size of this image from the temp path of it? Is there anything like PHP's $_FILES['foo']['size'] in Python?

Martin Thoma
  • 124,992
  • 159
  • 614
  • 958
saidozcan
  • 2,115
  • 9
  • 27
  • 39

6 Answers6

57

There are a few things to be aware of here - the content_length property will be the content length of the file upload as reported by the browser, but unfortunately many browsers dont send this, as noted in the docs and source.

As for your TypeError, the next thing to be aware of is that file uploads under 500KB are stored in memory as a StringIO object, rather than spooled to disk (see those docs again), so your stat call will fail.

MAX_CONTENT_LENGTH is the correct way to reject file uploads larger than you want, and if you need it, the only reliable way to determine the length of the data is to figure it out after you've handled the upload - either stat the file after you've .save()d it:

request.files['file'].save('/tmp/foo')
size = os.stat('/tmp/foo').st_size

Or if you're not using the disk (for example storing it in a database), count the bytes you've read:

blob = request.files['file'].read()
size = len(blob)

Though obviously be careful you're not reading too much data into memory if your MAX_CONTENT_LENGTH is very large

Community
  • 1
  • 1
DazWorrall
  • 13,682
  • 5
  • 43
  • 37
  • 4
    isn't http://stackoverflow.com/a/23601025/125507 better? just seek to the end of the file and you aren't using any memory? – endolith Jul 30 '15 at 01:43
  • *in the docs and source: https://github.com/pallets/werkzeug/blob/master/werkzeug/datastructures.py#L2676 – Khanh Le Oct 29 '18 at 04:19
42

If you don't want save the file to disk first, use the following code, this work on in-memory stream

import os

file = request.files['file']
# os.SEEK_END == 2
# seek() return the new absolute position
file_length = file.seek(0, os.SEEK_END)

# also can use tell() to get current position
# file_length = file.tell()

# seek back to start position of stream, 
# otherwise save() will write a 0 byte file
# os.SEEK_END == 0
file.seek(0, os.SEEK_SET)

otherwise, this will better

request.files['file'].save('/tmp/file')
file_length = os.stat('/tmp/file').st_size
Steely Wing
  • 16,239
  • 8
  • 58
  • 54
  • 2
    `file.seek(0, 2)` should do the same thing without importing `os`? http://stackoverflow.com/a/283719/125507 seek to 0 bytes from the end of the file – endolith Jul 30 '15 at 01:45
  • 5
    Warning: if you try to save the uploaded file after using the `file.seek(0, os.SEEK_END)` option above, your uploaded file will be corrupted, or rather it will be an empty file. Once you have obtained the file_length value, add the following: `file.seek(0, 0)` – DangerPaws Jan 05 '21 at 20:27
  • @DangerPaws my files are corrupted/empty but doing `file.seek(0, 0)` hasn't fixed it for me. – DillonB07 Aug 15 '22 at 17:15
21

The proper way to set a max file upload limit is via the MAX_CONTENT_LENGTH app configuration. For example, if you wanted to set an upload limit of 16 megabytes, you would do the following to your app configuration:

app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024

If the uploaded file is too large, Flask will automatically return status code 413 Request Entity Too Large - this should be handled on the client side.

Michael Pratt
  • 3,438
  • 1
  • 17
  • 31
  • 1
    Okay,this is a good way. Still isn't there any way to get the image size? – saidozcan Apr 02 '13 at 19:55
  • this is good unless your system is on a small drive and you want user to upload any file size unless it exceeds free space. in this case, use file.seek() suggested by Steely Wing – ierdna Apr 04 '16 at 01:22
  • changing app config may cause issues, if we want to have different limit file sizes on each request, like upload file = 2mb, upload photo = 500kb. – TomSawyer Sep 12 '19 at 07:59
  • App-scale configuration upload limit is not a "proper way" only something you should set ON TOP of everything. But then still you can have different file limitations for different file purposes. Also you may have dynamic storage placement/enlargement putting files on different drives depending on size... etc.. etc... – jave.web Oct 27 '20 at 15:11
7

The following section of the code should meet your purpose..


form_photo.seek(0,2)
size = form_photo.tell()

Aniket
  • 89
  • 1
  • 2
  • I tried this. while both .seek(0,2) and .tell() returned the file size (in bytes), once used, the file upload would not complete and save the file locally. (I get a filename with zero size saved.) .content_length, .content_type, .filename all continued to work after using .seek and .tell if I remove the .seek and .tell, the .save command returns to saving the file correctly. also : when I attempt a dir(form_photo) the methods .seek and .tell do not appear, but do appear when I do a dir(form_photo.stream) – CodingMatters Jul 14 '22 at 09:20
2

As someone else already suggested, you should use the

app.config['MAX_CONTENT_LENGTH'] 

to restrict file sizes. But Since you specifically want to find out the image size, you can do:

import os
photo_size = os.stat(request.files['post-photo']).st_size
print photo_size
codegeek
  • 32,236
  • 12
  • 63
  • 63
0

You can go by popen from os import

save it first

photo=request.files['post-photo']
photo.save('tmp')

now, just get the size

os.popen('ls -l tmp | cut -d " " -f5').read()
  • this in bytes
  • for Megabytes or Gigabytes, use the flag --b M or --b G
PYK
  • 3,674
  • 29
  • 17