Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alternative of send_file() in flask on Pythonanywhere?

I am new to python and still learning. I created a small python 3.6 Flask webapp on pythonanwhere and found out that send_file() is not working on pythonanywhere servers. I am actively looking for an alternative to download an excel file directly on the user machine. I also tried Response but it is not giving desired output. I read alot about it online and found that the send_file works fine if we set below

wsgi-disable-file-wrapper = True

However, i don't know where to set this as i couldn't find uWsgi.ini file where i could update this line.

Below are the methods which i tried but they failed, please help

SEND_FILE() configuration: ->>> Not running..

    output = BytesIO()
    writer = pd.ExcelWriter(output, engine='xlsxwriter')
    workbook = writer.book
    output.seek(0)
    return send_file(output,attachment_filename="testing.xlsx",as_attachment=True)

Output Error:

return environ.get('wsgi.file_wrapper', FileWrapper)(file, buffer_size)
SystemError: <built-in function uwsgi_sendfile> returned a result with an error set

With Response configuration:

writer = pd.ExcelWriter("abc.xlsx", engine='xlsxwriter')

return Response(writer,mimetype="text/csv",headers={"Content-disposition":"attachment; filename=myplot.csv"})

Output error:

Error running WSGI application
TypeError: '_XlsxWriter' object is not iterable
File "/home/hridesh1987/.virtualenvs/myproject/lib/python3.6/site-packages/werkzeug/wsgi.py", line 870, in __next__return self._next()
File "/home/hridesh1987/.virtualenvs/myproject/lib/python3.6/site-packages/werkzeug/wrappers.py", line 83, in _iter_encoded
for item in iterable:
like image 205
Hridesh Kohli Avatar asked Apr 29 '18 14:04

Hridesh Kohli


People also ask

How do I set up a flask application on PythonAnywhere?

Setting up Flask applications on PythonAnywhere. There are two main ways to set up a Flask application on PythonAnywhere: The first option works well if you're just playing around and want to throw something together from scratch. Go to the Web Tab and hit Add a new Web App, and choose Flask and the Python version you want.

Does flask's send_file require a bytesio object?

Here, it is stated that send_file requires a file pointer rather than a BytesIO object. A BytesIO object is actually supported by Flask's send_file method (plenty of examples & documentation online, and its working for me locally), but perhaps the poster meant that it is not supported by PythonAnywhere. Can I check what the true status for this is?

What is the latest version of flask for Python?

I am going to go with the latest version yet, which is Python 3.8 (Flask 1.1.1). In this step, you are just shown where the files for your flask app are going to be stored.

How do I grab a file from PythonAnywhere?

To grab a file (or directories) from your pythonanywhere account to your local machine, run the following from your local machine: If you have a another server somewhere, you could also rsync between that and PythonAnywhere.


2 Answers

I raised the same issue on the PythonAnywhere forums and they gave me this response. Kudos to 'glenn' of the PythonAnywhere staff.

Copy-pasted:

from io import BytesIO
from flask import Flask, Response
from werkzeug import FileWrapper

app = Flask(__name__)

@app.route('/')
def hello_world():
    b = BytesIO(b"blah blah blah")
    w = FileWrapper(b)
    return Response(w, mimetype="text/plain", direct_passthrough=True)

I adapted it slightly for my usage. I set the filename via the Content-Disposition header. I also had to tweak the FileWrapper import, and data is already a BytesIO object in my code:

from flask import Response
from werkzeug.wsgi import FileWrapper

def send_excel_file(data, filename):
    # See: https://www.pythonanywhere.com/forums/topic/13570/
    file_wrapper = FileWrapper(data)
    headers = {
        'Content-Disposition': 'attachment; filename="{}"'.format(filename)
    }
    response = Response(file_wrapper,
                        mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
                        direct_passthrough=True,
                        headers=headers)
    return response
like image 143
Mat Avatar answered Sep 27 '22 16:09

Mat


From the mentioned forum in Mat's answer I verified that:

[send_file()] does not work because the uWSGI file wrapper does not support file-like objects, only real files

...but applying Mat's solution still throws me a ValueError: I/O operation on closed file. Even with FileWrapper class.

This way is even simpler: If you use a file_pointer based on io eg. io.StringIO() you must use Response() instead. Not with a fp but sending the content directly. Based on your code:

with BytesIO() as output:
    writer = pd.ExcelWriter(output, engine='xlsxwriter')
    output.seek(0)
    headers = {"Content-disposition": "attachment; filename=testing.xlsx"}
    mimetype = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    return Response(output.read(), mimetype=mimetype, headers=headers)
like image 42
Rutrus Avatar answered Sep 27 '22 15:09

Rutrus