Добавлена обработка бинарных данных и форматирование email для n8n workflow

- Добавлен рабочий код для подготовки бинарных данных и вложений для узла Send email
- Реализовано форматирование form_data в читаемый текст с разделами:
  * Личные данные (с автоматическим расчетом возраста, код документа, серия и номер)
  * Контактная информация
  * Страховщик (с телефоном страховой компании)
  * Банковские реквизиты (с получателем платежа)
  * Информация о страховом случае
  * Описание ситуации
- Добавлена документация по использованию и устранению неполадок
- Созданы альтернативные версии кода (простая версия, Function Node версия)
This commit is contained in:
Fedor
2025-12-26 14:51:58 +03:00
parent 1fdb244fd4
commit ea0edafba5
7 changed files with 1211 additions and 0 deletions

View File

@@ -0,0 +1,113 @@
// ============================================================================
// n8n Code Node: Подготовка бинарных данных для отправки email с вложениями
// ============================================================================
// Согласно документации n8n, поле "Attachments" в узле "Send email" ожидает:
// "Enter the name of the binary properties that contain data to add as an attachment"
// "Add multiple attachments by entering a comma-separated list of binary properties"
//
// ИСПОЛЬЗОВАНИЕ В УЗЛЕ "Send email":
// В поле "Attachments" используйте: {{ $json.attachment_field }}
//
// Это строка с именами бинарных свойств через запятую, например:
// "file_0,file_1,file_2,file_3"
//
// ⚠️ ВАЖНО:
// - Это должны быть ИМЕНА бинарных свойств (ключи из объекта binary), а не сами данные
// - Формат: строка через запятую, НЕ массив
// - Бинарные данные должны быть в объекте binary с соответствующими ключами
//
// ❌ НЕ используйте:
// - {{ $json.attachments }} (это массив объектов!)
// - {{ $json.attachment_keys }} (это массив строк!)
// - {{ Object.keys($binary) }} (это массив!)
// ============================================================================
// Проверяем наличие бинарных данных
// В n8n бинарные данные доступны через $binary или $input.item.binary
// Пробуем разные способы доступа к бинарным данным
let inputBinary = $binary;
if (!inputBinary || Object.keys(inputBinary).length === 0) {
// Пробуем через $input
try {
inputBinary = $input.item?.binary || $input?.binary || $binary;
} catch (e) {
inputBinary = $binary;
}
}
if (!inputBinary || Object.keys(inputBinary).length === 0) {
// Если бинарных данных нет, возвращаем данные без изменений
return [{
json: {
...$json,
attachment_field: '',
attachments: [],
attachment_keys: [],
error: 'No binary data found',
debug: {
binaryKeys: Object.keys(inputBinary || {}),
hasBinary: !!inputBinary,
hasDollarBinary: !!$binary,
dollarBinaryKeys: Object.keys($binary || {})
}
}
}];
}
// Создаем объект для бинарных данных и массивы для разных форматов
const binary = {};
const attachmentString = [];
const attachmentArray = []; // Массив объектов для поля Attachments
const attachmentKeys = []; // Простой массив ключей (альтернативный формат)
let i = 0;
// Преобразуем все бинарные данные в формат file_0, file_1, etc.
for (const key of Object.keys(inputBinary)) {
const newKey = `file_${i}`;
const fileData = inputBinary[key];
// Копируем бинарные данные с сохранением всех свойств
binary[newKey] = {
...fileData,
fileName: fileData.fileName || fileData.name || `file_${i}.pdf`,
name: fileData.name || fileData.fileName || `file_${i}.pdf`
};
attachmentString.push(newKey);
attachmentKeys.push(newKey);
// Создаем объект для поля Attachments узла "Send email"
// ВАЖНО: data должен быть строкой с ключом, а не объектом
attachmentArray.push({
name: fileData.fileName || fileData.name || `file_${i}.pdf`,
data: newKey // Строка-ключ для ссылки на бинарные данные
});
i++;
}
// ⚠️ КРИТИЧНО: Бинарные данные должны быть переданы в объекте binary
// Ключи в binary должны совпадать с именами в attachment_field!
// В n8n Code Node нужно явно указать binary в возвращаемом объекте
const result = {
json: {
...$json,
// ⚠️ ВАЖНО: Строка с именами бинарных свойств через запятую БЕЗ ПРОБЕЛОВ
// Это именно то, что ожидает узел "Send email" согласно документации:
// "Enter the name of the binary properties... comma-separated list of binary properties"
// Формат: "file_0,file_1,file_2" - имена ключей из объекта binary
// ⚠️ Убираем все пробелы, чтобы имена точно совпадали с ключами в binary
attachment_field: attachmentString.join(',').replace(/\s+/g, ''),
// Массив объектов (для других целей, если нужно)
attachments: attachmentArray,
// Простой массив ключей (для отладки или других целей)
attachment_keys: attachmentKeys
},
// ⚠️ КРИТИЧНО: Бинарные данные должны быть переданы в объекте binary
binary: binary
};
// Возвращаем данные с бинарными файлами
return [result];

View File

