Unable to download files from the browser

Hi guys,

I'm trying download files from a directory listing on my node-red server.

I'm using node-red-contrib-fs-ops to display the directory content, but when I click on the generated links, I get the error Cannot GET /bioreact_20210920.csv where the file depends on the link clicked.

What am I missing?

[
    {
        "id": "53f68b03b7eb024a",
        "type": "http in",
        "z": "1c5e5e25.73f2f2",
        "name": "",
        "url": "/files",
        "method": "get",
        "upload": false,
        "swaggerDoc": "",
        "x": 140,
        "y": 480,
        "wires": [
            [
                "31b071d2319bb776"
            ]
        ]
    },
    {
        "id": "31b071d2319bb776",
        "type": "fs-ops-dir",
        "z": "1c5e5e25.73f2f2",
        "name": "",
        "path": "/home/pi/datalog",
        "pathType": "str",
        "filter": "*.*",
        "filterType": "str",
        "dir": "files",
        "dirType": "msg",
        "x": 300,
        "y": 480,
        "wires": [
            [
                "22bd3ccc46961e0b"
            ]
        ]
    },
    {
        "id": "22bd3ccc46961e0b",
        "type": "template",
        "z": "1c5e5e25.73f2f2",
        "name": "create dir index",
        "field": "payload",
        "fieldType": "msg",
        "format": "handlebars",
        "syntax": "mustache",
        "template": "<html>\n<body>\n<h1>Directory Index</h1>\n{{#files}}\n    <div><a href=\"{{.}}\">./{{.}}</a></div>\n{{/files}}\n</body>\n</html>",
        "output": "str",
        "x": 480,
        "y": 480,
        "wires": [
            [
                "2d01f5439eba6501"
            ]
        ]
    },
    {
        "id": "2d01f5439eba6501",
        "type": "http response",
        "z": "1c5e5e25.73f2f2",
        "name": "",
        "statusCode": "",
        "headers": {},
        "x": 650,
        "y": 480,
        "wires": []
    }
]

Thank you

I don't understand where the links are displayed. The flow you posted does not display anything, it just sends the links back to the client that has performed the get /files.

The http-in/http-out pair you have provided will only server the html from the template node, you will need to provide a similar pair with a wildcard that will actually serve the files themselves.

You probably also want to add a prefix to the filenames to make it easier to setup the wildcard http-in node to prevent clashes with anything else.

A second approach would be to edit your settings.js to set the httpStatic entry to point to /home/pi/datalog

Here is a demo on the flows site...

https://flows.nodered.org/flow/db68bd4934cf46f39e6e453a348bc419

@Steve-Mcl

In this example, the user needs to know what is the name of the file. In my case, there are new files generated everyday. I just want to list the directory content. The user will be able to select the files he wants.

@hardillb
I've already modified the settings.js to display the files of httpStatic value.

I'm new to NR and I'm not sure to understand what you're saying.

@Colin
I forgot to mention that I have set the httpStatic value to the folder I wanted to share. This is the result I get.
firefox_5NUH16t1LA

Then you will need to list the files in the directory & present them as download links.

Something like...

Result...

Flow...

