Загрузить файл в многоступенчатом формате formik

#reactjs #formik

#reactjs #formik

Вопрос:

Я реализовал многоступенчатую форму formik с помощью formik-wizard-form. Он отлично работает с другими входными данными, за исключением загрузки файла.

Я использую следующий код для загрузки файла:

 <Form.Group controlId="upload">
  <Col>
   <div className="form-group files">
     Add Attachments:
      <input 
      type="file" 
      name="upload"
      value={values.upload?.files }
      onChange={event => {
      setFieldValue("upload", event.currentTarget.files);
      }}
      multiple />
   </div>
  </Col>
</Form.Group>
  

Значения загрузки журнала консоли такие, как показано ниже.

Как мне передать загружаемые файлы на сервер?

значения изображения в журнале консоли

Ответ №1:

Хитрость с файловыми вводами в соответствии с документами заключается в:

В React <input type="file" /> всегда является неконтролируемым компонентом, поскольку его значение может быть установлено только пользователем, а не программно.

Таким образом, он использует свой собственный механизм наряду с реализациями безопасности браузера для установки значения отправленного файла. По соображениям безопасности вы не можете получить полный путь к файлу на локальном компьютере того, с которого он был отправлен, хотя мы можем использовать объект FileReader для асинхронного чтения содержимого файла, а затем использовать результат для заполнения значения и отправки его родительскому компоненту формы.

Бывают случаи, когда вы можете изменить значение ввода файла программно, например, на null, чтобы сбросить ваш ввод.

В своей многоступенчатой форме я использовал formik и material-ui. в нем есть поле (UploadFiled), которое type="file" будет обернуто компонентом пользовательского поля. Всякий раз, когда дочерний компонент получает обновление, файл загружается, а родительский компонент узнает и начинает считывать содержимое указанного большого двоичного объекта, после завершения атрибут result содержит данные: URL, представляющий данные файла, будет установлен в качестве его значения. В настоящее время он принимает базу изображений на основе проверки, которая установлена для него с помощью Yup.

Традиционный тип application / json не поможет при загрузке изображения на ваш сервер, а FormData поможет. Вам нужно написать свой handleSubmit(), используя данные формы, и передать значения, обрабатываемые Formik.

Вы можете использовать любой из Fetch API или Axios для отправки POST-запроса на ваш сервис, зависит от ваших предпочтений.

 const onSubmit = () => {
    // Create an object of formData
    const formData = new FormData();

    // Update the formData object
    formData.append("myFile", file, file.name);

    // Details of the uploaded file
    console.log(file);

    // Request made to the backend api
    // Send formData object
    axios.post("api/uploadfile", formData);
  };  

 // UploadForm.jsx

import React, { useState, useEffect } from "react";
import { Field, useField } from "formik";
import { Grid, FormHelperText } from "@material-ui/core";
import UploadField from "../../FormFields/UploadField";
import Thumb from "../Helper/Thumb";

const ImageForm = (props) => {
  const {
    formField: { image }
  } = props;

  const [field, meta, helper] = useField(image.name);
  const { touched, error } = meta;
  const { setValue } = helper;
  const isError = touched amp;amp; error amp;amp; true;
  const { value } = field;

  const [fileName, setFileName] = useState(value.name);
  const [file, setFile] = useState(value.file);
  const [src, setSrc] = useState(value.src);
  const _onChange = (e) => {
    let reader = new FileReader();
    let file = e.target.files[0];
    if (file) {
      reader.onloadend = () => setFileName(file.name);
      if (file.name !== fileName) {
        reader.readAsDataURL(file);
        setSrc(reader);
        setFile(file);
      }
    }
  };

  useEffect(() => {
    if (file amp;amp; fileName amp;amp; src) {
      setValue({ file: file, src: src, name: fileName });
      console.log(fileName);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [src, fileName, file]);

  return (
    <React.Fragment>
      <Grid container spacing={3} justify="center" alignItems="center">
        <Grid item xs={12}>
          <label>
            {image.label}
          </label>
          <br />
          <div
            style={{
              display: "flex",
              justifyContent: "flex-start",
              fontSize: "1.2em"
            }}
          >
            <Field
              variant="outlined"
              field={field}
              component={UploadField}
              onChange={_onChange}
              isError={isError}
            />
            {isError amp;amp; <FormHelperText color={"red"}>{error}</FormHelperText>}
          </div>
        </Grid>
        <Grid item>{file amp;amp; src amp;amp; <Thumb file={file} src={src}></Thumb>}</Grid>
      </Grid>
    </React.Fragment>
  );
};

export default ImageForm;  

// Поле для загрузки.jsx

 import React from "react";
import { Field } from "formik";

const UploadField = ({
  field,
  form: { touched, errors },
  name,
  label,
  isError,
  ...props
}) => {
  return (
    <>
      <Field
        variant="outlined"
        name="uploader"
        title={label}
        type={"file"}
        {...props}
      />
    </>
  );
};

export default UploadField;  

Ссылка на Code Sandbox,

Комментарии:

1. Спасибо, ваше решение решает мою проблему 🙂

Ответ №2:

Formik не поддерживает загрузку файлов «из коробки». Итак, если вы загружаете файлы асинхронно, вам придется сделать что-то вроде этого:

 const rebuildData = (values) => {
  let formData = new FormData();
  Object.keys(values).forEach(key => {
    formData.append(key, values[key]);
  });
  return formData;
};

<Formik
  onSubmit={values => {
    const data = rebuildData(values);
    axios({
        method: 'post',
        url: '/YOUR_ENDPOINT',
        data
      })
    }
  }
  

Не забудьте добавить остальные данные вашей формы в formData .

Комментарии:

1. Хотите добавить детали к ответу, убедитесь, что задали заголовки для многоступенчатой загрузки в axios, чтобы фактически доставлять файлы.

2. @Darius в моем случае нет необходимости отправлять какие-либо дополнительные заголовки, но если вам нужно отправить заголовки, просто используйте headers ключ в объекте параметров запроса.

3. Серверная часть @Darius на моем сервере каким-то образом «распознает» formData без каких-либо усилий со стороны интерфейса.

4. Понял. Что касается меня, я сохранял данные в многоступенчатой форме, не имея «формы» для отправки, только значения. Так что, возможно, это было потому, что я отправлял не «форму», а значения с переменной «file» там.

Ответ №3:

Вы можете использовать setFieldValue из formik. ссылка: Formik

 const _onChange = (e) => {
    let reader = new FileReader();
    let file = e.target.files[0];
    if (file) {
      reader.onloadend = () => setFileName(file.name);
      if (file.name !== fileName) {
        reader.readAsDataURL(file);
        setSrc(reader);
        setFieldValue('file',file);
      }
    }
  };