Добавлена обработка бинарных данных и форматирование email для n8n workflow
- Добавлен рабочий код для подготовки бинарных данных и вложений для узла Send email - Реализовано форматирование form_data в читаемый текст с разделами: * Личные данные (с автоматическим расчетом возраста, код документа, серия и номер) * Контактная информация * Страховщик (с телефоном страховой компании) * Банковские реквизиты (с получателем платежа) * Информация о страховом случае * Описание ситуации - Добавлена документация по использованию и устранению неполадок - Созданы альтернативные версии кода (простая версия, Function Node версия)
This commit is contained in:
113
ticket_form/docs/N8N_CODE_PREPARE_EMAIL_ATTACHMENTS.js
Normal file
113
ticket_form/docs/N8N_CODE_PREPARE_EMAIL_ATTACHMENTS.js
Normal 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];
|
||||
|
||||
@@ -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
|
||||
}];
|
||||
|
||||
359
ticket_form/docs/N8N_CODE_PREPARE_EMAIL_ATTACHMENTS_WORKING.js
Normal file
359
ticket_form/docs/N8N_CODE_PREPARE_EMAIL_ATTACHMENTS_WORKING.js
Normal 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
|
||||
}];
|
||||
|
||||
220
ticket_form/docs/N8N_EMAIL_ATTACHMENTS_FIX.md
Normal file
220
ticket_form/docs/N8N_EMAIL_ATTACHMENTS_FIX.md
Normal 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/
|
||||
|
||||
224
ticket_form/docs/N8N_EMAIL_ATTACHMENTS_QUICK_FIX.md
Normal file
224
ticket_form/docs/N8N_EMAIL_ATTACHMENTS_QUICK_FIX.md
Normal 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 }}`.
|
||||
|
||||
199
ticket_form/docs/N8N_EMAIL_ATTACHMENTS_TROUBLESHOOTING.md
Normal file
199
ticket_form/docs/N8N_EMAIL_ATTACHMENTS_TROUBLESHOOTING.md
Normal 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" узлы, если они доступны
|
||||
|
||||
51
ticket_form/docs/N8N_FUNCTION_PREPARE_EMAIL_ATTACHMENTS.js
Normal file
51
ticket_form/docs/N8N_FUNCTION_PREPARE_EMAIL_ATTACHMENTS.js
Normal 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
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user