Проблема с отправкой CSV-файла в БД через POST Axios и React-Final-Form

Мне нужно сохранить содержимое CSV в таблице базы данных с помощью React-Final-Form и Axios.

Я попытался создать простой HTML без использования Final-Form или Axios, и отправка в БД работает нормально. Проблема в том, что я пытаюсь передать содержимое CSV функции, которая будет обрабатывать вызов POST. См. Код ниже:

import React, { Fragment } from "react";
import { Form, Field } from "react-final-form";
import createDecorators from "final-form-focus";

const handleSubmitOnClick = file => {
   const url = 'http://localhost:3000/api/v1/invitations/upload';
   const data = new FormData();
   data.append('file', new File([file], { type: 'text/csv' }));

   return axios.post(url, data, {
     headers: {
       'content-type': 'multipart/form-data'
     }
   })
  .then(response => console.log(response))
  .catch(error => console.log(error));
}

const JoinTesting = () => 

  <Fragment>
    <h1>Join Testing Page</h1>
    <Form 
      onSubmit={handleSubmitOnClick}
      decorators={[focusOnError]}
    >
      {
        ({ 
          handleSubmit, 
          values, 
          submitting,
        }) => (
        <form onSubmit={handleSubmit} encType="multipart/form-data">
          <Field 
            name='invitation[file]'
            placeholder='Upload csv file'
            validate={required}
          >
            {({ input, meta, placeholder }) => (
              <div className={meta.active ? 'active' : ''}>
                <label>{placeholder}</label>
                <input {...input} 
                  type='file' 
                  placeholder={placeholder} 
                  className="join-field-input"
                />
                {meta.error && meta.touched && <span className="invalid">{meta.error}</span>}
                {meta.valid && meta.dirty && <span className="valid">Great!</span>}
              </div>
            )}
          </Field>

          <button 
            type="submit"
            className="join-button"
            disabled={submitting}
          >
            Submit
          </button>

          <pre>{JSON.stringify(values, 0, 2)}</pre>
        </form>
      )}
    </Form>
  </Fragment>

export default JoinTesting;

Если я удалю ВСЕ вышеперечисленное и просто использую этот HTML-код в своем компоненте JoinTesting, он будет работать нормально, но я не смогу обрабатывать ошибки (если таковые имеются).

<form action="http://localhost:3000/api/v1/invitations/upload" method="post" encType="multipart/form-data">
    Select CSV to upload:
    <input type="file" name="invitation[file]" id="fileToUpload" />
    <br></br>
    <input type="submit" value="Upload CSV" name="submit" />
  </form>

ПОЖАЛУЙСТА, ОБРАТИТЕ ВНИМАНИЕ: файл CSV имеет только 1 простой столбец с последовательностью адресов электронной почты. Это то, что ожидает запрос POST:

Заголовки:

Content-Type: application/json
Accept: application/json

Тело

{
  "invitation": {
    "file": "Email\[email protected]\[email protected]\[email protected]\[email protected]\n"
  }
}

Ответ API, ожидаемый при успешном вызове:

{
  "success": true,
  "emails": [
    "[email protected]",
    "[email protected]",
    "[email protected]",
    "[email protected]"
  ]
}

Я надеюсь, что кто-то может помочь. Георгий


person Joe    schedule 19.09.2019    source источник
comment
Какую ошибку вы получаете?   -  person Rikin    schedule 27.09.2019


Ответы (1)


Если вы не используете html form + HTTP POST + encType="multipart/form-data", вам нужно будет обработать загрузку файла самостоятельно.

Один из способов сделать это: получить ссылку на компонент input, прослушать изменения, когда происходит изменение, получить имя файла из входной ссылки, прочитать файл, сохранить данные. Вот компонент для этого:

function FileInput(props) {
  const fileInput = useRef(null);
  const setRef = ref => {
    fileInput.current = ref;
  };

  async function handleInputChange() {
    const { files } = fileInput.current;
    props.onChange(files[0]);
  }

  return (
    <input
      ref={setRef}
      type="file"
      placeholder={props.placeholder}
      className="join-field-input"
      onChange={handleInputChange}
    />
  );
}

Используйте это в компоненте Field, и состояние формы будет содержать файл:

<Field name="invitation[file]" placeholder="Upload csv file">
    {({ input, meta, placeholder }) => (
       // ...
        <FileInput {...input} placeholder={placeholder} />
       // ...
    )}
</Field>

Также handleSubmitOnClick получает весь объект values. Ваши значения должны выглядеть примерно так:

values = { invitation: { file: {} } } 

Итак, измените функцию handleSubmitOnClick на:

const handleSubmitOnClick = values => {
  const data = new FormData();
  const { file } = values.invitation;
  data.append('file', file, file.name);
  // ...
}

Вот codeandbox.

person Istvan Szasz    schedule 18.10.2019
comment
Я протестирую это на этой неделе и позволю вам. Спасибо за ответы. - person Joe; 21.10.2019
comment
Привет, Иштван, я получаю это сообщение об ошибке: Uncaught TypeError: Failed to build 'File': Указанное значение не может быть преобразовано в последовательность. Это происходит в строке data.append ..... - person Joe; 21.10.2019
comment
Привет, @Joe, я обновил ответ (функции handleInputChange и handleSubmitOnClick) и включил ссылку codeandbox. - person Istvan Szasz; 22.10.2019
comment
Привет, @Istvan Szasz, возвращенный файл пуст. { "invitation": { "file": {} } }, это не добавление содержимого файла. FormData: {} пусто - person Joe; 24.10.2019