@@ -0,0 +1,45 @@
// ============================================================================
// n8n Code Node: Простая версия - передача бинарных данных как есть
// ============================================================================
// Используйте этот код, если бинарные данные уже приходят с именами file_0, file_1, etc.
// ============================================================================
// Получаем бинарные данные (пробуем разные способы доступа)
let inputBinary = $binary || $input?.item?.binary || $input?.binary || {};
// Получаем JSON данные
const jsonData = $json || $input?.item?.json || {};
// Проверяем наличие бинарных данных
if (!inputBinary || Object.keys(inputBinary).length === 0) {
return [{
json: {
...jsonData,
attachment_field: '',
error: 'No binary data found',
debug: {
hasBinary: !!inputBinary,
binaryKeys: Object.keys(inputBinary || {})
}
}
}];
}
// Получаем все ключи бинарных данных
const binaryKeys = Object.keys(inputBinary);
// Создаем строку с именами бинарных свойств через запятую
const attachmentField = binaryKeys.join(',');
// Возвращаем данные с бинарными файлами
// ⚠️ КРИТИЧНО: Бинарные данные должны быть переданы в объекте binary!
return [{
json: {
...jsonData,
attachment_field: attachmentField
},
// ⚠️ ВАЖНО: Передаем бинарные данные БЕЗ ИЗМЕНЕНИЙ
// Если они уже называются file_0, file_1, etc., они так и останутся
binary: inputBinary
}];

View File

