Loading and saving data using fetch () from authenticated REST

I have a React application working with a REST backend built into Python and Flask. I download data from a database and save it as a CSV file through a browser. It works for me. However, I don’t understand why I had to go beyond the sources that I read and put together things to make it work. Why didn't I find this outlined better?

Some say that all I need to do is set the response header using mimetype and Content-Disposition: attachment; filename=something.csv Content-Disposition: attachment; filename=something.csv :

However, this in itself worked only with simple links, not fetch() and authentication , so I had to look for ways to save client data to disk, for example:

So my question is:

  • Why should I do it this way? or
  • What am I missing - which is easier?

It seems that the answer to the first question is that I can not change the request headers (to add an authentication token), except through work like XHR. There seems to be an answer here (no answer):

And that for some reason, answers to XHR with Content-Disposition: attachment do not make sense. It's true? Is there a more modern way to manage such requests in modern browsers?

I feel I don’t understand this enough, and it bothers me.

In any case, here is the working code that I want to simplify, if possible:

JS (Reaction):

 // /questions/26832/using-html5javascript-to-generate-and-save-a-file/196541#196541 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) }) } 

Python (Flask):

 @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)} ) 
+8
source share
2 answers

Tell the browser how to handle your download using the DOM. Node.appendChild with the Node.appendChild tag and let the user click the link.

 <a href="/api/admin/studies/1/csv" download="study1.csv">Download CSV</a> 

What you do is upload the file, insert the already completed request into the anchor tag, and create a second request using pom.click() in your code.

Edit: I missed the authorization header. If you like this sentence, you can put the token in the query string.

+1
source

As for this answer , you can use the 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')); 
+1
source

All Articles