在n8n自动化工作流中,经常需要与本地文件系统交互。无论是读取CSV数据、JSON配置文件,还是生成报告并保存到磁盘,Read/Write Files from Disk 节点都是不可或缺的工具。
本教程将带你从零开始,快速掌握这个强大节点的使用方法,并通过实战案例深入理解其应用场景。
Read/Write Files from Disk 是n8n内置的核心节点,允许你在自托管n8n实例上读写本地文件系统中的文件。
| 操作类型 | 功能说明 | 使用场景 |
|---|---|---|
| Read File(s) From Disk | 从服务器读取一个或多个文件 | 读取CSV、JSON、日志文件等 |
| Write File to Disk | 创建或更新服务器上的二进制文件 | 导出数据、生成报告、保存备份 |
参数1:File Selector(文件选择器)
* 匹配任何字符(不含路径分隔符)** 匹配任何字符(包含路径分隔符)? 单个字符匹配[abc] 匹配方括号内的任意字符示例路径:
# 单个文件
/home/user/documents/data.csv
# Docker容器内的绝对路径
/data/exports/report_2025.json
# Windows路径(Windows服务器)
C:/Users/admin/files/export.xlsx
# 使用通配符(读取所有CSV)
/data/files/*.csv
# 递归读取(所有子目录的CSV)
/data/files/**/*.csv可选配置项用于自定义输出格式:
| 选项名 | 说明 | 示例值 |
|---|---|---|
| File Extension | 指定输出文件的扩展名 | .csv, .json |
| File Name | 在输出中显示的文件名 | users_data |
| MIME Type | 文件的MIME类型 | text/csv, application/json |
| Put Output File in Field | 二进制数据存储的字段名 | fileData(默认data) |
参数1:File Path and Name(文件路径与名称)
参数2:Input Binary Field(输入二进制字段)
dataAppend(追加模式)
Manual Trigger
↓
Read/Write Files from Disk(读取操作)
↓
Extract from File(解析数据)
↓
Process/Transform Data
↓
输出结果Data Source(数据来源)
↓
Convert to File(转换为二进制)
↓
Read/Write Files from Disk(写入操作)
↓
确认保存完成步骤1 - 添加Read/Write Files节点
Read File(s) From Disk/data/files/customers.csv步骤2 - 添加Extract from File节点
data步骤3 - 后续处理
步骤1 - 准备数据
步骤2 - 添加Convert to File节点
步骤3 - 添加Write File节点
Write File to Disk/data/reports/report_{{$now.format('YYYY-MM-DD_HH-mm-ss')}}.jsondata./files/data.csv/home/user/files/data.csv| 错误 | 原因 | 解决方案 |
|---|---|---|
| 文件未找到 | 路径错误或权限不足 | 检查绝对路径,确保目录存在 |
| 权限被拒绝 | n8n没有读写权限 | 修改文件/目录权限或运行权限 |
| 路径相对 | 使用相对路径 | 改用绝对路径 |
| 容器找不到文件 | volume挂载错误 | 验证Docker compose volume配置 |
读取本地CSV文件,提取用户数据,验证邮箱格式,最后生成处理报告并保存。
{
"name": "CSV读取、处理和报告生成",
"nodes": [
{
"parameters": {},
"id": "6d89b9d1-6d2e-4f5f-8c8e-2a3b1c4d5e6f",
"name": "When clicking 'Test workflow'",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [50, 50]
},
{
"parameters": {
"fileSelector": "/data/files/users.csv",
"options": {}
},
"id": "7e9f0a1b-2c3d-4e5f-6g7h-3b4c5d6e7f8a",
"name": "Read Users CSV",
"type": "n8n-nodes-base.readWriteFile",
"typeVersion": 1,
"position": [250, 50]
},
{
"parameters": {
"options": {}
},
"id": "8f0a1b2c-3d4e-5f6g-7h8i-4c5d6e7f8g9a",
"name": "Extract from File",
"type": "n8n-nodes-base.extractFromFile",
"typeVersion": 1,
"position": [450, 50]
},
{
"parameters": {
"code": "return items.map(item => {\n const row = item.json;\n // 验证邮箱格式\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n const isValidEmail = emailRegex.test(row.email);\n \n return {\n json: {\n ...row,\n isValidEmail: isValidEmail,\n processedAt: new Date().toISOString()\n }\n };\n});"
},
"id": "9a1b2c3d-4e5f-6g7h-8i9j-5d6e7f8g9h0a",
"name": "Validate Email",
"type": "n8n-nodes-base.code",
"typeVersion": 1,
"position": [650, 50]
},
{
"parameters": {
"code": "const validUsers = items.filter(item => item.json.isValidEmail);\nconst invalidUsers = items.filter(item => !item.json.isValidEmail);\n\nconst report = {\n totalRecords: items.length,\n validCount: validUsers.length,\n invalidCount: invalidUsers.length,\n validPercentage: ((validUsers.length / items.length) * 100).toFixed(2) + '%',\n timestamp: new Date().toISOString(),\n summary: `处理${items.length}条记录,${validUsers.length}条有效,${invalidUsers.length}条无效`\n};\n\nreturn [{\n json: report\n}];"
},
"id": "0b2c3d4e-5f6g-7h8i-9j0k-6e7f8g9h0i1a",
"name": "Generate Report",
"type": "n8n-nodes-base.code",
"typeVersion": 1,
"position": [850, 50]
},
{
"parameters": {
"dataType": "json",
"json": "={{JSON.stringify($node[\"Generate Report\"].json)}}",
"options": {}
},
"id": "1c3d4e5f-6g7h-8i9j-0k1l-7f8g9h0i1j2a",
"name": "Convert to File",
"type": "n8n-nodes-base.convertToFile",
"typeVersion": 1,
"position": [1050, 50]
},
{
"parameters": {
"fileName": "/data/reports/process_report_{{$now.format('YYYY-MM-DD_HH-mm-ss')}}.json",
"inputBinaryField": "data",
"options": {}
},
"id": "2d4e5f6g-7h8i-9j0k-1l2m-8g9h0i1j2k3a",
"name": "Save Report",
"type": "n8n-nodes-base.readWriteFile",
"typeVersion": 1,
"position": [1250, 50]
}
],
"connections": {
"When clicking 'Test workflow'": {
"main": [[{ "node": "Read Users CSV", "type": "main", "index": 0 }]]
},
"Read Users CSV": {
"main": [[{ "node": "Extract from File", "type": "main", "index": 0 }]]
},
"Extract from File": {
"main": [[{ "node": "Validate Email", "type": "main", "index": 0 }]]
},
"Validate Email": {
"main": [[{ "node": "Generate Report", "type": "main", "index": 0 }]]
},
"Generate Report": {
"main": [[{ "node": "Convert to File", "type": "main", "index": 0 }]]
},
"Convert to File": {
"main": [[{ "node": "Save Report", "type": "main", "index": 0 }]]
}
}
}┌─────────────────────────────────────────────────────────────────┐
│ 手动触发工作流 │
└──────────────────────┬──────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 1️⃣ 读取 /data/files/users.csv │
│ (输出二进制文件数据) │
└──────────────────────┬──────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 2️⃣ 从文件提取数据 (Extract from File) │
│ (将CSV转换为JSON数据行) │
└──────────────────────┬──────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 3️⃣ 验证邮箱格式 (Code节点) │
│ (逐行检查邮箱合法性) │
└──────────────────────┬──────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 4️⃣ 生成处理报告 (Code节点) │
│ (统计有效/无效记录数) │
└──────────────────────┬──────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 5️⃣ 转换为文件格式 (Convert to File) │
│ (JSON转换为二进制) │
└──────────────────────┬──────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 6️⃣ 保存报告到磁盘 (Write File) │
│ (保存到 /data/reports/process_report_时间戳.json) │
└──────────────────────┬──────────────────────────────────────────┘
│
▼
✅ 完成!执行完工作流后,在 /data/reports/ 目录会生成类似的报告文件:
{
"totalRecords": 150,
"validCount": 147,
"invalidCount": 3,
"validPercentage": "98.00%",
"timestamp": "2025-12-02T09:30:45.123Z",
"summary": "处理150条记录,147条有效,3条无效"
}使用n8n表达式在文件名中插入动态内容:
// 当前日期和时间
/data/reports/report_{{$now.format('YYYY-MM-DD_HH-mm-ss')}}.json
// 包含工作流ID
/data/files/workflow_{{$workflow.id}}_data.csv
// 环境变量
/{{$env.FILE_OUTPUT_PATH}}/export_{{$now.format('YYYY-MM-DD')}}.json使用通配符一次性读取多个文件:
// 读取data目录下所有JSON文件
/data/files/*.json
// 递归读取所有子目录的CSV
/data/exports/**/*.csv
// 匹配特定模式的文件
/data/files/report_[0-9]*.xlsx// Code节点中的错误处理
try {
const fileContent = items[0].json;
// 处理文件内容
return [{ json: fileContent }];
} catch (error) {
return [{
json: {
error: true,
message: error.message,
file: 'unknown'
}
}];
}在Docker Compose中正确配置volume以支持文件操作:
services:
n8n:
image: n8nio/n8n
volumes:
- n8n_storage:/home/node/.n8n
- ./data:/data # 本地data目录映射到容器/data
- ./files:/files # 本地files目录映射到容器/files
environment:
- N8N_BASIC_AUTH_USER=admin
- N8N_BASIC_AUTH_PASSWORD=yourpassword
volumes:
n8n_storage:然后在工作流中使用:
/data/files/input.csv
/files/exports/output.json原因:
解决:
# SSH连接到服务器/容器验证路径
ls -la /data/files/
# 确保使用绝对路径,不是相对路径
# ❌ 错误:./files/data.csv
# ✅ 正确:/data/files/data.csv原因:
解决:
# 修改文件权限
chmod 644 /data/files/input.csv
chmod 755 /data/files/
# Docker容器内修改权限
docker exec -it n8n_container bash
chmod -R 755 /data/files/原因:
解决:
# 检查docker-compose.yml中的volume配置
volumes:
- ./data:/data # 本地./data映射到容器/data
# 验证挂载
docker inspect container_name | grep -A 5 MountsRead/Write Files from Disk 节点是n8n自托管实例中处理本地文件的核心工具。掌握以下要点,你就能轻松处理大多数文件操作场景:
✅ 关键要点回顾
[1] 官方文档: https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.readwritefile/
[2] n8n系列教程: https://www.undsky.com/blog/?category=n8n%E6%95%99%E7%A8%8B#