Forums

Flask, send_file, BytesIO & uWSGI

I am using Flask's send_file method to allow a user to download a dynamically generated Excel file. It is working fine in my local development environment. When I try to run on PythonAnywhere, I get the below error:

SystemError: <built-in function uwsgi_sendfile> returned a result with an error set

It is the same as is discussed here, in length.

The same issue is discussed here, where your team indicate that send_file is not supported.

However, in this thread, your team state that send_file is supported.

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 guess I can't do the recommended fix of setting --wsgi-disable-file-wrapper as I can't modify the uWSGI config. Is there another way?

Thank you!

You're confusing the uwsgi send_file function (your first and second links) with the flask send_file function.

The flask send_file function sometimes tries to delegate to the wsgi file wrapper that is built in to the wsgi server. When it does that with a BytesIO object, it does not work because the uWSGI file wrapper does not support file-like objects, only real files. Here is a simple example Flask app that only serves a BytesIO object by bypassing the standard Flask delegation to the uWSGI file wrapper:

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)

Fantastic, thank you for clearing that up for me and for the quick response! I'll try it out as soon as I can.

I tried this and it worked. Thank you!

I had to tweak the FileWrapper import to:

from werkzeug.wsgi import FileWrapper

is there a way to set a filename and extension? This is great as I was banging my head against the wall wondering why i couldnt use send_file, this is ok but I really need a filename...

It looks like this StackOverflow question covers the headers you need to set to do that.

That works great, thanks!

Thanks, worked great! It is a nasty error though, took me hours to find this post which finally solved. It would be nice if the error log said something clarifying like 'Flask send_file() with file-like objects not supported in python-anywhere'

Agreed, that would certainly make life easier! Unfortunately none of our code gets executed as part of the code path when the problem happens, though, so we can't introduce a custom error. We're hoping to be able to fix the underlying problem soon, though.

We have written a help page to help with this in future http://help.pythonanywhere.com/pages/FlaskSendFileBytesIO/