@@ -0,0 +1,359 @@
// ============================================================================
// n8n Code Node: Подготовка бинарных данных для отправки email с вложениями
// РАБОЧАЯ ВЕРСИЯ - проверена и работает!
// ============================================================================
// Этот код успешно обрабатывает бинарные данные из webhook и подготавливает
// их для узла "Send email"
//
// ИСПОЛЬЗОВАНИЕ В УЗЛЕ "Send email":
// 1. В поле "Attachments" используйте: {{ $json.attachment_field }}
// 2. В поле "Text" используйте читаемый текст: {{ $json.email_body }}
// (вместо нечитаемого JSON {{ $('Webhook').item.json.body.form_data }})
// ============================================================================
const input = $input.all()?.[0] || {};
const binaries = input.binary || {};
const body = input.json?.body || {};
// Парсим form_data в объект
let formData = {};
let sub_dir = body.sub_dir || "";
if (typeof body.form_data === "string") {
try {
formData = JSON.parse(body.form_data);
sub_dir = formData?.sub_dir || sub_dir;
} catch (e) {
// Если не удалось распарсить, formData останется пустым объектом
}
}
// Функция для вычисления возраста на основе даты рождения
// Поддерживает форматы: DD-MM-YYYY, DD.MM.YYYY, YYYY-MM-DD
function calculateAge(birthday) {
if (!birthday) return null;
try {
// Парсим дату в формате DD-MM-YYYY или DD.MM.YYYY
const dateStr = String(birthday).trim().replace(/\./g, '-');
const parts = dateStr.split(/[-/]/);
if (parts.length !== 3) return null;
// Определяем формат: DD-MM-YYYY или YYYY-MM-DD
let day, month, year;
if (parts[0].length === 4) {
// YYYY-MM-DD
year = parseInt(parts[0]);
month = parseInt(parts[1]) - 1; // месяцы в JS начинаются с 0
day = parseInt(parts[2]);
} else {
// DD-MM-YYYY
day = parseInt(parts[0]);
month = parseInt(parts[1]) - 1; // месяцы в JS начинаются с 0
year = parseInt(parts[2]);
}
if (isNaN(year) || isNaN(month) || isNaN(day)) return null;
// Проверяем валидность даты
if (year < 1900 || year > 2100) return null;
if (month < 0 || month > 11) return null;
if (day < 1 || day > 31) return null;
const birthDate = new Date(year, month, day);
const today = new Date();
// Проверяем, что дата валидна
if (birthDate.getFullYear() !== year ||
birthDate.getMonth() !== month ||
birthDate.getDate() !== day) {
return null; // Некорректная дата (например, 31 февраля)
}
let age = today.getFullYear() - birthDate.getFullYear();
const monthDiff = today.getMonth() - birthDate.getMonth();
// Если день рождения еще не наступил в этом году, уменьшаем возраст на 1
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
age--;
}
return age >= 0 ? age : null;
} catch (e) {
return null;
}
}
// Функция для форматирования form_data в читаемый текст
function formatFormDataToText(data) {
if (!data || typeof data !== "object") {
return "Данные формы не найдены";
}
const lines = [];
// Маппинг полей на читаемые названия
const fieldLabels = {
formid: "ID формы",
ip: "IP адрес",
region: "Регион",
source: "Источник",
direction: "Направление",
inn: "ИНН",
ogrn: "ОГРН",
accountname: "Название организации",
address: "Адрес",
email: "Email",
phone: "Телефон",
mobile: "Мобильный телефон",
website: "Веб-сайт",
bank_name: "Название банка",
bank_id: "БИК банка",
lastname: "Фамилия",
firstname: "Имя",
secondname: "Отчество",
birthday: "Дата рождения",
description: "Описание",
mailingstreet: "Адрес проживания",
code: "Код",
sub_dir: "Подпапка",
// Поля с префиксом cf_ (custom fields)
cf_1885: "Номер полиса",
cf_1945: "Получатель платежа",
cf_1726: "Тип события",
cf_2566: "Дата события",
cf_2568: "Номер рейса",
cf_departure_flight: "Рейс отправления",
cf_departure_date: "Дата отправления",
cf_1899: "Код документа",
cf_1804: "Серия и номер документа",
cf_1909: "Страна",
cf_2502: "Количество",
cf_2446: "Доп. поле 1",
cf_1887: "Доп. поле 2",
cf_1889: "Доп. поле 3"
};
// Добавляем заголовок
lines.push("═══════════════════════════════════════");
lines.push("ОБРАЩЕНИЕ ЗА СТРАХОВОЙ ВЫПЛАТОЙ");
lines.push("═══════════════════════════════════════");
lines.push("");
// Личные данные
if (data.lastname || data.firstname || data.secondname || data.birthday || data.cf_1899 || data.cf_1804) {
lines.push("ЛИЧНЫЕ ДАННЫЕ:");
if (data.lastname) lines.push(` Фамилия: ${data.lastname}`);
if (data.firstname) lines.push(` Имя: ${data.firstname}`);
if (data.secondname) lines.push(` Отчество: ${data.secondname}`);
if (data.birthday) {
lines.push(` Дата рождения: ${data.birthday}`);
// Вычисляем возраст на основе даты рождения
const calculatedAge = calculateAge(data.birthday);
if (calculatedAge !== null) {
lines.push(` Возраст: ${calculatedAge}`);
}
// Если не удалось вычислить возраст, но дата есть - можно добавить сообщение
// (но обычно calculateAge должен работать для корректных дат)
}
if (data.cf_1899) lines.push(` Код документа: ${data.cf_1899}`);
if (data.cf_1804) lines.push(` Серия и номер документа: ${data.cf_1804}`);
lines.push("");
}
// Контактная информация (только личные контакты заявителя)
if (data.email || data.mobile || data.mailingstreet) {
lines.push("КОНТАКТНАЯ ИНФОРМАЦИЯ:");
if (data.email) lines.push(` Email: ${data.email}`);
if (data.mobile) lines.push(` Мобильный: ${data.mobile}`);
if (data.mailingstreet) lines.push(` Адрес: ${data.mailingstreet}`);
lines.push("");
}
// Данные страховщика
if (data.accountname || data.inn || data.ogrn || data.address || data.website || data.phone) {
lines.push("СТРАХОВЩИК:");
if (data.accountname) lines.push(` Название: ${data.accountname}`);
if (data.inn) lines.push(` ИНН: ${data.inn}`);
if (data.ogrn) lines.push(` ОГРН: ${data.ogrn}`);
if (data.address) lines.push(` Адрес: ${data.address}`);
if (data.website) lines.push(` Веб-сайт: ${data.website}`);
if (data.phone) lines.push(` Телефон: ${data.phone}`);
lines.push("");
}
// Банковские реквизиты
if (data.bank_name || data.bank_id || data.cf_1945) {
lines.push("БАНКОВСКИЕ РЕКВИЗИТЫ:");
if (data.cf_1945) lines.push(` Получатель платежа: ${data.cf_1945}`);
if (data.bank_name) lines.push(` Банк: ${data.bank_name}`);
if (data.bank_id) lines.push(` БИК: ${data.bank_id}`);
lines.push("");
}
// Информация о страховом случае
if (data.cf_1885 || data.cf_1726 || data.cf_2566 || data.cf_2568) {
lines.push("ИНФОРМАЦИЯ О СТРАХОВОМ СЛУЧАЕ:");
if (data.cf_1885) lines.push(` Номер полиса: ${data.cf_1885}`);
if (data.cf_1726) lines.push(` Тип события: ${data.cf_1726}`);
if (data.cf_2566) lines.push(` Дата события: ${data.cf_2566}`);
if (data.cf_2568) lines.push(` Номер рейса: ${data.cf_2568}`);
if (data.cf_departure_flight) lines.push(` Рейс отправления: ${data.cf_departure_flight}`);
if (data.cf_departure_date) lines.push(` Дата отправления: ${data.cf_departure_date}`);
lines.push("");
}
// Описание
if (data.description) {
lines.push("ОПИСАНИЕ СИТУАЦИИ:");
lines.push(` ${data.description}`);
lines.push("");
}
// Дополнительная информация
const additionalFields = [];
for (const [key, value] of Object.entries(data)) {
// Пропускаем уже обработанные поля и пустые значения
if (key === "formid" || key === "sub_dir" || key === "code" ||
key.startsWith("cf_") && fieldLabels[key] ||
!value || value === "" || value === "0") {
continue;
}
// Добавляем необработанные поля
if (!fieldLabels[key] && !["lastname", "firstname", "secondname", "birthday",
"email", "phone", "mobile", "mailingstreet", "accountname", "inn", "ogrn",
"address", "website", "bank_name", "bank_id", "description",
"cf_1885", "cf_1945", "cf_1726", "cf_2566", "cf_2568", "cf_departure_flight",
"cf_departure_date", "cf_1899", "cf_1804", "cf_1909", "cf_2502"].includes(key)) {
additionalFields.push({ key, value });
}
}
if (additionalFields.length > 0) {
lines.push("ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ:");
for (const { key, value } of additionalFields) {
lines.push(` ${key}: ${value}`);
}
lines.push("");
}
// Техническая информация
lines.push("═══════════════════════════════════════");
lines.push("ТЕХНИЧЕСКАЯ ИНФОРМАЦИЯ:");
if (data.ip) lines.push(` IP адрес: ${data.ip}`);
if (data.region) lines.push(` Регион: ${data.region}`);
if (data.formid) lines.push(` ID формы: ${data.formid}`);
if (data.code) lines.push(` Код: ${data.code}`);
lines.push("═══════════════════════════════════════");
return lines.join("\n");
}
// Форматируем form_data в читаемый текст
const emailBodyText = formatFormDataToText(formData);
// 1) Парсим files_meta[group][prop] -> metaByGroup[group][prop]
// Преобразуем структуру типа:
// files_meta[supporting_documents][description] = "supporting_documents"
// files_meta[supporting_documents][count] = "2"
// В объект:
// metaByGroup[supporting_documents] = { description: "...", count: "2" }
const metaByGroup = {};
for (const [k, v] of Object.entries(body)) {
const m = k.match(/^files_meta\[([^\]]+)\]\[([^\]]+)\]$/);
if (!m) continue;
const group = m[1];
const prop = m[2];
metaByGroup[group] ??= {};
metaByGroup[group][prop] = v;
}
// Проверяем наличие бинарных данных
if (!binaries || Object.keys(binaries).length === 0) {
return [{
json: { has_files: false, message: "no files uploaded", sub_dir },
binary: {}
}];
}
// Вспомогательная функция для получения расширения файла
const getExt = (name) =>
name && name.includes(".") ? "." + name.split(".").pop().toLowerCase() : "";
// 2) Парсер бинарного поля
// Парсит имена полей типа:
// files[supporting_documents][0] -> { group: "supporting_documents", index: 0 }
// files[insurance_policy] -> { group: "insurance_policy", index: null }
function parseBinaryField(field) {
const m = field.match(/^files\[([^\]]+)\]\[(\d+)\]$/);
if (m) return { group: m[1], index: Number(m[2]) };
const m2 = field.match(/^files\[([^\]]+)\]$/);
if (m2) return { group: m2[1], index: null };
return { group: null, index: null };
}
// 3) Собираем ВСЁ В ОДИН ITEM
// Преобразуем бинарные данные в формат file_0, file_1, etc.
const outBinary = {};
const attachments = [];
let i = 0;
for (const [field, file] of Object.entries(binaries)) {
const { group, index } = parseBinaryField(field);
if (!group) continue;
if (!file?.data) continue; // 🔥 защита от пустого бинаря
const meta = metaByGroup[group] || {};
const origName = file.fileName || "";
const ext = getExt(origName);
// Создаем новый ключ для бинарных данных
const outKey = `file_${i}`;
// Создаем имя файла: group_index.ext или group.ext
const outName = `${group}${index !== null ? "_" + index : ""}${ext}`;
// Сохраняем бинарные данные с новым ключом
outBinary[outKey] = {
...file,
fileName: outName
};
// Сохраняем метаданные о файле
attachments.push({
key: outKey, // file_0, file_1, etc.
group, // supporting_documents, insurance_policy, etc.
description: meta.description || "",
original_field: meta.original_field || "",
index,
original_file_name: origName,
file_name: outName,
size: file.fileSize,
mime: file.mimeType
});
i++;
}
// Возвращаем данные с бинарными файлами
return [{
json: {
has_files: attachments.length > 0,
sub_dir,
attachments, // Массив с метаданными о файлах
attachment_keys: attachments.map(a => a.key), // Массив ключей: ["file_0", "file_1", ...]
attachment_field: attachments.map(a => a.key).join(","), // Строка для узла "Send email": "file_0,file_1,file_2"
// Форматированные данные для email
email_body: emailBodyText, // Читаемый текст для email
form_data_parsed: formData, // Распарсенные данные формы (JSON объект)
form_data_raw: body.form_data // Исходная JSON строка (для совместимости)
},
// ⚠️ КРИТИЧНО: Бинарные данные должны быть переданы в объекте binary
// Ключи в binary должны совпадать с именами в attachment_field!
binary: outBinary
}];

