Search code examples
pythonfileurlflaskstringio

Open Image From Online, Save To Server Flask


I am using Flask to open an image from a URL.

file = cStringIO.StringIO(urllib.urlopen(URL).read())
img = Image.open(file)

I then want to take the image and save it to my site. When I do this, I get

Traceback (most recent call last):
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1836, in __call__
    return self.wsgi_app(environ, start_response)
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1820, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1403, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1477, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1381, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1475, in full_dispatch_request
    rv = self.dispatch_request()
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1461, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/Users/User/Desktop/Flask/fl.py", line 37, in index
    img.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
  File "/Library/Python/2.7/site-packages/PIL/Image.py", line 1648, in save
    raise KeyError(ext)  # unknown extension
KeyError: ''

Here is the code to save:

filename = secure_filename(img.filename)
img.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
print url_for('uploaded_file', filename=filename)

@app.route('/uploads/<filename>')
def uploaded_file(filename):
    return send_from_directory(app.config['UPLOAD_FOLDER'], filename)

Why is this happening? How can I fix this?


Solution

  • When saving in image from an object without a filename, like a StringIO object, you need to tell PIL what type of image it is:

    img.save(os.path.join(app.config['UPLOAD_FOLDER'], filename), format='PNG')
    

    Here I stated that the format is a PNG, but you'll need to introspect the Content-Type header from the response and see what type is from that. Map the content type to the appropriate PIL format, based on the header.

    You'll also need to come up with a better filename; img.filename is an empty string as you never gave img.open() a filename. Use the last component of URL instead for example; presumably that'll have a filename:

    formats = {
        'image/jpeg': 'JPEG',
        'image/png': 'PNG',
        'image/gif': 'GIF'
    }
    
    response = urllib.urlopen(URL)
    image_type = response.info().get('Content-Type')
    try:
        format = formats[image_type]
    except KeyError:
        raise ValueError('Not a supported image format')
    
    file = cStringIO.StringIO(response.read())
    img = Image.open(file)
    
    # ...
    
    filename = secure_filename(URL.rpartition('/')[-1])
    img.save(os.path.join(app.config['UPLOAD_FOLDER'], filename), format=format)