# Upload file from client to server filesystem using Dashboard 2.0 template

**URL:** https://discourse.nodered.org/t/upload-file-from-client-to-server-filesystem-using-dashboard-2-0-template/88465
**Category:** Dashboard
**Tags:** dashboard-2, ui-template
**Created:** [3 June 2024 11:45 UTC](https://discourse.nodered.org/t/upload-file-from-client-to-server-filesystem-using-dashboard-2-0-template/88465 "2024-06-03T11:45:32Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![sesenlik1](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/sesenlik1/32/78036_2.png) [@sesenlik1](https://discourse.nodered.org/u/sesenlik1)
#### Post date: [3 June 2024 11:45 UTC](https://discourse.nodered.org/t/upload-file-from-client-to-server-filesystem-using-dashboard-2-0-template/88465/1 "2024-06-03T11:45:32Z")

</div>

Hello all,

I'm trying to implement file upload feature on my Node-Red app. I used the flow shared by Nick O'Leary in an old GoogleForum post in this [link](https://groups.google.com/g/node-red/c/wVreRiBOoJ4).

The first solution that Nick shared actually works well but redirects dashboard page to a page displaying only OK. Nick also provided another solution with ajax calls to solve this issue but I could not make it work in my app. The reason that the fix didn't work for me, might have been caused because I used Dashboard 2.0.

How can I implement a file upload in Dashboard 2.0? I'm sharing the flow that worked with redirection below.

Thanks

```auto
[
    {
        "id": "61dfcbcdc7a29c9a",
        "type": "ui-template",
        "z": "c911c322aad92eb4",
        "group": "e6e8fb22f71d7392",
        "page": "",
        "ui": "",
        "name": "File Access",
        "order": 0,
        "width": 0,
        "height": 0,
        "head": "",
        "format": "<template>\n\n<form action=\"/file-upload\" method=\"post\" enctype=\"multipart/form-data\">\n <input type=\"file\" name=\"fileToUpload\" id=\"fileToUpload\">\n <input type=\"submit\" value=\"Upload Image\" name=\"submit\">\n</form>\n\n</template>\n\n\n\n<script>\n\n \n\n export default {\n data() {\n // define variables available component-wide\n // (in <template> and component functions)\n return {\n videoFacingMode: 'user', // 'user' or 'environment'\n vConstraints: {\n audio: false,\n video: {\n facingMode: 'user'\n }\n }\n }\n },\n watch: {\n // watch for any changes of \"count\"\n \n },\n computed: {\n // automatically compute this variable\n // whenever VueJS deems appropriate\n \n },\n methods: {\n // expose a method to our <template> and Vue Application\n increase: function () {\n this.count++\n },\n takePhoto: function () {\n // played the sound\n //snap.currentTime = 0;\n //snap.play();\n \n // take the data out of the canvas\n const canvas = document.querySelector('.photo');\n const data = canvas.toDataURL('image/jpeg');\n const link = document.createElement('a');\n const strip = document.querySelector('.strip');\n const photoContainer = document.getElementById(\"photoContainer\");\n \n\n\n link.href = data;\n link.setAttribute('download', 'Picture');\n link.innerHTML = `<img src=\"${data}\" alt=\"The picture\" />`;\n strip.insertBefore(link, strip.firstChild);\n //click method downloads the image\n link.click();\n const arr = photoContainer.innerText;\n arr.push(data);\n photoContainer.innerText = arr;\n console.log(photoContainer);\n },\n takePhotoAndDownload: function(){\n // played the sound\n //snap.currentTime = 0;\n //snap.play();\n \n // take the data out of the canvas\n const canvas = document.querySelector('.photo');\n const data = canvas.toDataURL('image/jpeg');\n const link = document.createElement('a');\n const strip = document.querySelector('.strip');\n const photoArr = document.querySelector('.photoArr');\n \n link.href = data;\n link.download = \"testfilename.jpeg\";\n //click method downloads the image\n link.click();\n photoArr.push(data);\n }\n\n },\n mounted() {\n // code here when the component is first loaded\n\n const video = document.querySelector('.player');\n const canvas = document.querySelector('.photo');\n const ctx = canvas.getContext('2d');\n const strip = document.querySelector('.strip');\n const snap = document.querySelector('.snap');\n\n const photoContainer = document.createElement(\"photoContainer\");\n const arr = [];\n photoContainer.innerText = arr;\n console.log(photoContainer);\n\n // Fix for iOS Safari from https://leemartin.dev/hello-webrtc-on-safari-11-e8bcb5335295\n video.setAttribute('autoplay', '');\n video.setAttribute('muted', '');\n video.setAttribute('playsinline', '')\n\n // const constraints = {\n // audio: false,\n // video: {\n // facingMode: 'environment' // 'user' or 'environment'\n // }\n // }\n var facingMode = 'user';\n this.videoFacingMode = \"user\";\n \n\n var constraints = {\n audio: false,\n video: {\n facingMode: this.videoFacingMode//'environment' // 'user' or 'environment'\n }\n }\n\n\n video.addEventListener('click', function() {\n if (facingMode == \"user\") {\n facingMode = \"environment\";\n } else {\n facingMode = \"user\";\n }\n \n constraints = {\n audio: false,\n video: {\n facingMode: facingMode\n }\n }\n \n navigator.mediaDevices.getUserMedia(constraints).then(function success(stream) {\n video.srcObject = stream;\n });\n });\n\n\n function getVideo() {\n \n navigator.mediaDevices.getUserMedia(constraints)\n .then(localMediaStream => {\n console.log(localMediaStream);\n \n // DEPRECIATION : \n // The following has been depreceated by major browsers as of Chrome and Firefox.\n // video.src = window.URL.createObjectURL(localMediaStream);\n // Please refer to these:\n // Deprecated - https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL\n // Newer Syntax - https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/srcObject\n console.dir(video);\n if ('srcObject' in video) {\n video.srcObject = localMediaStream;\n } else {\n video.src = URL.createObjectURL(localMediaStream);\n }\n // video.src = window.URL.createObjectURL(localMediaStream);\n video.play();\n })\n .catch(err => {\n console.error(`OH NO!!!!`, err);\n });\n }\n\n function paintToCanvas() {\n const width = video.videoWidth;\n const height = video.videoHeight;\n canvas.width = width;\n canvas.height = height;\n\n return setInterval(() => {\n ctx.drawImage(video, 0, 0, width, height);\n // take the pixels out\n // let pixels = ctx.getImageData(0, 0, width, height);\n // mess with them\n // pixels = redEffect(pixels);\n\n // pixels = rgbSplit(pixels);\n // ctx.globalAlpha = 0.8;\n\n // pixels = greenScreen(pixels);\n // put them back\n // ctx.putImageData(pixels, 0, 0);\n }, 16);\n }\n\n \n\n function redEffect(pixels) {\n for (let i = 0; i < pixels.data.length; i+=4) {\n pixels.data[i + 0] = pixels.data[i + 0] + 200; // RED\n pixels.data[i + 1] = pixels.data[i + 1] - 50; // GREEN\n pixels.data[i + 2] = pixels.data[i + 2] * 0.5; // Blue\n }\n return pixels;\n }\n\n function rgbSplit(pixels) {\n for (let i = 0; i < pixels.data.length; i+=4) {\n pixels.data[i - 150] = pixels.data[i + 0]; // RED\n pixels.data[i + 500] = pixels.data[i + 1]; // GREEN\n pixels.data[i - 550] = pixels.data[i + 2]; // Blue\n }\n return pixels;\n }\n\n function greenScreen(pixels) {\n const levels = {};\n\n document.querySelectorAll('.rgb input').forEach((input) => {\n levels[input.name] = input.value;\n });\n\n for (i = 0; i < pixels.data.length; i = i + 4) {\n red = pixels.data[i + 0];\n green = pixels.data[i + 1];\n blue = pixels.data[i + 2];\n alpha = pixels.data[i + 3];\n\n if (red >= levels.rmin\n && green >= levels.gmin\n && blue >= levels.bmin\n && red <= levels.rmax\n && green <= levels.gmax\n && blue <= levels.bmax) {\n // take it out!\n pixels.data[i + 3] = 0;\n }\n }\n\n return pixels;\n }\n\n function takePhoto() {\n // played the sound\n //snap.currentTime = 0;\n //snap.play();\n \n // take the data out of the canvas\n const data = canvas.toDataURL('image/jpeg');\n const link = document.createElement('a');\n link.href = data;\n link.setAttribute('download', 'picture');\n link.innerHTML = `<img src=\"${data}\" alt=\"The Picture\" />`;\n strip.insertBefore(link, strip.firstChild);\n }\n \n \n //getVideo();\n\n //video.addEventListener('canplay', paintToCanvas);\n },\n unmounted() {\n // code here when the component is removed from the Dashboard\n // i.e. when the user navigates away from the page\n }\n\n \n }\n</script>\n<style>\n /* define any styles here - supports raw CSS */\n .my-class {\n color: red;\n }\n html {\n box-sizing: border-box;\n }\n \n *, *:before, *:after {\n box-sizing: inherit;\n }\n \n html {\n font-size: 10px;\n background: #ff0000;\n }\n \n .photobooth {\n background: white;\n max-width: 150rem;\n margin: 2rem auto;\n border-radius: 2px;\n }\n \n /*clearfix*/\n .photobooth:after {\n content: '';\n display: block;\n clear: both;\n }\n \n .photo {\n width: 100%;\n float: left;\n }\n \n .player {\n position: absolute;\n top: 20px;\n right: 20px;\n width:200px;\n }\n \n /*\n Strip!\n */\n \n .strip {\n padding: 2rem;\n }\n \n .strip img {\n width: 100px;\n overflow-x: scroll;\n padding: 0.8rem 0.8rem 2.5rem 0.8rem;\n box-shadow: 0 0 3px rgba(0,0,0,0.2);\n background: white;\n }\n \n .strip a:nth-child(5n+1) img { transform: rotate(10deg); }\n .strip a:nth-child(5n+2) img { transform: rotate(-2deg); }\n .strip a:nth-child(5n+3) img { transform: rotate(8deg); }\n .strip a:nth-child(5n+4) img { transform: rotate(-11deg); }\n .strip a:nth-child(5n+5) img { transform: rotate(12deg); }\n</style>",
        "storeOutMessages": true,
        "passthru": true,
        "resendOnRefresh": true,
        "templateScope": "local",
        "className": "",
        "x": 290,
        "y": 860,
        "wires": [
            []
        ]
    },
    {
        "id": "90de0cfe.ca90a",
        "type": "http in",
        "z": "c911c322aad92eb4",
        "name": "",
        "url": "/file-upload",
        "method": "post",
        "upload": true,
        "swaggerDoc": "",
        "x": 314,
        "y": 932,
        "wires": [
            [
                "f44eee6a.fa1a8",
                "7835838e.a259cc"
            ]
        ]
    },
    {
        "id": "f44eee6a.fa1a8",
        "type": "debug",
        "z": "c911c322aad92eb4",
        "name": "",
        "active": true,
        "console": "false",
        "complete": "req.files",
        "x": 630,
        "y": 940,
        "wires": []
    },
    {
        "id": "7835838e.a259cc",
        "type": "function",
        "z": "c911c322aad92eb4",
        "name": "",
        "func": "msg.payload = \"OK\";\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 421,
        "y": 984,
        "wires": [
            [
                "b2930be5.501e38"
            ]
        ]
    },
    {
        "id": "b2930be5.501e38",
        "type": "http response",
        "z": "c911c322aad92eb4",
        "name": "",
        "statusCode": "",
        "headers": {},
        "x": 521,
        "y": 1024,
        "wires": []
    },
    {
        "id": "e6e8fb22f71d7392",
        "type": "ui-group",
        "name": "My Group",
        "page": "e2669f7ebdf6c137",
        "width": "6",
        "height": "1",
        "order": -1,
        "showTitle": true,
        "className": "",
        "visible": "true",
        "disabled": "false"
    },
    {
        "id": "e2669f7ebdf6c137",
        "type": "ui-page",
        "name": "Page N",
        "ui": "cf18f162c725eabc",
        "path": "/pageN",
        "icon": "home",
        "layout": "grid",
        "theme": "f33b4cc2097266ae",
        "order": -1,
        "className": "",
        "visible": "true",
        "disabled": "false"
    },
    {
        "id": "cf18f162c725eabc",
        "type": "ui-base",
        "name": "My Dashboard1",
        "path": "/dashboard",
        "includeClientData": true,
        "acceptsClientConfig": [
            "ui-notification",
            "ui-control"
        ],
        "showPathInSidebar": false,
        "navigationStyle": "default"
    },
    {
        "id": "f33b4cc2097266ae",
        "type": "ui-theme",
        "name": "Default Theme",
        "colors": {
            "surface": "#ffffff",
            "primary": "#0094CE",
            "bgPage": "#eeeeee",
            "groupBg": "#ffffff",
            "groupOutline": "#cccccc"
        },
        "sizes": {
            "pagePadding": "12px",
            "groupGap": "12px",
            "groupBorderRadius": "4px",
            "widgetGap": "12px"
        }
    }
]

