使用 React、Ruby on rails 5 和 CarrierWave 多次上传文件
Posted
技术标签:
【中文标题】使用 React、Ruby on rails 5 和 CarrierWave 多次上传文件【英文标题】:Multiple upload file using React, Ruby on rails 5 and CarrierWave 【发布时间】:2019-05-09 04:30:47 【问题描述】:我被一个问题困扰了大约 2 天。我可能正在寻找,但我找不到关于它的帖子...
因此,我将 Ruby on Rails 框架与 gem react on rails 结合使用,并尝试使用 CarrierWave gem 在 react 中进行多次上传。
因此,当只有一个文件时,上传效果非常好。但我采用了另一种策略,结果我最终不得不为单个模型上传多个文件。所以我创建了一个多态模型(如果我在其他地方需要它,它更通用)。
所以问题是我将数据从 react 发送到我的控制器的那一刻。确实 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
我的反应组件
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 的经典对象,它不会出错,但不会将文件保存在多态模型中...
如果您需要我发布另一个文件以获得更多理解,请在评论中说出来,提前谢谢您:)
【问题讨论】:
在处理实际上传时,如果可以的话,您可能希望使用ActiveStorage 而不是 CarrierWave - 它非常容易处理文件参数,您可以使用 API 进行直接上传。跨度> 【参考方案1】:让我们从修复控制器方法开始:
# 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
没有实际意义:
-
像人类编译器一样复制哈希键。 RoR 有大量的哈希方法,例如
Hash#slice
和 Hash#except
。
手动 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);
【讨论】:
【参考方案2】:感谢您的快速回答。
我尝试了你告诉我的方式(我就是这样做的,但当它不起作用时,我即兴发挥):
我改变了这样的设置:
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]”有问题...
【讨论】:
您是否尝试将属性添加到 band_music[musics_attributes][image/whatever][] 中?以上是关于使用 React、Ruby on rails 5 和 CarrierWave 多次上传文件的主要内容,如果未能解决你的问题,请参考以下文章
使用Ruby on Rails的Babel-Transpiler:入门[关闭]
Ruby on Rails - 在 OSX 上使用 Ruby 2.4.4 而不是 rails 5.1.6 的配置问题/异常
Ruby on Rails全栈课程5.2 项目上线--在云服务器上配置Ruby On Rails环境
在 ruby on rails 5.2.4 中使用谷歌云翻译时出错