View File

@@ -0,0 +1,220 @@
# Исправление проблемы с вложениями в узле "Send email" n8n
## Проблема
Узел "Send email" выдает ошибку:
```
The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received undefined
```
**Причина:** В поле `Attachments` передается строка с именами файлов (`file_0,file_1,file_2...`), но узел ожидает бинарные данные файлов.
---
## Решение
### Вариант 1: Использование Code Node для подготовки данных (Рекомендуется)
#### Шаг 1: Добавьте Code Node перед узлом "Send email"
**Код для Code Node:**
```javascript
// Создаем объект для бинарных данных и массив имен файлов
const binary = {};
const attachmentString = [];
const attachmentArray = []; // Массив объектов для поля Attachments
let i = 0;
// Преобразуем все бинарные данные в формат file_0, file_1, etc.
for (const key of Object.keys($binary)) {
const newKey = `file_${i}`;
binary[newKey] = $binary[key];
attachmentString.push(newKey);
// Создаем объект для поля Attachments узла "Send email"
attachmentArray.push({
name: $binary[key].fileName || $binary[key].name || `file_${i}.pdf`,
data: newKey // Ссылка на бинарные данные
});
i++;
}
// Возвращаем данные с бинарными файлами
return [{
json: {
...$json,
attachment_field: attachmentString.join(','),
attachments: attachmentArray
},
binary: binary
}];
```
#### Шаг 2: Настройте узел "Send email"
**Согласно официальной документации n8n:**
> "Enter the name of the binary properties that contain data to add as an attachment. Add multiple attachments by entering a comma-separated list of binary properties."
**В поле "Attachments" используйте:**
**✅ ПРАВИЛЬНО (РЕКОМЕНДУЕТСЯ):**
```
{{ $json.attachment_field }}
```
Это строка с именами бинарных свойств через запятую: `"file_0,file_1,file_2,file_3"`
**Важно понимать:**
- Это имена ключей из объекта `binary`, а не сами данные
- Формат: строка через запятую, НЕ массив
- Узел "Send email" найдет бинарные данные по этим именам в объекте `binary`
**❌ НЕПРАВИЛЬНО - НЕ ИСПОЛЬЗУЙТЕ:**
1. **Массив объектов:**
```javascript
{{ $json.attachments }} // ❌ Это массив объектов!
```
2. **Массив строк:**
```javascript
{{ $json.attachment_keys }} // ❌ Это массив строк!
{{ Object.keys($binary) }} // ❌ Это тоже массив!
```
**⚠️ КРИТИЧНО:**
- Используйте **СТРОКУ** `{{ $json.attachment_field }}`
- Убедитесь, что бинарные данные переданы в объекте `binary` выходного значения Code Node
- Ключи в объекте `binary` должны точно совпадать с именами в строке `attachment_field`!
- Например: если `attachment_field = "file_0,file_1"`, то в `binary` должны быть ключи `file_0` и `file_1`
---
### Вариант 2: Прямое использование бинарных данных (Проще)
Если бинарные данные уже есть в правильном формате, просто используйте их напрямую:
**В поле Attachments узла "Send email":**
```javascript
{{
Object.keys($binary).map(key => {
const file = $binary[key];
return {
name: file.fileName || file.name || key,
data: file.data || file
};
})
}}
```
---
### Вариант 3: Использование Function Node (Если Code Node не работает)
Если Code Node не передает бинарные данные правильно, используйте Function Node:
```javascript
const items = $input.all();
const result = items.map(item => {
const binary = {};
const attachmentArray = [];
let i = 0;
for (const key of Object.keys(item.binary || {})) {
const newKey = `file_${i}`;
binary[newKey] = item.binary[key];
attachmentArray.push({
name: item.binary[key].fileName || item.binary[key].name || `file_${i}.pdf`,
data: newKey
});
i++;
}
return {
json: {
...item.json,
attachments: attachmentArray,
attachment_field: Object.keys(binary).join(',')
},
binary: binary
};
});
return result;
```
---
## Проверка работы
1. **Убедитесь, что бинарные данные передаются:**
- В Code/Function Node должен быть объект `binary` в возвращаемом значении
- Проверьте в OUTPUT панели, что бинарные данные присутствуют
2. **Проверьте формат данных:**
- В поле Attachments должен быть массив объектов или правильная ссылка на бинарные данные
- Каждый объект должен содержать `name` и `data`
3. **Тестирование:**
- Запустите workflow в тестовом режиме
- Проверьте, что email отправляется с вложениями
---
## Частые ошибки
1. **Ошибка: "Received undefined"**
- **Причина:** Бинарные данные не переданы в выходном объекте
- **Решение:** Убедитесь, что объект `binary` включен в возвращаемое значение Code/Function Node
2. **Ошибка: "options.attachments.split is not a function" или "property.split is not a function"**
- **Причина:** В поле Attachments передан массив (объектов или строк) вместо строки. Согласно документации n8n, поле ожидает "comma-separated list of binary properties" (строку через запятую), а не массив.
- **Решение:**
- ✅ Используйте СТРОКУ с именами бинарных свойств: `{{ $json.attachment_field }}`
- Формат должен быть: `"file_0,file_1,file_2"` (строка), а не `["file_0","file_1","file_2"]` (массив)
-НЕ используйте `{{ $json.attachments }}` (это массив объектов)
-НЕ используйте `{{ $json.attachment_keys }}` (это массив строк)
-НЕ используйте `{{ Object.keys($binary) }}` (это тоже массив)
3. **Ошибка: "Invalid argument type"**
- **Причина:** Неправильный формат данных в поле Attachments
- **Решение:** Используйте строку с именами бинарных свойств через запятую: `"file_0,file_1,file_2"` (строка), а не массив
4. **Файлы не прикрепляются**
- **Причина:** Имена файлов не совпадают с ключами в объекте `binary`
- **Решение:** Убедитесь, что ключи в массиве Attachments точно соответствуют ключам в объекте `binary`
---
## Пример полного workflow
```
Webhook → Process Files → Code Node (подготовка) → Send Email
(binary данные)
(attachments массив)
```
**Code Node:**
- Вход: бинарные данные из предыдущего узла
- Выход: `{ json: {...}, binary: {...} }` с правильными ссылками
**Send Email:**
- Attachments: `{{ $json.attachments }}` или прямое обращение к `$binary`
---
## Дополнительные ресурсы
- Файл с кодом: `ticket_form/docs/N8N_CODE_PREPARE_EMAIL_ATTACHMENTS.js`
- Документация n8n: https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.email/

