I have a React app working with a REST backend built in Python and Flask. I'm downloading data from a database and saving it as a CSV file through the browser. I have this working. What I don't understand, however, is why I had to go beyond the sources I've been reading and mash stuff up to get this to work. Why haven't I found this outlined better?
Some say all I have to do is set the response header with mimetype and Content-Disposition: attachment; filename=something.csv
:
Yet, this, alone, was only working with plain links, and not with fetch()
and authentication, so I had to go looking for ways to save client data to disk such as this:
So, my question is either:
It appears that the answer to the first question is that I can't modify request headers (to add the auth token) except through XHR type work. This appears to be answered (non-answered, really) here:
And, that for some reason, responses to XHR with Content-Disposition: attachment
are meaningless. Is that true? Is there not a more intrinsic way to manage requests like this in modern browsers?
I feel like I don't understand this enough and that bugs me.
Anyhow, here is the working code I am looking to simplify, if possible:
// https://stackoverflow.com/a/18197511/680464
download(filename, text) {
var pom = document.createElement('a');
pom.setAttribute('href', 'data:text/csv;charset=utf-8,' + encodeURIComponent(text));
pom.setAttribute('download', filename);
if (document.createEvent) {
var event = document.createEvent('MouseEvents');
event.initEvent('click', true, true);
pom.dispatchEvent(event);
}
else {
pom.click();
}
}
downloadReport(studyID) {
fetch(`/api/admin/studies/${studyID}/csv`
, {
headers: {
"Authorization": "Bearer " + this.props.authAPI.getToken()
, "Accept": "text/csv"
}
}
)
.then(this.checkStatus.bind(this))
.then((response) => response.text())
.then((responseText) => this.download(`study${studyID}.csv`, responseText))
.catch((error) => {
console.error(this.props.location.pathname, error)
})
}
@app.route("/api/admin/studies/<int:study_id>/csv", methods=["GET"])
@admin.login_required
def admin_get_csv(study_id):
test = [("1","2","3"),("4","5","6")]
def generate():
for row in test:
yield ",".join(row) + "\n"
return Response(
generate()
, mimetype="text/csv"
, headers={"Content-Disposition": "attachment; filename=study{0}.csv".format(study_id)}
)
In reference to this answer, you can use FileSaver or download.js libraries.
Example:
var saveAs = require('file-saver');
fetch('/download/urf/file', {
headers: {
'Content-Type': 'text/csv'
},
responseType: 'blob'
}).then(response => response.blob())
.then(blob => saveAs(blob, 'test.csv'));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With