Как отправить изображение внутри формы в переменной состояния в react на бэкенд django

Я пытаюсь отправить изображение для хранения в бэкенде. Изображение хранится в react useState, который обновляется, когда пользователь изменяет форму ввода, и отправляется в django backend api. Этот api принимает и другие входные данные, не только изображение, но и строки. Поэтому для отправки запроса я храню все в useState и выполняю запрос в async-функции. Апи проверен на работу на postman, проблема в том, что я не очень хорошо знаю, как сделать запрос. Изображение является необязательным параметром, поэтому оно прекрасно работает только с другими входными данными, без изображения.

Ранее я игнорировал изображение и выполнял запрос вместе с другими входными данными, что прекрасно работало. Я сделал это, поместив все в объект. Однако когда я помещаю изображение в тот же объект, оно перестает работать. Я осмотрелся и выяснил, что для отправки изображений нужно использовать конструктор FormData, что я и сделал, но столкнулся с несколькими проблемами.

Вот как выглядит мой компонент формы:


export default function Component() {
    const [ image, setImage ] = useState<File>();


    const handleImageChange =  (e: React.ChangeEvent<HTMLInputElement>) => {
        const file = e.target.files?.[0];
        console.log('file', file)
        setImage(file);
    }

    const handleSubmit = async (e: any) => {
        const dummy_info = {
           name: "hello",
           start_date: "2024-01-01"
        }


        e.preventDefault();
  

        const formData = new FormData();


        formData.append('name', info.name);
        formData.append('start_date', info.start_date);
        
        if (image) formData.append('event_picture', image);

        const res = await apiService.post('<backend_api_url>', formData, {
                            "Content-Type": "multipart/form-data"
                       });

    }

    return (
            <form onSubmit={handleSubmit}>
              
                <input
                    // value={info.event_picture || ''}
                    className="self-center" id="image" type="file" accept="image/*"
                    onChange={handleImageChange}
                    />
                <button type="submit">Submit</button>
            </form>
    )
}

У меня есть следующий объект apiService, который я использую для надежного выполнения этих вызовов, хотя я думаю, что здесь может быть одна из проблем:

const apiService = {
    post: async function(url: string, data?: any, extraHeaders?: any): Promise<any> {
        console.log('post', url, data);
        try {
            const token = await getAccessToken();
            const headers = {
                'Authorization': `Bearer ${token}`,
                "Content-Type": "application/json",
                'Accept': 'application/json',
            };

            if (extraHeaders) {
                Object.assign(headers, extraHeaders);
            }

            return new Promise((resolve, reject) => {
                fetch(`${process.env.NEXT_PUBLIC_API_HOST}${url}`, {
                    method: 'POST',
                    body: data,
                    headers: headers
                })
                    .then(response => response.json())
                    .then((json) => {
                        console.log('Response:', json);

                        resolve(json);
                    })
                    .catch((error => {
                        reject(error);
                    }))
            })
        } catch (e) {
            console.error('error fetching data', e);
        }

    },
}

При добавлении изображения и выполнении этого запроса я получаю в ответ следующее {detail: 'Multipart form parse error - Invalid boundary in multipart: None'}

Я знаю, что объект FormData не пуст, так как я console.log и он возвращает значения (для изображения: ['event_picture', File])

примечание: я использую Nextjs и заранее благодарю вас

Для отправки изображения на ваш сервер у вас есть несколько вариантов:

  1. Отправка на сервер изображения в виде строки base64. (не рекомендуется)
  2. Отправка изображения на ваш сервер и сохранение его непосредственно на вашем сервере. (не рекомендуется)
  3. Загрузка на внешнее хранилище S3 на AWS и отправка url изображения на ваш сервер. (рекомендуется)

Я не рекомендую использовать первый и второй способ, потому что это увеличивает сложность проекта. первый способ увеличивает размер базы данных из-за огромного количества длинных строк, сохраняемых в таблицах. Второй способ не увеличивает размер базы данных, но вам придется написать больше функциональности для управления изображениями, добавления метаданных к изображениям, преобразования их в различные размеры (например, 64x64, 128x128, 256x256 и т. д.), поиска между изображениями, приватных и публичных изображений, указания, кто может получить доступ к изображению, и многое другое.

Я настоятельно рекомендую использовать внешний сервер S3 для обработки всего вышеперечисленного и использовать Ant design или любой другой front-end фреймворк, который значительно упрощает работу по получению изображения и его загрузке на сервер S3. В основном вы загружаете изображение на сервер AWS S3 и можете отправить ссылку на него в ваш бэкенд Django, если изображение общедоступно, или, если изображение приватно, вы можете просто отправить его id в ваш бэкенд Django и сохранить в базе данных, когда пользователь с доступом приходит, вы создаете пользовательскую ссылку на таймер для этого пользователя.

Вы можете узнать об Ant.design по этому URL https://ant.design/

Подробнее о S3 в AWS можно узнать по этому адресу https://aws.amazon.com/pm/serv-s3/

Но если вы хотите пойти по первому пути, вы можете легко преобразовать ваше изображение в Base64String и отправить эту строку на ваш сервер как входной текст. Вот как это можно сделать: Как преобразовать изображение в строку Base64 с помощью JavaScript?

или Если вы хотите воспользоваться вторым способом и загрузить изображение непосредственно на свой сервер, вы можете посмотреть эту ссылку: https://omarshishani.com/how-to-upload-images-to-server-with-react-and-express/

При загрузке файлов, например изображений, необходимо внимательно относиться к объекту FormData. В React/Next.js важно позволить браузеру автоматически управлять 'content-type'.

Ручная установка Content-Type в "multipart/form-data" может привести к проблемам, таким как ошибка "Invalid boundary in multipart", с которой вы столкнулись, потому что браузер не будет включать необходимую границу, которая разделяет различные части данных формы.

Чтобы решить эту проблему, необходимо удалить ручную настройку 'content-type' в Component и Service.

Сообщите мне, если у вас возникнут другие вопросы после рассмотрения моего мнения.

Вернуться на верх