[{"id":"f31a598d.9fd2c8","type":"function","z":"553814a2.1248ec","name":"Set base path","func":"//restrict to c:\\temp\\\nvar basePath = flow.get(\"basePath\");\nvar filename = msg.req.params.fn;\n\n\nif(filename.includes(\"..\\\\\")){\n    msg.payload = \"Illegal file path\";\n    msg.statusCode = 405;//not allowed\n    return [null, msg];//fire output 2\n} else if(filename.includes(\"../\")){\n    msg.payload = \"Illegal file path\";\n    msg.statusCode = 405;//not allowed\n    return [null, msg];//fire output 2\n} \n//TODO: add more checks\n\nmsg.filename = basePath + filename;\nreturn [msg, null];//fire output 1\n\n\n","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1300,"y":2380,"wires":[["34dc99e5.495466"],["98261154.3006"]]},{"id":"98261154.3006","type":"http response","z":"553814a2.1248ec","name":"","statusCode":"","headers":{},"x":1730,"y":2420,"wires":[]},{"id":"34dc99e5.495466","type":"file in","z":"553814a2.1248ec","name":"","filename":"","format":"","chunk":false,"sendError":false,"encoding":"none","x":1550,"y":2380,"wires":[["98261154.3006"]]},{"id":"38d65d59.1d8aa2","type":"catch","z":"553814a2.1248ec","name":"","scope":null,"uncaught":false,"x":1200,"y":2440,"wires":[["3b8014a.86ad8ec","5b18a8e7.fb8da8"]]},{"id":"3b8014a.86ad8ec","type":"function","z":"553814a2.1248ec","name":"Set 404","func":"msg.payload = msg.error;\nmsg.statusCode = 404;//resource not found\nreturn msg;","outputs":1,"noerr":0,"x":1560,"y":2440,"wires":[["98261154.3006"]]},{"id":"5b18a8e7.fb8da8","type":"debug","z":"553814a2.1248ec","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":1350,"y":2480,"wires":[]},{"id":"a8c2985e.d23ad8","type":"ui_template","z":"553814a2.1248ec","group":"dfb4a60f.d788f8","name":"ui_temlplate - present download links on dashboard","order":0,"width":"12","height":"10","format":"<div ng-bind-html=\"msg.payload\"></div>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":false,"templateScope":"local","className":"","x":1430,"y":2660,"wires":[[]]},{"id":"5de7cbb4.fa21a4","type":"comment","z":"553814a2.1248ec","name":"Create http endpoint <nodered>/files/xxx  where xxx is the file name to download","info":"","x":1320,"y":2340,"wires":[]},{"id":"67ecfa7f.3f0e24","type":"http in","z":"553814a2.1248ec","name":"","url":"/files/:fn","method":"get","upload":false,"swaggerDoc":"","x":1110,"y":2380,"wires":[["f31a598d.9fd2c8"]]},{"id":"b724cbe904e0a0a4","type":"fs-ops-dir","z":"553814a2.1248ec","name":"","path":"basePath","pathType":"flow","filter":"*","filterType":"str","dir":"files","dirType":"msg","x":1260,"y":2600,"wires":[["889579700842c8c5"]]},{"id":"5bb555b2920a103f","type":"inject","z":"553814a2.1248ec","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"c:\\temp\\","payloadType":"str","x":1120,"y":2260,"wires":[["26bc8255abc483dc"]]},{"id":"26bc8255abc483dc","type":"change","z":"553814a2.1248ec","name":"store flow.basePath","rules":[{"t":"set","p":"basePath","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1330,"y":2260,"wires":[[]]},{"id":"2184b260657996f6","type":"ui_ui_control","z":"553814a2.1248ec","name":"","events":"all","x":1100,"y":2600,"wires":[["b724cbe904e0a0a4"]]},{"id":"1b45551e6f9646f1","type":"comment","z":"553814a2.1248ec","name":"Setup basePath","info":"","x":1120,"y":2220,"wires":[]},{"id":"52034f887a419a0e","type":"comment","z":"553814a2.1248ec","name":"Get files in basePath and generate table","info":"","x":1190,"y":2560,"wires":[]},{"id":"d76416d92215b169","type":"inject","z":"553814a2.1248ec","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1100,"y":2660,"wires":[["b724cbe904e0a0a4"]]},{"id":"889579700842c8c5","type":"template","z":"553814a2.1248ec","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"\n<table>\n    {{#files}}\n    <tr>\n        <td> {{.}} </td>\n        <td> <a href=\"/files/{{{.}}}\">download</a><td>\n    </tr>\n    {{/files}}\n</table>\n\n","output":"str","x":1440,"y":2600,"wires":[["a8c2985e.d23ad8"]]},{"id":"dfb4a60f.d788f8","type":"ui_group","name":"Data Export","tab":"48418b79.0f5834","order":1,"disp":true,"width":"12"},{"id":"48418b79.0f5834","type":"ui_tab","name":"Dashboard","icon":"dashboard","order":1}]

PS: Uses fs-ops nodes for getting directly listing

1 Like

It works. Now I will need to learn how the nodes you added work.

Thanks!

PS : I've notice the lena.jpg file. Do you do computer vision?

Just a bit here and there (play time mostly with an idea in the back of my mind for a future node) :wink:

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.