Home > Software design >  File becomes NULL when I upload the file with carrierwave from react
File becomes NULL when I upload the file with carrierwave from react

Time:04-26

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!

  • Related