Skip to content

Commit

Permalink
Merge pull request #931 from FlowFuse/add-file-upload-example
Browse files Browse the repository at this point in the history
add file upload template example
  • Loading branch information
joepavitt authored Jun 11, 2024
2 parents d55f121 + da193e4 commit 636ffdf
Show file tree
Hide file tree
Showing 2 changed files with 220 additions and 2 deletions.
122 changes: 122 additions & 0 deletions docs/examples/file-upload.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
[
{
"id": "ac0d56d388a4daa4",
"type": "ui-template",
"z": "977143edb097b685",
"group": "44010f4b04014d29",
"page": "",
"ui": "",
"name": "Binary File Upload",
"order": 0,
"width": 0,
"height": 0,
"head": "",
"format": "<template>\n <!-- Card for uploading binary file -->\n <v-card raised color=\"white\">\n <!-- Card Title -->\n <v-card-title>Upload binary file to Node-Red</v-card-title>\n <br>\n <v-card-text>\n <!-- File Input -->\n <v-file-input label=\"Click here to select a file\" show-size v-model=\"uploadFile\">\n </v-file-input>\n <!-- Progress Indicator -->\n <div>Progress: {{ progress }} bytes loaded</div>\n </v-card-text>\n <v-card-actions>\n <v-spacer></v-spacer>\n <!-- Upload Button -->\n <v-btn right @click=\"startUpload\">Upload File</v-btn>\n </v-card-actions>\n </v-card>\n</template>\n\n<script>\n export default {\n data() {\n return {\n uploadFile: null, // Holds the selected file\n progress: 0 // Progress indicator for file upload\n }\n },\n methods: {\n // Method triggered when Upload File button is clicked\n startUpload() {\n // Check if a file is selected\n if (!this.uploadFile) {\n console.warn('No file selected');\n return;\n }\n\n // Log the selected file information to console\n console.log('File selected:');\n console.log(this.uploadFile);\n\n // Create a FileReader instance to read the file\n const reader = new FileReader();\n\n // When the file is read, send it to Node-RED\n reader.onload = () => {\n // Prepare the payload to send\n const payload = {\n topic: 'upload', // Topic for Node-RED\n payload: this.uploadFile, // File content\n file: {\n name: this.uploadFile.name, // File name\n size: this.uploadFile.size, // File size\n type: this.uploadFile.type // File type\n }\n };\n \n // Send the payload to Node-RED (assuming 'send' method is defined)\n this.send(payload);\n };\n\n // Track progress of file reading\n reader.onprogress = (event) => {\n this.progress = event.loaded; // Update progress\n };\n\n // Read the file as an ArrayBuffer\n reader.readAsArrayBuffer(this.uploadFile);\n }\n },\n }\n</script>",
"storeOutMessages": true,
"passthru": true,
"resendOnRefresh": true,
"templateScope": "local",
"className": "",
"x": 390,
"y": 280,
"wires": [
[
"7c2cc0e98edc2535"
]
]
},
{
"id": "df9661cfece5ac80",
"type": "debug",
"z": "977143edb097b685",
"name": "debug 365",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 870,
"y": 280,
"wires": []
},
{
"id": "7c2cc0e98edc2535",
"type": "function",
"z": "977143edb097b685",
"name": "buffer2string",
"func": "msg.payload = msg.payload.toString()\nreturn msg;\n",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 630,
"y": 280,
"wires": [
[
"df9661cfece5ac80"
]
]
},
{
"id": "44010f4b04014d29",
"type": "ui-group",
"name": "Binary Upload",
"page": "dd8d2662278f4f72",
"width": "6",
"height": "1",
"order": -1,
"showTitle": true,
"className": "",
"visible": "true",
"disabled": "false"
},
{
"id": "dd8d2662278f4f72",
"type": "ui-page",
"name": "Data Entry New",
"ui": "cb79bc4520925e32",
"path": "/entry",
"icon": "note-multiple",
"layout": "grid",
"theme": "0d92c765bfad87e6",
"order": 1,
"className": "",
"visible": "true",
"disabled": "false"
},
{
"id": "cb79bc4520925e32",
"type": "ui-base",
"name": "My UI",
"path": "/dashboard",
"includeClientData": true,
"acceptsClientConfig": [
"ui-notification",
"ui-control"
],
"showPathInSidebar": false
},
{
"id": "0d92c765bfad87e6",
"type": "ui-theme",
"name": "Basic Blue Theme",
"colors": {
"surface": "#4d58ff",
"primary": "#0094ce",
"bgPage": "#eeeeee",
"groupBg": "#ffffff",
"groupOutline": "#cccccc"
},
"sizes": {
"pagePadding": "12px",
"groupGap": "12px",
"groupBorderRadius": "4px",
"widgetGap": "12px"
}
}
]
100 changes: 98 additions & 2 deletions docs/user/template-examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ description: Get inspired with a variety of UI template examples in Node-RED Das
import FlowViewer from '../components/FlowViewer.vue'
import ExampleFlowWorldmap from '../examples/template-worldmap.json'
import ExampleDataTable from '../examples/template-data-table.json'
import ExampleFileUpload from '../examples/file-upload.json'

