Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

send_file() when called return text document instead of an image

Tags:

python

flask

web

I want to send an image file from server side to client side. I am using flask framework.

But the problem is whenever I call the route in which send_file() is, the response return is an File. When I click this file gedit opens it with nothing in that file. That means it must be text file written.

I referred the flask docs for send_file().

Here is what I am doing in the code:

@app.route('/try')
def trial():
    todown = 'https://igcdn-photos-e-a.akamaihd.net//hphotos-ak-xaf1//t51.2885-15//e35//12093691_1082288621781484_1524190206_n.jpg'  
    resp = requests.get(todown)
    return send_file(resp,mimetype="image/jpeg",attachment_filename="img.jpg",as_attachment=True)

Whenever I load localhost:5000/try a file is downloaded but not the image file that I want to download.

Error I am getting in my terminal is AttributeError: 'Response' object has no attribute 'read' error.

What must be the problem. Is anything missing in this above snippet?

like image 958
Suraj Palwe Avatar asked Nov 20 '15 03:11

Suraj Palwe


1 Answers

  1. resp is a requests.models.Response object, not string nor bytes:

    >>> import requests
    >>> todown = 'https://igcdn-photos-e-a.akamaihd.net//hphotos-ak-xaf1//t51.2885-15//e35//12093691_1082288621781484_1524190206_n.jpg'
    >>> resp = requests.get(todown)
    >>> resp
    <Response [200]>
    >>> type(resp)
    <class 'requests.models.Response'>
    
  2. Flask.send_file() sends a file.


So first at all you need use resp.content to get the content of the object, it'll return bytes object (and by the way, resp.text return string object. Always use .content if you're downloading an image, video, or other non-text things).

>>> import requests
>>> todown = 'https://igcdn-photos-e-a.akamaihd.net//hphotos-ak-xaf1//t51.2885-15//e35//12093691_1082288621781484_1524190206_n.jpg'
>>> resp = requests.get(todown)
>>> type(resp.content)
<class 'bytes'>

Please check the document for more details.


Then, because Flask.send_file() send a file, so you need write the image into a file before you send it.

But since you don't need use this image on your server anyway, I'd suggest use io.BytesIO in this case, then you don't need delete that image after you sent it. And note that use io.StringIO if you're sending a text file.

For example:

import requests
from io import BytesIO
from flask import Flask, send_file

app = Flask(__name__)

@app.route('/')
def tria():
    todown = 'https://igcdn-photos-e-a.akamaihd.net//hphotos-ak-xaf1//t51.2885-15//e35//12093691_1082288621781484_1524190206_n.jpg'
    resp = requests.get(todown)
    return send_file(BytesIO(resp.content), mimetype="image/jpeg", attachment_filename="img2.jpg", as_attachment=True)

app.run(port=80, debug=True)

However, if you want write the image into a file and send it then, sure you can also do it. We can use tempfile.NamedTemporaryFile() to create a tempfile instead of just create a file to avoid rewrite your important files.

From the document:

This function operates exactly as TemporaryFile() does, except that the file is guaranteed to have a visible name in the file system (on Unix, the directory entry is not unlinked).

That name can be retrieved from the name attribute of the file object. Whether the name can be used to open the file a second time, while the named temporary file is still open, varies across platforms (it can be so used on Unix; it cannot on Windows NT or later). If delete is true (the default), the file is deleted as soon as it is closed.

The returned object is always a file-like object whose file attribute is the underlying true file object. This file-like object can be used in a with statement, just like a normal file.

For example:

import tempfile
import requests
from flask import Flask, send_file

app = Flask(__name__)


@app.route('/')
def tria():
    todown = 'https://igcdn-photos-e-a.akamaihd.net//hphotos-ak-xaf1//t51.2885-15//e35//12093691_1082288621781484_1524190206_n.jpg'

    resp = requests.get(todown)

    with tempfile.NamedTemporaryFile() as f:  
        # create a file-like object use `NamedTemporaryFile()` and `with` 
        # as the basic usage which said in the document     

        f.write(resp.content)
        # write the content of the image into it

        return send_file(f.name, mimetype="image/jpeg",
                         attachment_filename="img2.jpg", as_attachment=True)                             
        # `f.name` is the temp file's filename

app.run(port=80, debug=True)
like image 133
Remi Crystal Avatar answered Sep 27 '22 20:09

Remi Crystal