0

I created a download button that downloads the file on click. The problem is when I click the download button, I'm able to see the content that I want to download by using Chrome inspect -> Network -> Response but it is not opening a window to save the file to my PC. For example, I'm trying to download text.txt which contains multiple lines of MY FILE string. I'm able to see it on Response tab but how can I download the .txt file.enter image description here

Relevant React Code:

<button onClick={(e) => downloadHandler(e)}>Download</button>
    let downloadHandler = (e) =>{
      const fileName = e.target.parentElement.id;
      fetch('http://localhost:3001/download',{
        method: 'post',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({id: fileName})
    })
  }

Relevant Backend Code:

const uploadFolder = `${__dirname}` + `\\uploads\\`;
app.post('/download', function(req, res){
    let fileName = req.body.id; // example output: 'dog.jpg'
    res.download(uploadFolder+fileName, fileName); // Set disposition and send it.
  });

The idea is that I will feed fileName to backend and then download it with res.download(uploadFolder+fileName, fileName); line. I think im suppose to use window.open('/download') but that just opens the homepage on a new tab or maybe I am just placing it wrong.

dewren99
  • 41
  • 8
  • I don't think `fetch()` will pop up the download window. `fetch()` gets data for your Javascript. I think you'd have to do `res.download()` in response to a browser form post (not done via Javascript) or in response to a URL in the browser bar or a clicked on link. When you do `fetch()`, the browser is not watching the response. The response goes to your Javascript. – jfriend00 Dec 20 '19 at 08:12
  • @jfriend00 when you are saying javascript, are you referring to React or Express or both? – dewren99 Dec 20 '19 at 08:28
  • I'm referring to client-side Javascript. When you make a `fetch()` request in Javascript, you're just getting data for your Javascript, not for the browser. For the browser to pay attention to the type of response, the response needs to be going into the browser window itself, via things like a typed URL, via a clicked on link or a browser submitted form response. – jfriend00 Dec 20 '19 at 08:30
  • @dewren99 Can you please try adding an error handler callback as => `res.download(uploadFolder+fileName, fileName, function(err){if (err){console.log(err)} else {res.download(uploadFolder+fileName, fileName);}} ` – David R Dec 20 '19 at 08:34
  • @DavidR I am not sure if I understood your request right, but when I replaced my ```res.download``` line with yours, I got this error ```Error: Can't set headers after they are sent.```. But I am still able to see the inside of ```.txt``` file on ```Response``` tab. – dewren99 Dec 20 '19 at 09:01
  • @dewren99 So, are you setting status somewhere after your `res.download..` ? Also are you using `res.end()` after that? – David R Dec 20 '19 at 09:49
  • @DavidR My react component that stores the ```downloadHandler``` starts like this ```export default function MyDropzone(){...```. I am not saving status after the call and I am not using ```res.end```. The codes posted above are the only parts that are related to the download functionality. – dewren99 Dec 20 '19 at 10:04
  • One final try, Can you please place those two statements, (i.e, `let fileName = ..... and res.download(uploa.......`) inside an if condition like this, `if (!res.headersSent) { // place those two statements here}` and check if it works? – David R Dec 20 '19 at 10:26
  • @DavidR Unfortunately, It did not affect the output. The same issue persists. – dewren99 Dec 20 '19 at 10:39
  • https://stackoverflow.com/questions/20176982/res-download-not-working-in-my-case – arunp9294 Dec 20 '19 at 12:32
  • @arunp9294 I got the idea of ```window.open('download)``` from that post, but I don't know how to apply it in my case. The second comment mentions ``opening a new window with my ajax request location`` but I'm storing files locally, so they are not on a server. – dewren99 Dec 20 '19 at 17:55

2 Answers2

2

Okay, I have managed to solve my issue. three main modifications were made to make this code and idea work.

  1. Changing the request from POST to GET. Another StackOverflow thread also mentions this.

  2. Using axios() instead of fetch().

  3. Creating Blob object from the res.download(filePath, fileName) response value.


Anyone else having this problem with the React Code part should check this Github link.

Final State of the React function posted in the question

    let downloadHandler = (e) =>{
      const fileName = e.target.parentElement.id;
    axios({
        method: 'get',
        url: 'http://localhost:3001/download/'+fileName,
        responseType: 'blob',
        headers: {},
        })
        .then((res) => {
            const url = window.URL.createObjectURL(new Blob([res.data]));
            const link = document.createElement('a');
            link.href = url;
            link.setAttribute('download', fileName);
            document.body.appendChild(link);
            link.click();
        })
        .catch((error) => {
            alert(error);
        })
  }

Final State of the backend code posted in the question

const uploadFolder = `${__dirname}` + `\\uploads\\`;
app.get('/download/:filename', function(req, res){
    let fileName = req.params.filename
    const filePath = uploadFolder+fileName;
    res.download(filePath, fileName);
  });
dewren99
  • 41
  • 8
0

My mistake was that i was navigating to http from https

Mufaddal Hamid
  • 281
  • 3
  • 12