My enviroment is here:
Environment | Version |
---|---|
Rails | 7.0.0 |
Carrierwave | 2.2.2 |
React | 18.0 |
Next.js | 12.1.4 |
Carrierwave is already used in my app and it works fine.
Now, I'm replacing client-side as rails to react.
current configuration of Carrierwave is here:
carrierwave.rb
#config/initializers/carrierwave.rb
if Rails.env.production?
CarrierWave.configure do |config|
config.fog_credentials = {
provider: 'AWS',
aws_access_key_id: ENV['S3_ACCESS_KEY'],
aws_secret_access_key: ENV['S3_SECRET_KEY'],
region: ENV['S3_REGION']
}
config.fog_authenticated_url_expiration = 86400
config.fog_directory = 'something'
config.cache_storage = :fog
config.fog_public = false
end
else
CarrierWave.configure do |config|
config.fog_credentials = {
provider: 'AWS',
aws_access_key_id: ENV['S3_ACCESS_KEY'],
aws_secret_access_key: ENV['S3_SECRET_KEY'],
region: ENV['S3_REGION']
}
config.fog_authenticated_url_expiration = 86400
config.fog_directory = 'something'
config.cache_storage = :fog
config.fog_public = false
end
end
cv_uploader.rb
#app/uploaders/cv_uploader.rb
class CvUploader < CarrierWave::Uploader::Base
storage :fog
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def filename
original_filename if original_filename
end
end
user.rb
class User < ApplicationRecord
mount_uploader :cv, CvUploader
...
So I thought What I just need is saving the file to database as file name and read it from react.
I wrote this:
new.tsx
export default function UsersNewPage() {
const [cv, setCv] = React.useState<string>("");
const handleChangeCv = useMemo(
() => (event: any) => setCv(event.target.value),
[cv]
);
const createUser = async () => {
dispatch(setLoading(true));
await axios
.post(`${process.env.NEXT_PUBLIC_ENDPOINT}/admin/create_user`, {
headers: {
...
},
params: {
...
cv: cv,
...
},
})
.then((response) => {
...
}
})
.catch(() => {
...
});
}
};
return(
<label htmlFor="contained-button-file">
<Input
id="contained-button-file"
type="file"
value={cv}
onChange={handleChangeCv}
/>
<IconButton onClick={handleDeleteCv}>
<DeleteIcon />
</IconButton>
</label>
)
users_controller.rb
def create_user
if User.find_by(email: params[:params][:email])
return render json: { not_exist: true }
end
User.create(str_user)
render json: { not_new: true }
end
private
def str_user
params[:params].permit(
:cv,
)
end
But If The file name which I posted is fuga.xls
, the response would be "C:\\fakepath\\fuga.xls"
.
I don't understand where C:\\fakepath\\
comes from...
And executing User.create(str_user)
, the cv will be NULL
.
Seems not success to save the file.
I tried to fix that, I wrote:
carrierwave.rb
##ADD
elsif Rails.env.development?
CarrierWave.configure do |config|
config.asset_host = 'http://localhost:3000' #Rails port is 3000
config.storage = :file
config.cache_storage = :file
end
cv_uploader.rb
# :fog→:file
storage :file
But it couldn't help it.
Anyone have an idea to fix it?
Thank you.
CodePudding user response:
I solved the problem.
All the cause of bug is I didn't convert file type.
So I changed as view
<label htmlFor="contained-button-file">
<Input
id="contained-button-file"
type="file"
// value={cv} <-- without value
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
handleChangeCv(e);
}}
/>
<IconButton onClick={handleDeleteCv}>
<DeleteIcon />
</IconButton>
</label>
and reading file
const handleChangeCv = useMemo(
() => (e: any) => {
const file = e.target.files[0];
setCv(file);
},
[cv]
);
convert to FormData
const createFormData = () => {
const formData = new FormData();
formData.append("user[cv]", cv!);
return formData;
};
Send to rails
const data = createFormData(); // <-- here!
dispatch(setLoading(true));
await axios
.post(`${process.env.NEXT_PUBLIC_ENDPOINT}/admin/create_user`, data, {
headers: {
"access-token": access_token!,
client: client!,
uid: uid!,
},
params: {
...
You can read the file in rails like params[:user][:cv]
That's it!