I have rows of dicts and want to make it available for download as a CSV file. This works perfectly in my local development environment. But when I use it on the server, the HTTP headers get prepended at the beginning of the file, and I can’t figure out why.
Sample code:
import csv
import io
import arrow
from sanic import response
from sanic.views import HTTPMethodView
class DownloadCSVView(HTTPMethodView):
async def get(self, request):
rows = [
dict(a=1, b=2, c=3),
dict(a=7, b=8, c=9),
dict(a=4, b=5, c=6),
]
fieldnames = rows[0].keys()
async def streaming_fn(response):
data = io.StringIO()
writer = csv.DictWriter(
data,
fieldnames=fieldnames,
extrasaction='ignore'
)
writer.writeheader()
for row in rows:
writer.writerow(row)
await response.write(data.getvalue())
content_type = 'text/csv'
return response.stream(
streaming_fn,
content_type=content_type,
headers={
'Content-Disposition': 'attachment; filename="foo-{}.csv";'.format(
arrow.utcnow().format('YYYY-MM-DD-HH-MM-SS')
),
'Content-Type': content_type,
}
)
CSV file in local:
a,b,c
1,2,3
7,8,9
4,5,6
CSV file when hosted:
HTTP/1.1 200 OK
Content-Disposition: attachment; filename="foo-2020-10-22-08-10-13.csv";
Content-Type: text/csv
Set-Cookie: session=2d43dxxxxxxxxxxxxxxxxxx; Path=/; HttpOnly; expires=Thu, 29-Oct-2020 08:09:06 GMT; Max-Age=604800; SameSite=None; Secure
Transfer-Encoding: chunked
Connection: close
511f
a,b,c
1,2,3
7,8,9
4,5,6
I can’t figure out if sanic is doing this or it has to do with the hosting / cloudflare / anything else that is not present in my local dev.
Also, if you have a suggestion on how to write this better, it would be great — I use StringIO
with csv
only because that is how I could get things working with the examples in the docs, but if there are in fact better ways to write a CSV file, I would love to know about it.
Any help would be greatly appreciated!