当我使用来自反应的载波上传文件时,文件变为 NULL

File becomes NULL when I upload the file with carrierwave from react

我的环境在这里:

Environment Version
Rails 7.0.0
Carrierwave 2.2.2
React 18.0
Next.js 12.1.4

Carrierwave 已在我的应用程序中使用,并且运行良好。
现在,我将客户端替换为 rails 来做出反应。

Carrierwave 的当前配置在这里:

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
...

所以我想我只需要将文件作为文件名保存到数据库并从 React 中读取它。
我写了这个:

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

但是如果我发布的文件名是fuga.xls,响应将是"C:\fakepath\fuga.xls"
我不明白C:\fakepath\从哪里来...

并执行 User.create(str_user),cv 将为 NULL
似乎没有成功保存文件。
我试图解决这个问题,我写道:

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

但是没办法。
有人有解决办法吗?

谢谢。

我解决了这个问题。
错误的原因都是我没有转换文件类型。

所以我改变了视图

                    <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>

正在读取文件

  const handleChangeCv = useMemo(
    () => (e: any) => {
      const file = e.target.files[0];
      setCv(file);
    },
    [cv]
  );

转换为FormData

  const createFormData = () => {
    const formData = new FormData();
    formData.append("user[cv]", cv!);
    return formData;
  };

发送至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: {
            ...

您可以像 params[:user][:cv] 一样阅读 rails 中的文件
就是这样!