Многократная загрузка файла с использованием React, Ruby on rails 5 и CarrierWave - PullRequest
0 голосов
/ 07 декабря 2018

Я застрял на проблеме около 2 дней.Возможно, я смотрю, но не могу найти сообщение об этом ...

Итак, я использую инфраструктуру Ruby on Rails с gem реагировать на рельсы, и я пытаюсь сделать множественную загрузку в реакции, используядрагоценный камень CarrierWave.

Таким образом, загрузка работала очень хорошо, когда был только один файл.Но я принял другую стратегию, оказалось, что мне наконец-то нужно было загрузить несколько файлов для одной модели.Поэтому я создал полиморфную модель (если она мне нужна где-то еще, она более общая).

Итак, проблема в том, что я посылаю данные от реакции на мой контроллер.Действительно, JSON.stringify не работает с объектным файлом, и я не понимаю, как я мог это сделать ...

Мой контроллер

  # POST /band_musics
  # POST /band_musics.json
  def create
    @band_music = BandMusic.new({
      :name => band_music_params[:name],
      :phone => band_music_params[:phone],
      :mail => band_music_params[:mail],
      :style => band_music_params[:style],
      :comment => band_music_params[:comment],
      :status => band_music_params[:status],
      :musics_attributes => JSON.parse(band_music_params[:musics_attributes]),
      :youtubes_attributes => JSON.parse(band_music_params[:youtubes_attributes])
    })
    respond_to do |format|
      if @band_music.save
        format.html { redirect_to root_path, notice: 'Le groupe a bien été enregisté' }
        format.json { render :show, status: :created, location: @band_music }
      else
        format.html { render :new }
        format.json { render json: @band_music.errors, status: :unprocessable_entity }
      end
    end
  end

Мой band_music_params

def band_music_params
  params.require(:band_music).permit(:name, :mail, :phone, :style, :comment, :status, :youtubes_attributes, :musics_attributes)
end

My React Component

import PropTypes from 'prop-types';
import React from 'react';
import ReactDropzone from 'react-dropzone'

export default class GroupForm extends React.Component {
  static propTypes = {
  };

  /**
   * @param props - Comes from your rails view.
   */
  constructor(props) {
    super(props);
    this.state = {
      files: {},
      loading: false,
      disabled: true,
    };
  }

  submitGroupForm = (event) => {
    event.preventDefault();

    let response_files = {}
    Object.keys(this.state.files).map((file, index) => {
      response_files[index] = {
          'lastMod'    : this.state.files[file].sound.lastModified,
          'lastModDate': this.state.files[file].sound.lastModifiedDate,
          'name'       : this.state.files[file].sound.name,
          'size'       : this.state.files[file].sound.size,
          'type'       : this.state.files[file].sound.type,
      }
    });

    const file_array = JSON.stringify(response_files);

    this.setState({ loading: true });
    let formPayLoad = new FormData();
    formPayLoad.append('band_music[musics_attributes]', file_array);

    fetch(this.props.url, {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'X-CSRF-Token': ReactOnRails.authenticityToken(),
      },
      body: formPayLoad,
    }).then((response) => {
      console.log(response)
    });
  }

  onDrop = (files) => {
    if (Object.keys(this.state.files).length === 3) return
    var countFile = Object.keys(this.state.files).length
    console.log(countFile)
    files.forEach(file => {
      let hash = this.state.files
      hash[`${countFile}`] = { sound: file }
      this.setState({
        files: hash
      });
    });
  }

  render() {
    console.log(this.state)
    return (
      <div>
        <form onSubmit={this.submitGroupForm}>
          <div className="form-group">
            <label htmlFor="sound" className="color-primary">
              Fichier(s) Audio(s)
            </label>
            <ReactDropzone
              accept="audio/*"
              onDrop={this.onDrop}
              className="react-drop-zone-css"
            >
              { Object.keys(this.state.files).length > 0 &&
                <div className="fileContainer">
                  {Object.keys(this.state.files).map((file) => (
                    <p className="file">
                      <i className="fa fa-music fa-2x mb-2"></i>
                      {this.state.files[file].sound.name}
                    </p>
                  ))}
                  { Object.keys(this.state.files).length < 1 &&
                    <p className="plus">
                      <i className="fa fa-plus fa-3x mb-2"></i>
                    </p>
                  }
                </div>
              }

              { Object.keys(this.state.files).length === 0 &&
                <div className="d-flex justify-content-center align-items-center w-100 h-100">
                  <p className="mb-0">Cliquez pour importer un fichier audio ! (3 fichiers max)</p>
                </div>
              }
            </ReactDropzone>
          </div>

          <div className="row justify-content-center">
            {
              !!this.state.loading ? (
                <input className="btn btn-lg bg-button color-white mt-3" type="submit" value="Chargement... Cette action peut prendre quelques minutes" />
              ) : (
                <input className={"btn btn-lg bg-button color-white mt-3 " + (!!this.state.disabled ? 'disabled' : '')} disabled={!!this.state.disabled} type="submit" value="S'inscrire au tremplin" />
              )
            }
          </div>
        </form>
      </div>
    );
  }
}

