Перейти к содержимому
Все статьи
· Обновлено 19 апреля 2026 г.

Отправка файлов через Discord Webhook (изображения, вложения, multipart)

Загрузка файлов через Discord webhook с multipart/form-data. Рабочие примеры на Python, JavaScript, curl и PHP — изображения, PDF, логи, вложения в embed.

загрузка файловвложенияmultipartwebhookизображенияdiscord apiтуториал
Отправка файлов через Discord Webhook (изображения, вложения, multipart)

Discord webhook’и не только для текста. Можно загрузить скриншоты, лог-файлы, PDF, архивы — что угодно до 25 МБ (лимит free-tier 2026). Подвох: нужно перейти с JSON на multipart/form-data. Если формат неверный — Discord молча отклоняет загрузку с общим 400.

В гайде — точная форма запроса и рабочий код для Python, JavaScript, curl и PHP, включая прикрепление файла внутрь embed’а как изображения.

Как работает загрузка файлов

Обычно вы шлёте application/json:

{ "content": "Hello" }

Для файлов — multipart/form-data с двумя частями:

  1. payload_json — обычный webhook JSON (content, embeds, username и т. д.)
  2. files[N] — бинарный контент файла, индекс с 0

Тот же JSON, что обычно, идёт внутри payload_json; файл — рядом.

Лимиты файлов (2026)

TierМакс. файлФайлов на сообщение
Free25 МБ10
Nitro Basic50 МБ10
Nitro500 МБ10
Boost (сервер)50–100 МБ от уровня10

Webhook использует boost-tier сервера. Webhook в Level 3 сервере шлёт 100 МБ, даже если у вас free-аккаунт.

Python — requests

import requests

WEBHOOK_URL = "https://discord.com/api/webhooks/ID/TOKEN"

with open("screenshot.png", "rb") as f:
    files = {
        "file": ("screenshot.png", f, "image/png"),
    }
    payload = {
        "payload_json": '{"content": "Артефакт билда:", "username": "CI Bot"}'
    }
    r = requests.post(WEBHOOK_URL, data=payload, files=files)
    r.raise_for_status()

Ключевое:

  • payload_json — это строка (не dict) внутри data
  • MIME-тип важен — image/png, application/pdf, text/plain и т. п.
  • requests сам строит multipart boundary

Несколько файлов

files = {
    "files[0]": ("error.log", open("error.log", "rb"), "text/plain"),
    "files[1]": ("trace.txt", open("trace.txt", "rb"), "text/plain"),
    "files[2]": ("dump.bin", open("dump.bin", "rb"), "application/octet-stream"),
}
requests.post(WEBHOOK_URL, data={"payload_json": '{"content": "Crash report"}'}, files=files)

Ключи должны быть files[0], files[1], … по порядку.

JavaScript — Node.js FormData

В Node 18+ FormData и fetch встроены:

import fs from 'node:fs';

const form = new FormData();
form.append('payload_json', JSON.stringify({
  content: 'Прикреплён ежедневный отчёт',
  username: 'Reports Bot',
}));

const buffer = fs.readFileSync('./report.pdf');
form.append('file', new Blob([buffer], { type: 'application/pdf' }), 'report.pdf');

const res = await fetch(process.env.WEBHOOK_URL, {
  method: 'POST',
  body: form,
});
console.log(res.status);

Content-Type вручную не ставьfetch сам выведет multipart boundary из FormData.

Загрузка из браузера (без бэкенда)

Если разрешаете загрузку из <input type="file">:

async function uploadToDiscord(file, message) {
  const form = new FormData();
  form.append('payload_json', JSON.stringify({ content: message }));
  form.append('file', file, file.name);

  const res = await fetch(WEBHOOK_URL, { method: 'POST', body: form });
  if (!res.ok) throw new Error(`Upload failed: ${res.status}`);
}

document.querySelector('input[type=file]').addEventListener('change', async (e) => {
  await uploadToDiscord(e.target.files[0], 'Новая загрузка с сайта');
});

Внимание: не светите webhook URL в клиентском коде, если не готовы, что им может пользоваться кто угодно. В проде проксируйте через бэкенд.

curl — командная строка

curl -X POST \
  -F "payload_json={\"content\":\"Прикреплён лог\"}" \
  -F "file=@./server.log;type=text/plain" \
  https://discord.com/api/webhooks/ID/TOKEN

Кавычки важны: JSON внутри payload_json нужно экранировать в shell. Для сложного JSON удобнее писать во временный файл:

echo '{"content":"Лог","embeds":[{"title":"Build #42","color":3066993}]}' > /tmp/p.json
curl -X POST \
  -F "payload_json=<@/tmp/p.json" \
  -F "file=@./build.log" \
  https://discord.com/api/webhooks/ID/TOKEN

PHP — CURLFile

<?php
$webhook = 'https://discord.com/api/webhooks/ID/TOKEN';

$payload = json_encode([
    'content' => 'Прикреплён ежедневный бэкап',
    'username' => 'Backup Bot',
]);

$ch = curl_init($webhook);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
    'payload_json' => $payload,
    'file' => new CURLFile('/srv/backups/db.sql.gz', 'application/gzip', 'db.sql.gz'),
]);

$response = curl_exec($ch);
curl_close($ch);

CURLFile сам ставит правильный MIME boundary; не используйте @filename-строки (deprecated и небезопасно с PHP 5.6).

Изображения внутри embed

Хотите, чтобы файл показался внутри embed, а не отдельным вложением? Ссылайтесь через attachment://:

import requests

with open("chart.png", "rb") as f:
    files = {"file": ("chart.png", f, "image/png")}
    payload_json = '''
    {
      "embeds": [{
        "title": "Q4 Revenue",
        "image": { "url": "attachment://chart.png" }
      }]
    }
    '''
    requests.post(WEBHOOK_URL, data={"payload_json": payload_json}, files=files)

Имя файла в attachment://chart.png должно точно совпадать с именем в multipart-части. Работает для image, thumbnail, author.icon_url, footer.icon_url.

Стриминг больших файлов

Для файлов под 25 МБ — стримите с диска, чтобы не грузить всё в память:

import requests

def upload_large(path: str, message: str):
    with open(path, "rb") as f:
        files = {"file": (path.split("/")[-1], f)}
        return requests.post(
            WEBHOOK_URL,
            data={"payload_json": f'{{"content": "{message}"}}'},
            files=files,
            timeout=60,  # увеличить таймаут на больших файлах
        )

upload_large("/var/log/app/2026-04.tar.gz", "Месячные логи")

Файлы со спойлером

Префикс SPOILER_ в имени файла = Discord размывает превью:

files = {"file": ("SPOILER_screenshot.png", open("screenshot.png", "rb"), "image/png")}

Полезно для скриншотов с чувствительными данными — открыть можно, но автопревью не палит.

Частые ошибки

СтатусПричинаРешение
400 + Request entity too largeФайл больше лимитаСжать, разрезать или поднять boost
400 + ошибка payload_jsonНевалидный JSON внутриСначала валидировать через json.dumps()
413Серверный лимит размераТо же, что 400 — уменьшить файл
415Неверный content-typeНе ставь Content-Type: application/json для файлов
200 но файл не появилсяНе совпадает имя файла с attachment://Точное совпадение имени с расширением

Попробуйте вживую

Конструктор Discord Webhook поддерживает drag-drop файлов в сообщение и показывает превью точно как будет в Discord — тот же multipart, что описан выше. Для других рецептов смотрите гайды Python и JavaScript.

Источники

Попробуйте в нашем инструменте

Открыть конструктор Discord Webhook