const examples = ref({
'worldmap': ExampleFlowWorldmap,
'custom-data-table': ExampleDataTable
'custom-data-table': ExampleDataTable,
'file-upload': ExampleFileUpload
})
</script>

Expand Down Expand Up @@ -171,6 +173,100 @@ You can try out the above example with this flow:

<FlowViewer :flow="examples['custom-data-table']" height="200px"/>

### File Upload

When building applications with Node-RED, there's often a need to process files for analysis. In such cases, we require a file upload widget which is not currently available. Fortunately, we can achieve this easily using the `ui-template` widget and Vuetify JS components.

To do that we will use the [`v-file-input`](https://vuetifyjs.com/en/components/file-inputs/) component which provides the interface for uploading files.

```javascript
<template>
<!-- Card for uploading binary file -->
<v-card raised color="white">
<!-- Card Title -->
<v-card-title>Upload binary file to Node-Red</v-card-title>
<br>
<v-card-text>
<!-- File Input -->
<v-file-input label="Click here to select a file" show-size v-model="uploadFile">
</v-file-input>
<!-- Progress Indicator -->
<div>Progress: {{ progress }} bytes loaded</div>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<!-- Upload Button -->
<v-btn right @click="startUpload">Upload File</v-btn>
</v-card-actions>
</v-card>
</template>

<script>
export default {
data() {
return {
uploadFile: null, // Holds the selected file
progress: 0 // Progress indicator for file upload
}
},
methods: {
// Method triggered when Upload File button is clicked
startUpload() {
// Check if a file is selected
if (!this.uploadFile) {
console.warn('No file selected');
return;
}

// Log the selected file information to console
console.log('File selected:');
console.log(this.uploadFile);

// Create a FileReader instance to read the file
const reader = new FileReader();

// When the file is read, send it to Node-RED
reader.onload = () => {
// Prepare the payload to send
const payload = {
topic: 'upload', // Topic for Node-RED
payload: this.uploadFile, // File content
file: {
name: this.uploadFile.name, // File name
size: this.uploadFile.size, // File size
type: this.uploadFile.type // File type
}
};

// Send the payload to Node-RED (assuming 'send' method is defined)
this.send(payload);
};

// Track progress of file reading
reader.onprogress = (event) => {
this.progress = event.loaded; // Update progress
};

// Read the file as an ArrayBuffer
reader.readAsArrayBuffer(this.uploadFile);
}
},
}
</script>
```

To add a file upload component to your dashboard, insert the above Vue snippet into the ui-template widget. This snippet allows importing the file, and after clicking the upload button, it first tracks the progress of the file reading process and shows how much of the file is read. Once the file is fully loaded, it sends the message object to the subsequent node containing the file and related information for further processing. However, it's important to note that this sends the buffer arrays, which we are converting into strings using the change node in the following example.

<FlowViewer :flow="examples['file-upload']" height="200px"/>

Additionally, please be aware that when using this example with Node-RED default settings, it will only accept files up to 1MB. The dashboard uses `socket.io` for communication, which has a default limit of 1MB. You can increase this limit by changing the following value in the `settings.js` file. For more information, refer to the [Settings](https://dashboard.flowfuse.com/user/settings.html).

```javascript
dashboard: {
maxHttpBufferSize: 1e8 // size in bytes, example: 100 MB
}
```

### World Map

Node-RED has a very popular [worldmap node](https://flows.nodered.org/node/node-red-contrib-web-worldmap) which allows for intricate plotting of data on a globe. The node creates a new endpoint onto your web server at `/worldmap` (or whichever path you override the default with).
Expand Down Expand Up @@ -265,4 +361,4 @@ By default, the side navigation drawer that lists all of the pages in your Dashb
background-color: white;
color: black;
}
```
```

0 comments on commit 636ffdf

Please sign in to comment.