Python Flask send_file StringIO blank files

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

  • https://matthewmoisen.com/blog/how-to-download-a-csv-file-in-flask/
  • https://www.geeksforgeeks.org/stringio-module-in-python/
  • https://docs.python.org/3/library/csv.html

Namaste 🙏


Use BytesIO to 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'Just 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,
        download_name='a_file.txt',
        mimetype='text/csv'
    )

Prior to Flask 2.0, download_name was called attachment_filename.


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,
        download_name='test.csv',
        mimetype='text/csv'
    )

Prior to Flask 2.0, download_name was called attachment_filename.