Чтобы упростить задачу, как передать файл в параметрах.Здесь я попытался преобразовать файл в классический объект для stringify, он не делает никакой ошибки, но не сохраняет файл в полиморфной модели ...

Если вам нужно, чтобы я опубликовал другойфайл для большего понимания, пожалуйста, так скажите в комментарии, заранее спасибо :))

Ответы [ 2 ]

0 голосов
/ 07 декабря 2018

Спасибо за ваш быстрый ответ.

Я попробовал так, как вы сказали мне делать (это то, что я сделал, но когда это не сработало, я импровизировал):

Я изменил настройки следующим образом:

params.require(:band_music).permit(:name, :mail, :phone, :style, :comment, :status, youtubes_attributes: [:url], musics_attributes: [:lastMod, :lastModDate, :name, :size, :type])

Моя функция создания в контроллере

# POST /band_musics
  # POST /band_musics.json
  def create
    @band_music = BandMusic.new(band_music_params)
    respond_to do |format|
      if @band_music.save
        format.html { redirect_to root_path, notice: 'Le groupe a bien été enregisté' }
        format.json { render :show, status: :created, location: @band_music }
      else
        format.html { render :new }
        format.json { render json: @band_music.errors, status: :unprocessable_entity }
      end
    end
  end

Моя функция submitForm

submitGroupForm = (event) => {
    event.preventDefault();

    let response_files = {}
    Object.keys(this.state.files).map((file, index) => {
      response_files[index] = {
          'lastMod'    : this.state.files[file].sound.lastModified,
          'lastModDate': this.state.files[file].sound.lastModifiedDate,
          'name'       : this.state.files[file].sound.name,
          'size'       : this.state.files[file].sound.size,
          'type'       : this.state.files[file].sound.type,
      }
    });

    this.setState({ loading: true });
    let formPayLoad = new FormData();
    formPayLoad.append('band_music[musics_attributes]', response_files);

    fetch(this.props.url, {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'X-CSRF-Token': ReactOnRails.authenticityToken(),
      },
      body: formPayLoad,
    }).then((response) => {
      console.log(response)
    });
  }

Вот журналы :

Started POST "/band_musics" for 127.0.0.1 at 2018-12-07 14:34:36 +0100
Processing by BandMusicsController#create as JSON
  Parameters: {"band_music"=>{"name"=>"dqsdqsd", "musics_attributes"=>"[object Object]", "mail"=>"qsdqsdqsd", "phone"=>"090909", "style"=>"acid breaks", "comment"=>"dsqdqsdqsd", "youtubes_attributes"=>"[object Object]", "status"=>"pending"}}
Unpermitted parameters: :musics_attributes, :youtubes_attributes

Проблема с параметрами и "[object, Object]" ...

0 голосов
/ 07 декабря 2018

Давайте начнем с того, что исправим метод контроллера:

  # POST /band_musics
  # POST /band_musics.json
  def create
    @band_music = BandMusic.new(band_music_params)
    respond_to do |format|
      if @band_music.save
        format.html { redirect_to root_path, notice: 'Le groupe a bien été enregisté' }
        format.json { render :show, status: :created, location: @band_music }
      else
        format.html { render :new }
        format.json { render json: @band_music.errors, status: :unprocessable_entity }
      end
    end
  end

Фактической точки в этом нет:

  1. Копирование хеш-ключей, как в человеческом компиляторе.В RoR есть множество хеш-методов, таких как Hash#slice и Hash#except.
  2. Вручную JSON преобразует вложенные атрибуты.

Вместо этого вы можете внести в белый список вложенные атрибуты, передав хэш в#permit.

def band_music_params
  params.require(:band_music)
        .permit(
           :name, :mail, :phone, :style, :comment, :status, 
           youtubes_attributes: [:foo, :bar], 
           musics_attributes: [:lastMod, :lastModDate, :name, :size, :type]
        )
end

Это разрешает массив хэшей с атрибутами [:lastMod, :lastModDate, :name, :size, :type].Вы также должны, конечно, удалить строку в своем коде реагирования, которая преобразует объекты в строки JSON:

// Don't do this.
const file_array = JSON.stringify(response_files);
...