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:
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.
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?
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.
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.
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
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With