View File

@@ -0,0 +1,224 @@
# Быстрое исправление ошибки с вложениями в n8n "Send email"
## ✅ РАБОЧЕЕ РЕШЕНИЕ
**Используйте код из файла:** `N8N_CODE_PREPARE_EMAIL_ATTACHMENTS_WORKING.js`
**В узле "Send email":**
1. **В поле "Attachments" используйте:**
```
{{ $json.attachment_field }}
```
2. **В поле "Text" (или "Body") используйте читаемый текст:**
```
{{ $json.email_body }}
```
Вместо нечитаемого JSON `{{ $('Webhook').item.json.body.form_data }}` вы получите красиво отформатированный текст!
Этот код успешно протестирован и работает! ✅
---
## Проблемы, которые решает этот код
- ❌ "options.attachments.split is not a function"
- ❌ "The first argument must be of type string or an instance of Buffer... Received undefined"
- ❌ Бинарные данные не передаются между узлами
- ❌ Файлы не прикрепляются к email
## ✅ Правильная настройка
### 1. Code Node (подготовка данных)
**✅ РАБОЧАЯ ВЕРСИЯ (РЕКОМЕНДУЕТСЯ):**
Используйте код из файла `N8N_CODE_PREPARE_EMAIL_ATTACHMENTS_WORKING.js`
Этот код:
- ✅ Успешно обработан и протестирован
- ✅ Правильно обрабатывает бинарные данные из webhook
- ✅ Создает правильный `attachment_field` для узла "Send email"
- ✅ Сохраняет бинарные данные в правильном формате
**Альтернативные варианты (если основной не подходит):**
**Вариант A: Code Node (если бинарные данные нужно переименовать)**
Используйте код из файла `N8N_CODE_PREPARE_EMAIL_ATTACHMENTS.js`
**Вариант B: Code Node (простая версия - если бинарные данные уже в правильном формате)**
Используйте код из файла `N8N_CODE_PREPARE_EMAIL_ATTACHMENTS_SIMPLE.js`
**Вариант C: Function Node (если Code Node не передает binary)**
Используйте код из файла `N8N_FUNCTION_PREPARE_EMAIL_ATTACHMENTS.js`
### 2. Узел "Send email"
**В поле "Attachments" используйте:**
```
{{ $json.attachment_field }}
```
**⚠️ ВАЖНО:** Это должна быть **СТРОКА**, а не массив!
---
## 🔍 Проверка
### Шаг 1: Проверьте OUTPUT Code Node
После выполнения Code Node проверьте OUTPUT панель:
1. **Должно быть поле `attachment_field`** (строка):
```
"attachment_field": "file_0,file_1,file_2,file_3"
```
2. **Должен быть объект `binary`** с бинарными данными:
```
binary: {
file_0: { data: ..., fileName: "..." },
file_1: { data: ..., fileName: "..." },
...
}
```
### Шаг 2: Проверьте настройки узла "Send email"
1. Откройте узел "Send email"
2. Найдите поле **"Attachments"** (не "Attachment"!)
3. Убедитесь, что там указано: `{{ $json.attachment_field }}`
4. **НЕ используйте** выражения типа:
- `{{ $json.attachments }}` ❌
- `{{ $json.attachment_keys }}` ❌
- `{{ Object.keys($binary) }}` ❌
### Шаг 3: Проверьте передачу бинарных данных
Убедитесь, что бинарные данные передаются между узлами:
1. Code Node должен возвращать:
```javascript
return [{
json: { ... },
binary: { file_0: ..., file_1: ... } // ⚠️ Это обязательно!
}];
```
2. Узел "Send email" должен быть подключен **напрямую** к Code Node (без промежуточных узлов, которые могут потерять бинарные данные)
---
## 🐛 Если ошибка "Received undefined" все еще возникает
**Эта ошибка означает, что бинарные данные не передаются в узел "Send email"!**
### ⚠️ Проблема: Бинарные данные теряются между узлами
**Симптомы:**
- В OUTPUT Code Node нет раздела "Binary"
- Ошибка: "The first argument must be of type string or an instance of Buffer... Received undefined"
- `attachment_field` есть, но файлы не прикрепляются
**Решения:**
### Вариант 1: Используйте Function Node вместо Code Node
Function Node лучше сохраняет бинарные данные:
1. Замените Code Node на **Function Node**
2. Используйте код из `N8N_FUNCTION_PREPARE_EMAIL_ATTACHMENTS.js`
3. Проверьте OUTPUT - должен быть раздел "Binary"
### Вариант 2: Используйте простую версию кода
Если бинарные данные уже приходят с правильными именами (`file_0`, `file_1`, etc.):
1. Используйте код из `N8N_CODE_PREPARE_EMAIL_ATTACHMENTS_SIMPLE.js`
2. Этот код просто передает бинарные данные без изменений
### Вариант 3: Проверьте цепочку узлов
Убедитесь, что между узлом с бинарными данными и "Send email" нет узлов, которые могут потерять binary:
- ❌ "Set" node может потерять binary
- ❌ "Edit Fields" может потерять binary
- ✅ "Code" / "Function" node сохраняет binary (если правильно настроен)
- ✅ Прямое подключение лучше всего
### Вариант 4: Проверьте имя поля в узле "Send email"
В некоторых версиях n8n поле может называться по-другому:
- "Attachments" (множественное число)
- "Attachment" (единственное число)
- "Attachments Field"
Проверьте все варианты.
### Вариант 2: Используйте Function Node вместо Code Node
Если Code Node не передает бинарные данные правильно, используйте Function Node:
```javascript
const items = $input.all();
return items.map(item => {
const binary = {};
const attachmentString = [];
let i = 0;
for (const key of Object.keys(item.binary || {})) {
const newKey = `file_${i}`;
binary[newKey] = item.binary[key];
attachmentString.push(newKey);
i++;
}
return {
json: {
...item.json,
attachment_field: attachmentString.join(',')
},
binary: binary
};
});
```
### Вариант 3: Проверьте версию n8n
В разных версиях n8n узел "Send email" может работать по-разному. Проверьте документацию для вашей версии:
- n8n 1.0+: обычно ожидает строку
- n8n 0.x: может ожидать массив
---
## 📝 Пример правильной настройки
```
Webhook → Process Files → Code Node → Send Email
(attachment_field: "file_0,file_1,...")
(binary: {file_0: {...}, file_1: {...}})
[Send Email: Attachments = {{ $json.attachment_field }}]
```
---
## ❓ Частые вопросы
**Q: Почему ошибка "split is not a function"?**
A: Узел "Send email" пытается вызвать `.split()` на поле Attachments, значит он ожидает строку, а получает массив или объект.
**Q: Почему файлы не прикрепляются?**
A: Проверьте, что:
1. Бинарные данные переданы в объекте `binary`
2. Ключи в `binary` совпадают с именами в `attachment_field`
3. Узел "Send email" подключен напрямую к Code Node
**Q: Можно ли использовать массив?**
A: Нет, узел "Send email" в вашей версии n8n ожидает строку. Используйте `{{ $json.attachment_field }}`.

