📋 サンプルフロー(クリックで展開)
※ 使用前に /tmp/uploads, /tmp/config.json, /tmp/data ディレクトリを作成してください
[
{
"id": "watch_sample_tab",
"type": "tab",
"label": "Watch サンプル",
"disabled": false,
"info": ""
},
{
"id": "watch_comment1",
"type": "comment",
"z": "watch_sample_tab",
"name": "━━━ パターン1: 基本的なファイル監視 ━━━",
"info": "",
"x": 220,
"y": 40,
"wires": []
},
{
"id": "watch_basic",
"type": "watch",
"z": "watch_sample_tab",
"name": "uploads監視",
"files": "/tmp/uploads",
"recursive": true,
"x": 140,
"y": 100,
"wires": [["watch_debug1"]]
},
{
"id": "watch_debug1",
"type": "debug",
"z": "watch_sample_tab",
"name": "変更検知",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "true",
"targetType": "full",
"statusVal": "file",
"statusType": "msg",
"x": 330,
"y": 100,
"wires": []
},
{
"id": "watch_comment2",
"type": "comment",
"z": "watch_sample_tab",
"name": "━━━ パターン2: ファイル変更 → 内容読み込み ━━━",
"info": "",
"x": 240,
"y": 180,
"wires": []
},
{
"id": "watch_read",
"type": "watch",
"z": "watch_sample_tab",
"name": "設定ファイル監視",
"files": "/tmp/config.json",
"recursive": false,
"x": 160,
"y": 240,
"wires": [["watch_file_read"]]
},
{
"id": "watch_file_read",
"type": "file in",
"z": "watch_sample_tab",
"name": "設定読み込み",
"filename": "",
"filenameType": "msg",
"format": "utf8",
"chunk": false,
"sendError": false,
"encoding": "utf8",
"allProps": false,
"x": 360,
"y": 240,
"wires": [["watch_json_parse"]]
},
{
"id": "watch_json_parse",
"type": "json",
"z": "watch_sample_tab",
"name": "JSON解析",
"property": "payload",
"action": "",
"pretty": false,
"x": 530,
"y": 240,
"wires": [["watch_debug2"]]
},
{
"id": "watch_debug2",
"type": "debug",
"z": "watch_sample_tab",
"name": "設定内容",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"x": 690,
"y": 240,
"wires": []
},
{
"id": "watch_comment3",
"type": "comment",
"z": "watch_sample_tab",
"name": "━━━ パターン3: ファイル種別による振り分け ━━━",
"info": "",
"x": 240,
"y": 320,
"wires": []
},
{
"id": "watch_filter",
"type": "watch",
"z": "watch_sample_tab",
"name": "データフォルダ監視",
"files": "/tmp/data",
"recursive": true,
"x": 170,
"y": 380,
"wires": [["watch_switch"]]
},
{
"id": "watch_switch",
"type": "switch",
"z": "watch_sample_tab",
"name": "拡張子で振り分け",
"property": "file",
"propertyType": "msg",
"rules": [
{"t": "regex", "v": "\\.csv$", "vt": "str", "case": true},
{"t": "regex", "v": "\\.(jpg|png|gif)$", "vt": "str", "case": true},
{"t": "regex", "v": "\\.json$", "vt": "str", "case": true},
{"t": "else"}
],
"checkall": "false",
"repair": false,
"outputs": 4,
"x": 370,
"y": 380,
"wires": [["watch_debug_csv"], ["watch_debug_img"], ["watch_debug_json"], ["watch_debug_other"]]
},
{
"id": "watch_debug_csv",
"type": "debug",
"z": "watch_sample_tab",
"name": "CSV処理",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "file",
"targetType": "msg",
"statusVal": "\"CSV: \" & file",
"statusType": "jsonata",
"x": 560,
"y": 340,
"wires": []
},
{
"id": "watch_debug_img",
"type": "debug",
"z": "watch_sample_tab",
"name": "画像処理",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "file",
"targetType": "msg",
"statusVal": "\"IMG: \" & file",
"statusType": "jsonata",
"x": 560,
"y": 380,
"wires": []
},
{
"id": "watch_debug_json",
"type": "debug",
"z": "watch_sample_tab",
"name": "JSON処理",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "file",
"targetType": "msg",
"statusVal": "\"JSON: \" & file",
"statusType": "jsonata",
"x": 560,
"y": 420,
"wires": []
},
{
"id": "watch_debug_other",
"type": "debug",
"z": "watch_sample_tab",
"name": "その他",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "file",
"targetType": "msg",
"statusVal": "\"OTHER: \" & file",
"statusType": "jsonata",
"x": 550,
"y": 460,
"wires": []
},
{
"id": "watch_comment4",
"type": "comment",
"z": "watch_sample_tab",
"name": "━━━ パターン4: ファイルサイズでフィルタ ━━━",
"info": "",
"x": 230,
"y": 540,
"wires": []
},
{
"id": "watch_size",
"type": "watch",
"z": "watch_sample_tab",
"name": "サイズ監視",
"files": "/tmp/uploads",
"recursive": false,
"x": 140,
"y": 600,
"wires": [["watch_size_check"]]
},
{
"id": "watch_size_check",
"type": "switch",
"z": "watch_sample_tab",
"name": "1MB以上?",
"property": "size",
"propertyType": "msg",
"rules": [
{"t": "gte", "v": "1048576", "vt": "num"},
{"t": "else"}
],
"checkall": "false",
"repair": false,
"outputs": 2,
"x": 310,
"y": 600,
"wires": [["watch_debug_large"], ["watch_debug_small"]]
},
{
"id": "watch_debug_large",
"type": "debug",
"z": "watch_sample_tab",
"name": "大きいファイル",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"statusVal": "\"大: \" & $string(size) & \" bytes\"",
"statusType": "jsonata",
"x": 500,
"y": 580,
"wires": []
},
{
"id": "watch_debug_small",
"type": "debug",
"z": "watch_sample_tab",
"name": "小さいファイル",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"statusVal": "\"小: \" & $string(size) & \" bytes\"",
"statusType": "jsonata",
"x": 500,
"y": 620,
"wires": []
},
{
"id": "watch_comment5",
"type": "comment",
"z": "watch_sample_tab",
"name": "━━━ テスト用: ファイル作成 ━━━",
"info": "Injectボタンを押すとテストファイルが作成されます",
"x": 200,
"y": 700,
"wires": []
},
{
"id": "watch_test_inject",
"type": "inject",
"z": "watch_sample_tab",
"name": "テストファイル作成",
"props": [{"p": "payload"}],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "テストデータ: ",
"payloadType": "str",
"x": 170,
"y": 760,
"wires": [["watch_test_func"]]
},
{
"id": "watch_test_func",
"type": "function",
"z": "watch_sample_tab",
"name": "タイムスタンプ追加",
"func": "msg.payload = msg.payload + new Date().toISOString();\nmsg.filename = \"/tmp/uploads/test_\" + Date.now() + \".txt\";\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 370,
"y": 760,
"wires": [["watch_test_write"]]
},
{
"id": "watch_test_write",
"type": "file",
"z": "watch_sample_tab",
"name": "ファイル書き込み",
"filename": "",
"filenameType": "msg",
"appendNewline": true,
"createDir": true,
"overwriteFile": "true",
"encoding": "utf8",
"x": 570,
"y": 760,
"wires": [["watch_test_debug"]]
},
{
"id": "watch_test_debug",
"type": "debug",
"z": "watch_sample_tab",
"name": "作成完了",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "filename",
"targetType": "msg",
"statusVal": "filename",
"statusType": "msg",
"x": 740,
"y": 760,
"wires": []
}
]
msg.payload = "/tmp/uploads/image.jpg"
msg.file = "image.jpg"
msg.type = "file"
msg.size = 1024
プロパティ: msg.file
ルール1: 正規表現 \.csv$ → CSV処理へ
ルール2: 正規表現 \.(jpg|png|gif)$ → 画像処理へ
ルール3: 正規表現 \.json$ → JSON処理へ
ルール4: その他 → その他処理へ
演習4: FTPアップロード処理システム上級
📝 課題:
FTPフォルダを監視し、CSVファイルがアップロードされたら内容を解析してJSON形式に変換、処理結果を保存してください。
🎯 要求仕様:
- Watch ノードでFTPフォルダを監視
- Delay ノードで書き込み完了を待機(2秒)
- Read File + CSV ノードでデータ読み込み・解析
- Function ノードでJSON変換
- 処理結果をJSONファイルとして保存
✅ 成功の条件:
/tmp/ftp_incoming に CSV ファイルをコピーすると 2 秒後に自動的に処理が開始される
- .csv 以外のファイル(例: .txt)を配置してもフローが反応しない
/tmp/ftp_processed/ フォルダに同名の .json ファイルが生成される
- 生成された JSON ファイルに
recordCount や processedAt フィールドが含まれている
💡 ヒント
なぜDelayが必要?
- FTPアップロード中にWatchが検知することがある
- 書き込み完了を待ってから読み込むと安全
CSVノードの設定:
✅ 解答例フロー
[
{"id": "ex4_tab", "type": "tab", "label": "演習4", "disabled": false, "info": ""},
{"id": "ex4_watch", "type": "watch", "name": "FTPフォルダ", "files": "/tmp/ftp_incoming", "recursive": false, "x": 140, "y": 100, "wires": [["ex4_filter"]], "z": "ex4_tab"},
{"id": "ex4_filter", "type": "switch", "name": "CSVのみ", "property": "file", "propertyType": "msg", "rules": [{"t": "regex", "v": "\\.csv$", "vt": "str", "case": true}], "checkall": "true", "repair": false, "outputs": 1, "x": 290, "y": 100, "wires": [["ex4_delay"]], "z": "ex4_tab"},
{"id": "ex4_delay", "type": "delay", "name": "2秒待機", "pauseType": "delay", "timeout": "2", "timeoutUnits": "seconds", "rate": "1", "nbRateUnits": "1", "rateUnits": "second", "randomFirst": "1", "randomLast": "5", "randomUnits": "seconds", "drop": false, "allowrate": false, "outputs": 1, "x": 420, "y": 100, "wires": [["ex4_read"]], "z": "ex4_tab"},
{"id": "ex4_read", "type": "file in", "name": "CSV読み込み", "filename": "", "filenameType": "msg", "format": "utf8", "chunk": false, "sendError": false, "encoding": "utf8", "allProps": false, "x": 560, "y": 100, "wires": [["ex4_csv"]], "z": "ex4_tab"},
{"id": "ex4_csv", "type": "csv", "name": "CSV解析", "sep": ",", "hdrin": true, "hdrout": "none", "multi": "mult", "ret": "\\n", "temp": "", "skip": "0", "strings": true, "include_empty_strings": false, "include_null_values": false, "x": 700, "y": 100, "wires": [["ex4_func"]], "z": "ex4_tab"},
{"id": "ex4_func", "type": "function", "name": "JSON変換", "func": "var baseName = msg.file.replace('.csv', '');\nvar result = {\n originalFile: msg.filename,\n processedAt: new Date().toISOString(),\n recordCount: msg.payload.length,\n data: msg.payload\n};\nmsg.payload = JSON.stringify(result, null, 2);\nmsg.outputFile = \"/tmp/ftp_processed/\" + baseName + \".json\";\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 150, "y": 180, "wires": [["ex4_write_json"]], "z": "ex4_tab"},
{"id": "ex4_write_json", "type": "file", "name": "JSON保存", "filename": "", "filenameType": "msg", "appendNewline": false, "createDir": true, "overwriteFile": "true", "encoding": "utf8", "x": 310, "y": 180, "wires": [["ex4_debug"]], "z": "ex4_tab"},
{"id": "ex4_debug", "type": "debug", "name": "処理完了", "active": true, "tosidebar": true, "console": false, "tostatus": true, "complete": "outputFile", "targetType": "msg", "statusVal": "\"処理完了: \" & file", "statusType": "jsonata", "x": 470, "y": 180, "wires": [], "z": "ex4_tab"}
]