Forums

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

On localhost it works perfectly fine but on pythonanywhere it doesn't work anymore. When I press on a button which should download a word document i get this error message: SystemError: <built-in function uwsgi_sendfile> returned a result with an error set. views.py:

def downloadWord(request, pk):
order = Order.objects.get(pk=pk)
order_items = order.order_items.all()




date = f'{order.date}'
d =date[8:]
y = date[:4]
m = date[5:7]
date = f'{d}.{m}.{y}'


context={

    'order_items': order_items,
    'order': order,
    'date': date

}

byte_io = BytesIO()
tpl = DocxTemplate(os.path.join(BASE_DIR, 'media/word_documents/order.docx'))
tpl.render(context)
tpl.save(byte_io)
byte_io.seek(0)
data = dict()

return FileResponse(byte_io, as_attachment=True, filename=f'order_{order.title}.docx')

We have a help page that covers what is causing that and what you can do about it here: http://help.pythonanywhere.com/pages/FlaskSendFileBytesIO/

Thanks for the quick response! I have seen that page and I tried to it like that but then it says "AttributeError: 'FileWrapper' object has no attribute 'write'". Maybe I did something wrong (I am a newbie).

from werkzeug.wsgi import FileWrapper

def downloadWord(request, pk):
    order = Order.objects.get(pk=pk)
    order_items = order.order_items.all()




    date = f'{order.date}'
    d =date[8:]
    y = date[:4]
    m = date[5:7]
    date = f'{d}.{m}.{y}'


    context={

        'order_items': order_items,
        'order': order,
        'date': date

    }

    byte_io = BytesIO()
    byte_io = FileWrapper(byte_io)
    tpl = DocxTemplate(os.path.join(BASE_DIR, 'media/word_documents/order.docx'))
    tpl.render(context)
    tpl.save(byte_io)
    byte_io.seek(0)


    return FileResponse(byte_io, as_attachment=True, filename=f'order_{order.title}.docx')

Looks like the interface of DocxTemplate does not work with FileWrapper objects.

This is actually the last step to complete the website. Is there any way to disable the file wrapper in uWSGI ? It is really important for me to have this function. And can someone please tell me the reason why it works on localhost but not on pythonanywhere?

I suspect it works locally because you're using Flask's dev server (with app.run). The uWSGI system we use has a number of enhancements that are required to make it work in production environments -- unfortunately this bug is in one of those enhancements.

What happens if you change your code so that it writes to the byte_io like this;

tpl = DocxTemplate(os.path.join(BASE_DIR, 'media/word_documents/order.docx'))
tpl.render(context)

byte_io = BytesIO()
tpl.save(byte_io)
byte_io.seek(0)

return FileResponse(FileWrapper(byte_io), as_attachment=True, filename=f'order_{order.title}.docx')

When I do this, this happens https://ibb.co/bK6y32t

That's promising -- it looks like the data is being sent, but the instructions on how to render it that were send to the browser were wrong. Are you sure you included the as_attachment=True in the FileResponse constructor? It might also be worth setting the MIME type by adding content_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document".

Thank you very much!! You made my day.

No problem! So it's all working now?

Everything works fine. I recommended this site to my friends!

Thanks!

This solution ignores 'filename' setting for me. Filename is the name of function, that prepares download...

return FileResponse(FileWrapper(zip_buffer), as_attachment=True, filename='my_custom_filename.zip', content_type="application/zip")

where does FileResponse come from? is it a flask thing?

Something like this:

#Django 3.2
#[..]
from docx import Document
import zipfile
from django.http import FileResponse
from wsgiref.util import FileWrapper
import os
import io


def generate(request):
    #[..]
    # First File
    doc1 = Document(os.path.join(BASE_DIR, "something1.docx"))
    file1 = io.BytesIO()
    doc1.save(file1)
    file1.seek(0)

    # Second File
    doc2 = Document(os.path.join(BASE_DIR, "something2.docx"))
    file2 = io.BytesIO()
    doc2.save(file2)
    file2.seek(0)

    # ZIP
    zip_buffer = io.BytesIO()
    with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED, False) as zip_file:
        for filename, data in [('something1.docx', file1), ('something2.docx', file2)]:
            zip_file.writestr(filename, data.getvalue())
    zip_buffer.seek(0)

    return FileResponse(FileWrapper(zip_buffer), as_attachment=True, filename='my_custom_filename.zip', content_type="application/zip")

On local PC --> return FileResponse(zip_buffer, as_attachment=True, filename='my_custom_filename.zip') works great. Also on VM Linux with Gunicorn everything worked by default.

Seems, that this is fixed in uwsgi 2.0.19 --> https://github.com/unbit/uwsgi/issues/1126

What do you mean by the filename is "the name of function, that prepares download"?

Sorry - wrong assumption. First I thought, that filename is taken from the View function, that is used to prepare the download - in this case - 'generate'. Upon further investigation - I can confirm, that filename is set from form action, for example if form action is:

<form action="generate" method="post">

then name of offered filename to download will be 'generate'. If I set form action to:

<form action="my_custom_filename" method="post">

Then it works as expected and filename will be 'my_custom_filename'. So I guess this is the workaround I have to use...