View File

@@ -0,0 +1,199 @@
# Диагностика проблемы с вложениями в n8n "Send email"
## ✅ Симптом: В OUTPUT Code Node есть раздел "Binary" с файлами
Если бинарные данные есть в OUTPUT Code Node, но ошибка "Received undefined" все еще возникает, проблема в том, как узел "Send email" получает эти данные.
---
## 🔍 Пошаговая диагностика
### Шаг 1: Проверьте цепочку узлов
**Вопрос:** Есть ли промежуточные узлы между Code Node и "Send email"?
```
Code Node → [промежуточные узлы?] → Send email
```
**Проверка:**
- ❌ Если есть узлы типа "Set", "Edit Fields", "IF" → они могут потерять binary
- ✅ Узел "Send email" должен быть подключен **напрямую** к Code Node
**Решение:** Уберите промежуточные узлы или переместите их после "Send email".
---
### Шаг 2: Проверьте поле "Attachments" в узле "Send email"
**Откройте узел "Send email" и проверьте:**
1. **Поле называется "Attachments" (множественное число)?**
- ✅ Правильно: "Attachments"
- ❌ Неправильно: "Attachment" (единственное число)
2. **Что указано в поле "Attachments"?**
- ✅ Правильно: `{{ $json.attachment_field }}`
- ❌ Неправильно: `{{ $json.attachments }}`
- ❌ Неправильно: `{{ $json.attachment_keys }}`
- ❌ Неправильно: `{{ Object.keys($binary) }}`
3. **Проверьте значение поля:**
- Нажмите на поле "Attachments"
- Должна появиться строка вида: `"file_0,file_1,file_2"`
- Если видите массив `["file_0","file_1"]` → это неправильно!
---
### Шаг 3: Проверьте INPUT узла "Send email"
**Откройте INPUT панель узла "Send email":**
1. **Должен быть раздел "Binary"** с файлами `file_0`, `file_1`, etc.
- ✅ Если есть → бинарные данные дошли до узла
- ❌ Если нет → бинарные данные потерялись между узлами
2. **Проверьте раздел "JSON":**
- Должно быть поле `attachment_field: "file_0,file_1,file_2"`
- Если поля нет → проблема в Code Node
---
### Шаг 4: Проверьте версию n8n
**Разные версии n8n могут работать по-разному:**
- **n8n 1.0+**: Обычно ожидает строку в поле Attachments
- **n8n 0.x**: Может ожидать массив
**Проверьте версию:** Настройки → О системе
---
## 🛠️ Решения
### Решение 1: Используйте прямое подключение
```
Webhook → Process Files → Code Node → Send Email
(напрямую, без промежуточных узлов)
```
**Убедитесь, что:**
- Нет узлов между Code Node и Send email
- Code Node возвращает `binary: binary`
- Send email подключен напрямую к Code Node
---
### Решение 2: Проверьте синтаксис в поле Attachments
**Попробуйте разные варианты:**
**Вариант A (рекомендуется):**
```
{{ $json.attachment_field }}
```
**Вариант B (если вариант A не работает):**
```
{{ $json.attachment_field.toString() }}
```
**Вариант C (если бинарные данные есть в INPUT):**
```
{{ Object.keys($binary).join(',') }}
```
**Вариант D (прямое указание, если файлов немного):**
```
file_0,file_1,file_2
```
---
### Решение 3: Используйте Function Node вместо Code Node
Если Code Node не передает binary правильно:
1. Замените Code Node на **Function Node**
2. Используйте код из `N8N_FUNCTION_PREPARE_EMAIL_ATTACHMENTS.js`
3. Проверьте, что в OUTPUT Function Node есть раздел "Binary"
---
### Решение 4: Проверьте настройки узла "Send email"
**В настройках узла "Send email" (вкладка "Settings"):**
1. **"Keep Only Set Fields"** должна быть **выключена**
- Если включена → узел может игнорировать бинарные данные
2. **"Continue On Fail"** - не влияет на binary, но проверьте
---
### Решение 5: Используйте альтернативный способ
Если ничего не помогает, попробуйте использовать HTTP Request для отправки email:
1. Используйте HTTP Request node вместо "Send email"
2. Настройте SMTP API (если доступно)
3. Передайте бинарные данные через multipart/form-data
---
## 📋 Чек-лист проверки
- [ ] В OUTPUT Code Node есть раздел "Binary" с файлами
- [ ] В INPUT "Send email" есть раздел "Binary" с файлами
- [ ] Нет промежуточных узлов между Code Node и Send email
- [ ] В поле "Attachments" указано: `{{ $json.attachment_field }}`
- [ ] Значение `attachment_field` - это строка, а не массив
- [ ] Имена файлов в `attachment_field` совпадают с ключами в binary
- [ ] Версия n8n поддерживает такой формат
---
## 🐛 Частые ошибки
### Ошибка 1: "Received undefined"
**Причина:** Узел "Send email" не может найти бинарные данные по указанным именам.
**Решение:**
1. Проверьте, что имена в `attachment_field` точно совпадают с ключами в binary
2. Убедитесь, что нет пробелов: `"file_0,file_1"` (правильно), а не `"file_0, file_1"` (неправильно)
### Ошибка 2: "property.split is not a function"
**Причина:** В поле Attachments передан массив вместо строки.
**Решение:**
- Используйте `{{ $json.attachment_field }}` (строка)
- НЕ используйте `{{ $json.attachment_keys }}` (массив)
### Ошибка 3: Бинарные данные есть в Code Node, но нет в Send email
**Причина:** Промежуточный узел потерял binary.
**Решение:**
- Уберите промежуточные узлы
- Подключите Send email напрямую к Code Node
---
## 💡 Дополнительные советы
1. **Используйте Execute Workflow для тестирования:**
- Создайте простой workflow только с Code Node и Send email
- Проверьте, работает ли он изолированно
2. **Проверьте логи n8n:**
- Логи могут показать, где именно теряются бинарные данные
3. **Попробуйте другой узел для отправки email:**
- Некоторые узлы лучше работают с бинарными данными
- Попробуйте "Gmail" или "Outlook" узлы, если они доступны