```

---

<div class="post-metadata">

### Author: ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)
#### Post date: [3 June 2024 11:58 UTC](https://discourse.nodered.org/t/upload-file-from-client-to-server-filesystem-using-dashboard-2-0-template/88465/2 "2024-06-03T11:58:05Z")

</div>

See this recent example (and follow its links)

> [@Upload a file from the Dashboard](https://discourse.nodered.org/t/upload-a-file-from-the-dashboard/88357/2):
>
> See this thread. [Vuetify file upload - v-file-input - #2 by Steve-Mcl](https://discourse.nodered.org/t/vuetify-file-upload-v-file-input/86087/2)

---

<div class="post-metadata">

### Author: ![joepavitt](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/joepavitt/32/59722_2.png) [@joepavitt](https://discourse.nodered.org/u/joepavitt)
#### Post date: [11 June 2024 13:17 UTC](https://discourse.nodered.org/t/upload-file-from-client-to-server-filesystem-using-dashboard-2-0-template/88465/3 "2024-06-11T13:17:28Z")

</div>

Just to update here, we now have a file-upload example included in our documentation: [UI Template Examples | Node-RED Dashboard 2.0](https://dashboard.flowfuse.com/user/template-examples.html#file-upload)

---

<div class="post-metadata">

### Author: ![sesenlik1](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/sesenlik1/32/78036_2.png) [@sesenlik1](https://discourse.nodered.org/u/sesenlik1)
#### Post date: [11 June 2024 14:20 UTC](https://discourse.nodered.org/t/upload-file-from-client-to-server-filesystem-using-dashboard-2-0-template/88465/4 "2024-06-11T14:20:46Z")

</div>

Thank you Steve and Joe, I managed to implement file upload feature using your solutions.

---

<div class="post-metadata">

### Author: ![system](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/1X/d073cd938eafa2e558d7c2cd59003b3ef4963033.png) [@system](https://discourse.nodered.org/u/system)
#### Post date: [25 June 2024 14:21 UTC](https://discourse.nodered.org/t/upload-file-from-client-to-server-filesystem-using-dashboard-2-0-template/88465/5 "2024-06-25T14:21:32Z")

</div>

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