How to Force Download vs Display of File


Hello -
I have a template node with the following code:

<form action="/results.csv" method="download">
       <input type="submit" value="Download Data" 
         name="Submit" id="frm2_submit" />

It generates a button that downloads the file "results.csv" when browsing with chrome on a desktop. When I'm browsing with safari on mobile the same button opens the csv file in a new page. Is there a way to force the file to download regardless of the browser used to access the information?


Any ideas? I'm at a loss here.


How are you serving up /results.csv ? Is it a node-red flow?


Where do you expect it to download it to on a mobile device?

  • Thank you for pointing this out. I was expecting some type of download file dialog, but the mobile devices don't seem to work the same way. After opening the file I am able to save to a google drive, local filesystem, etc. So it appears the functionality is there, but the user will need to use the standard "share" dialog on their device.
  • Yes it's a node-red flow that creates the file on the raspberry pi file system. Once the file is created it's static in the html public directory.

Follow-up question. I've since updated the code so my file names are dynamically generated and would like the button to pull the most recent file. The file name is stored in a global variable "datafilename" and passed to the template node via msg.payload. How do I make the form action recognize a variable rather than a string? As the code is shown below the button tries to open the path /ui/msg.payload.



Just wanted to close the loop for anyone that finds this post in the future. I was able to get the files to be saved with unique file names and to pull the most recent with the click of the "Download Data" button.

The trick ended up being http end points. When the button is clicked it redirects the browser to the


page. When the files are downloaded they all share the same name on the client pc "test-results.csv" - but on the server the files are stored with their unique names.

[{"id":"29609c41.1d0fb4","type":"http in","z":"26d1e667.74bb0a","name":"","url":"/test-results.csv","method":"get","upload":false,"swaggerDoc":"","x":410,"y":120,"wires":[["1dacd39a.1491cc"]]},{"id":"e58a1ac.6d31fe8","type":"file in","z":"26d1e667.74bb0a","name":"","filename":"","format":"","sendError":true,"x":810,"y":120,"wires":[["70727af2.b39474"]]},{"id":"70727af2.b39474","type":"change","z":"26d1e667.74bb0a","name":"Set Headers","rules":[{"t":"set","p":"headers","pt":"msg","to":"{}","tot":"json"},{"t":"set","p":"headers.content-type","pt":"msg","to":"text/csv","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":970,"y":120,"wires":[["54282fe1.a3e85"]]},{"id":"54282fe1.a3e85","type":"http response","z":"26d1e667.74bb0a","name":"","statusCode":"","headers":{},"x":1130,"y":120,"wires":},{"id":"1dacd39a.1491cc","type":"function","z":"26d1e667.74bb0a","name":"Input filename","func":"msg.filename = String(global.get('datafilename'))\nreturn msg;","outputs":1,"noerr":0,"x":640,"y":120,"wires":[["e58a1ac.6d31fe8"]]}]