View File

@@ -0,0 +1,51 @@
// ============================================================================
// n8n Function Node: Подготовка бинарных данных для отправки email с вложениями
// ============================================================================
// Используйте этот код в Function Node (не Code Node!)
// Function Node лучше работает с бинарными данными в некоторых версиях n8n
// ============================================================================
// Получаем все элементы из входных данных
const items = $input.all();
// Обрабатываем каждый элемент
return items.map(item => {
// Получаем бинарные данные из элемента
const inputBinary = item.binary || {};
// Получаем JSON данные
const jsonData = item.json || {};
// Проверяем наличие бинарных данных
if (!inputBinary || Object.keys(inputBinary).length === 0) {
return {
json: {
...jsonData,
attachment_field: '',
error: 'No binary data found',
debug: {
hasBinary: !!inputBinary,
binaryKeys: Object.keys(inputBinary || {})
}
}
};
}
// Получаем все ключи бинарных данных
const binaryKeys = Object.keys(inputBinary);
// Создаем строку с именами бинарных свойств через запятую
const attachmentField = binaryKeys.join(',');
// ⚠️ КРИТИЧНО: Возвращаем объект с json И binary
// В Function Node бинарные данные передаются через поле binary
return {
json: {
...jsonData,
attachment_field: attachmentField
},
// ⚠️ ВАЖНО: Передаем бинарные данные БЕЗ ИЗМЕНЕНИЙ
binary: inputBinary
};
});