Missing boundary in multipart/form-data — блог Игоря Звягинцева
Лирическое вступление
Чтобы писать посты в блог нужен редактор. Чтобы посты читали в них нужно добавлять картинки. Чтобы добавлять картинки в посты в редакторе должна быть кнопка «Добавить картинку». И она у меня есть, но, как в том анекдоте, есть один нюанс* и состоит он в том, что редактор постов является самописным Vue приложением в основе которого Vue2Editor который, в свою очередь, построен вокруг редактора Quill (говно, не используйте). Как следствие самописности, реализация таких вещей как загрузка картинок начиная от выбора картинки с помощью диалогового окна или драг-н-дропа до отправки её на сервер ложатся на мои плечи.
Самая суть
Итак, у меня есть код, который загружает картинку на сервер.
handleImageAdded: async function(file) { if (!file) return const formData = new FormData() formData.append('image', file) const headers = new Headers({ 'Content-Type': 'multipart/form-data', user: /* имя пользователя */, token: /* токен пользователя */ }) try { const result = await fetch( '/api/endpoint/for/image', { headers, method: 'POST', body: formData } ) const url = result. data.url // … код вставки картинки в пост } catch (error) { console.error(error) } }
Кто понял где тут ошибка, тот молодец и дальше может не читать.
Тем, кто не понял скажу, что источник проблемы в отправляемых заголовках. Точнее в Content-Type. Далее в нескольких предложениях я рефлексирую на тему как я допустил такую ошибку. Если вы не любите воду в блогах, то листайте до подзаголовка «Решение»
Посыпаю голову пеплом
Мой основной язык это JavaScript. В последний раз на PHP много лет назад и, когда, в своём переписывании движка сайта с нуля, я дошёл до необходимости загрузить файл на сервер. Я естественно вбил в гугл «PHP загрузка файлов». Первая же ссылка привела меня сюда. Если взглянуть на форму из Примера №1, то можно увидеть, что в тэге <form> указаны несколько свойств. Если они указаны в самом первом примере, рассудил я, который, по идее, должен быть самым простым и минимально рабочим, значит надо их явно передавать. Тем более там первой строчкой идёт комментарий с КАПСЛОКОМ где выделены слова ДОЛЖЕН БЫТЬ. Логично? Логично! А вот и нет!
Решение
Проблема в заголовке Content-Type. Он есть. Он правильный, но с ним что-то не так. Что?
Не так с ним то, что он не полон. Если не устанавливать его в коде, то браузер выставит его самостоятельно и выглядеть он будет вот так:
То есть, даже если мы программно выгружаем файл, то умница-браузер всё равно сам подставит правильный заголовок за нас. Как говаривали на Руси: —«Не лезь вперёд батьки в пекло!». Очень мудрые слова.
Итого есть два стула: на одном вам придётся руками ставить Content-Type и boundary. Это сложно, долго и бессмысленно. На втором вам просто нужно не добавлять заголовок в коде. Выбор за вами.
К столь простому решению меня привёл вот этот комментарий
______
* Если вы не знаете этого анекдота и вы не являетесь тургеневской барышней или HR который по этому посту пытается оценить меня как возможного кандидата, то обязательно загуглите этот анекдот. Если вы HR и рассматриваете меня как возможного кандидата, то не делайте этого.
HTML-форма enctype Атрибут
❮ Тег HTML
Попробуйте сами »Определение и использование
Атрибут enctype
указывает, как данные формы должны быть закодированы, когда
отправив его на сервер.
Примечание: Атрибут enctype
можно использовать, только если метод = "пост"
.
Поддержка браузера
Атрибут | |||||
---|---|---|---|---|---|
enctype | Да | Да | Да | Да | Да |