Python Flask send_file StringIO blank files

27,459

Solution 1

The issue here is that in Python 3 you need to use StringIO with csv.write and send_file requires BytesIO, so you have to do both.

@app.route('/test_download')
def test_download():
    row = ['hello', 'world']
    proxy = io.StringIO()
    writer = csv.writer(proxy)
    writer.writerow(row)
    # Creating the byteIO object from the StringIO Object
    mem = io.BytesIO()
    mem.write(proxy.getvalue().encode())
    # seeking was necessary. Python 3.5.2, Flask 0.12.2
    mem.seek(0)
    proxy.close()
    return send_file(
        mem,
        as_attachment=True,
        attachment_filename='test.csv',
        mimetype='text/csv'
    )

Solution 2

I guess you should write bytes.

from io import BytesIO    
from flask import Flask, send_file
app = Flask(__name__)
@app.route('/test_download', methods=['POST'])
def test_download():
    # Use BytesIO instead of StringIO here.
    buffer = BytesIO()
    buffer.write(b'jJust some letters.')
    # Or you can encode it to bytes.
    # buffer.write('Just some letters.'.encode('utf-8'))
    buffer.seek(0)
    return send_file(buffer, as_attachment=True,
                     attachment_filename='a_file.txt',
                     mimetype='text/csv')
if __name__ == '__main__':
    app.run(debug=True)

Solution 3

make_response

  • To get Flask to download a csv file to the user, we pass a csv string to the make_response function, which returns a Response object.
  • Then we add a Header which tells the browser to accept the file as a download.
  • The Mimetype also must be set to text/csv in order to get the web browser to save it in something other than an html document.
from flask import Flask, make_response  
app = Flask(__name__)
@app.route('/test_download', methods=['POST'])
def test_download():
    with StringIO() as buffer:
        # forming a StringIO object  
        buffer = StringIO()
        buffer.write('Just some letters.')
        # forming a Response object with Headers to return from flask 
        response = make_response(buffer.getvalue())
        response.headers['Content-Disposition'] = 'attachment; filename=namaste.csv'
        response.mimetype = 'text/csv'
        # return the Response object
        return response

P.S. It is preferred to use python's built-in csv library to deal with csv files

References

Namaste 🙏

Share:
27,459
Author by

Daniel Hitchcock

Updated on July 09, 2022

Comments

  • Daniel Hitchcock 4 months

    I'm using python 3.5 and flask 0.10.1 and liking it, but having a bit of trouble with send_file. I ultimately want to process a pandas dataframe (from Form data, which is unused in this example but necessary in the future) and send it to download as a csv (without a temp file). The best way to accomplish this I've seen is to us StringIO.

    Here is the code I'm attempting to use:

    @app.route('/test_download', methods = ['POST'])
    def test_download():
        buffer = StringIO()
        buffer.write('Just some letters.')
        buffer.seek(0)
        return send_file(buffer, as_attachment = True,\
        attachment_filename = 'a_file.txt', mimetype = 'text/csv')
    

    A file downloads with the proper name, however the file is completely blank.

    Any ideas? Issues with encoding? Has this been answered elsewhere? Thanks!

  • Daniel Hitchcock over 6 years
    Yes that works-- I just learned that flask on python 3 won't work with StringIO. Follow up question--do you know a way to convert a pandas dataframe into into a bytes csv for download?
  • lord63. j
    lord63. j over 6 years
    @DanielHitchcock Hi, you should provide a Minimal, Complete, and Verifiable example so I can reproduce this problem(like this question, but with the pandas dataframe example), I don't quiet familiar with pandas so my current answer is no. You may ask another question if necessary.
  • Sanchit
    Sanchit over 4 years
    This should be the accepted answer since it is more generic in sense of exporting/download CSV file by using send_file() method.
  • Charles L.
    Charles L. over 1 year
    In the encode I added "utf-8-sig", or you might want "utf-8". Also see stackoverflow.com/questions/2223882/…
  • Fips over 1 year
    The crucial part is "buffer.seek(0)" - I had a problem with an excel-file from pandas and it was solved with this line
  • Jacob Lester 4 months
    mem.seek(0) is needed otherwise get a "Failed - File incomple" error. Can confirm for Python 3.7.10 Flask 1.1.1