Rails 4 - Carrierwave图像无法上传

时间:2016-04-02 22:52:12

标签: ruby-on-rails carrierwave nested-forms nested-attributes

更新:感谢大家帮忙解决这个问题。我决定放弃尝试学习并为配置文件模型添加另一个图像属性。这将是现在必须做的。

我试图在rails 4中创建一个应用程序。

我有一个用户模型,上面有一个头像属性。

我也有个人资料模型。

用户:有一个个人资料 个人资料:属于User

我拆分它们的原因是因为配置文件包含所有变量,而用户包含所有未经管理员权限无法更改的固定属性。

例外是我的用户模型有一个avatar(image)属性,我希望允许用户更改。

我的代码的其他部分有用户载波,它可以工作。但是,这个用例出了点问题。

我有一个头像上传者:

class AvatarUploader < CarrierWave::Uploader::Base

  # Include RMagick or MiniMagick support:
  # include CarrierWave::RMagick
  include CarrierWave::MiniMagick

  # Choose what kind of storage to use for this uploader:
  # storage :file
  storage :fog

  # Override the directory where uploaded files will be stored.
  # This is a sensible default for uploaders that are meant to be mounted:
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  def cache_dir
    "#{Rails.root}/tmp/uploads"
  end

  # Provide a default URL as a default if there hasn't been a file uploaded:
  # def default_url
  #   # For Rails 3.1+ asset pipeline compatibility:
  #   # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
  #
  #   "/images/fallback/" + [version_name, "default.png"].compact.join('_')
  # end

  # Process files as they are uploaded:
  # process :scale => [200, 300]
  #
  # def scale(width, height)
  #   # do something
  # end

  # Create different versions of your uploaded files:
  # version :thumb do
  #   process :resize_to_fit => [50, 50]
  # end

  process :resize_to_fit => [800, 800]
  # Create different versions of your uploaded files:
  version :thumb do
    process :resize_to_fill => [200,200]
  end

  version :profile do
    process :resize_to_fill => [345,245]
  end

  version :wide do
    process :resize_to_fill => [951,245]
  end

  version :preview do
    process :resize_to_fill => [90,90]
  end

  version :small do
    process :resize_to_fill => [35,35]
  end


  # Add a white list of extensions which are allowed to be uploaded.
  # For images you might use something like this:
  def extension_white_list
     %w(jpg jpeg gif png)
  end

  # Override the filename of the uploaded files:
  # Avoid using model.id or version_name here, see uploader/store.rb for details.
  # def filename
  #   "something.jpg" if original_filename
  # end

end

在我的用户模型中,我有:

class User < ActiveRecord::Base
  mount_uploader :avatar, AvatarUploader
end

在我的用户控制器中 - 我将强化参数中的头像列入白名单。

在我的个人资料控制器中,我尝试添加:

params.require(:profile).permit(:title, :overview, user_attributes: [:avatar])

我还尝试在我的个人资料模型中允许用户属性(如下所示):

belongs_to :user
accepts_nested_attributes_for :user

我在views users文件夹中有一个部分表单。

<%= simple_fields_for :user,  html: { multipart: true } do |f| %>
  <%= f.error_notification %>
  <div class="form-inputs">
    <%= f.input :avatar, as: :file, :label => "Add a profile image (head shot)" %>
  </div>
<% end %>

该部分内容包含在我的个人资料表单中。

然后我想在我的个人资料展示视图中显示用户头像:

<div class="col-md-5 col-lg-4 vc-photo" style= "background-image: url(<%= image_url @profile.user.avatar.url if @profile.user.avatar? %>);">&nbsp;</div>

但是,它只是呈现空白。

我试过了:

);“&GT;

但它仍然只是呈现空白。

谁能看到我做错了什么?

当我尝试通过检查user.avatar来测试rails控制台时,我得到了这个:

u.avatar  =&GT; #,last_sign_in_ip:#,confirmation_token:“73abb47df224dbc3a612b46ced66e1aba ...”,confirmed_at:“2016-04-02 07:13:57”,confirmation_sent_at:“2016-04-02 22:59:44”,unconfirmed_email:“testiest @ gmail.com“,failed_attempts:0,unlock_token:nil,locked_at:nil,created_at:”2016-04-02 07:13:57“,updated_at:”2016-04-02 22:59:43“,avatar:nil ,approved:false&gt ;, @ mounted_as =:avatar&gt;

采取以下建议

我更改了表单,以便配置文件表单现在具有:

  <%= render 'users/profileimgform', f: f %>

users / profileimgform现在有:

<%= simple_fields_for :user,  html: { multipart: true } do |ff| %>
  <%= f.error_notification %>

              <div class="form-inputs">


              <%= ff.input :avatar, as: :file, :label => "Add a profile image (head shot)" %>

              </div>


        <% end %>

我不确定是否需要将“ff”合并到我的个人资料表单中。我尝试用'ff'替换最后一个'f'但是错误地询问我是否应该是单个'f'。

当我尝试这个时,控制台仍然显示user.avatar是'nil'。

4 个答案:

答案 0 :(得分:1)

我猜这段代码有错误:

public class MainViewModel : ViewModelBase
    {
        //constructor
        public MainViewModel(BootstrapperApplication bootstrapper)
        {

            this.IsThinking = false;

            this.Bootstrapper = bootstrapper;
            this.Bootstrapper.ApplyComplete += this.OnApplyComplete;
            this.Bootstrapper.DetectPackageComplete += this.OnDetectPackageComplete;
            this.Bootstrapper.PlanComplete += this.OnPlanComplete;

            this.Bootstrapper.CacheAcquireProgress += (sender, args) =>
            {
                this.cacheProgress = args.OverallPercentage;
                this.Progress = (this.cacheProgress + this.executeProgress) / 2;
            };
            this.Bootstrapper.ExecuteProgress += (sender, args) =>
            {
                this.executeProgress = args.OverallPercentage;
                this.Progress = (this.cacheProgress + this.executeProgress) / 2;
            };
        }

        #region Properties

        private bool installEnabled;
        public bool InstallEnabled
        {
            get { return installEnabled; }
            set
            {
                installEnabled = value;
                RaisePropertyChanged("InstallEnabled");
            }
        }

        private bool uninstallEnabled;
        public bool UninstallEnabled
        {
            get { return uninstallEnabled; }
            set
            {
                uninstallEnabled = value;
                RaisePropertyChanged("UninstallEnabled");
            }
        }

        private bool isThinking;
        public bool IsThinking
        {
            get { return isThinking; }
            set
            {
                isThinking = value;
                RaisePropertyChanged("IsThinking");
            }
        }

        private int progress;
        public int Progress
        {
            get { return progress; }
            set
            {
                this.progress = value;
                RaisePropertyChanged("Progress");
            }
        }

        private int cacheProgress;
        private int executeProgress;

        public BootstrapperApplication Bootstrapper { get; private set; }

        #endregion //Properties

        #region Methods

        private void InstallExecute()
        {
            IsThinking = true;
            Bootstrapper.Engine.Plan(LaunchAction.Install);
        }

        private void UninstallExecute()
        {
            IsThinking = true;
            Bootstrapper.Engine.Plan(LaunchAction.Uninstall);
        }

        private void ExitExecute()
        {
            CustomBA.BootstrapperDispatcher.InvokeShutdown();
        }

        /// <summary>
        /// Method that gets invoked when the Bootstrapper ApplyComplete event is fired.
        /// This is called after a bundle installation has completed. Make sure we updated the view.
        /// </summary>
        private void OnApplyComplete(object sender, ApplyCompleteEventArgs e)
        {
            IsThinking = false;
            InstallEnabled = false;
            UninstallEnabled = false;
            this.Progress = 100;
        }

        /// <summary>
        /// Method that gets invoked when the Bootstrapper DetectPackageComplete event is fired.
        /// Checks the PackageId and sets the installation scenario. The PackageId is the ID
        /// specified in one of the package elements (msipackage, exepackage, msppackage,
        /// msupackage) in the WiX bundle.
        /// </summary>
        private void OnDetectPackageComplete(object sender, DetectPackageCompleteEventArgs e)
        {
            if (e.PackageId == "KubeInstallationPackageId")
            {
                if (e.State == PackageState.Absent)
                    InstallEnabled = true;

                else if (e.State == PackageState.Present)
                    UninstallEnabled = true;
            }
        }

        /// <summary>
        /// Method that gets invoked when the Bootstrapper PlanComplete event is fired.
        /// If the planning was successful, it instructs the Bootstrapper Engine to 
        /// install the packages.
        /// </summary>
        private void OnPlanComplete(object sender, PlanCompleteEventArgs e)
        {
            if (e.Status >= 0)
                Bootstrapper.Engine.Apply(System.IntPtr.Zero);
        }

        #endregion //Methods

        #region RelayCommands

        private RelayCommand installCommand;
        public RelayCommand InstallCommand
        {
            get
            {
                if (installCommand == null)
                    installCommand = new RelayCommand(() => InstallExecute(), () => InstallEnabled == true);

                return installCommand;
            }
        }

        private RelayCommand uninstallCommand;
        public RelayCommand UninstallCommand
        {
            get
            {
                if (uninstallCommand == null)
                    uninstallCommand = new RelayCommand(() => UninstallExecute(), () => UninstallEnabled == true);

                return uninstallCommand;
            }
        }

        private RelayCommand exitCommand;
        public RelayCommand ExitCommand
        {
            get
            {
                if (exitCommand == null)
                    exitCommand = new RelayCommand(() => ExitExecute());

                return exitCommand;
            }
        }

        #endregion //RelayCommands
    }

查看生成的html。我认为应该是这样的:

<%= simple_fields_for :user,  html: { multipart: true } do |f| %>
  <%= f.error_notification %>
  <div class="form-inputs">
    <%= f.input :avatar, as: :file, :label => "Add a profile image (head shot)" %>
  </div>
<% end %>

答案 1 :(得分:0)

请注意,嵌套表单或复杂表单应该将模型和关联与form_forfields_for帮助器连接起来,如下所示:

<%= form_for @person do |f| %>
  Addresses:
  <ul>
    <%= f.fields_for :addresses do |addresses_form| %>
      <li>
        <%= addresses_form.label :kind %>
        <%= addresses_form.text_field :kind %>
      </li>
    <% end %>
  </ul>
<% end %>

模型和关联之间的连接方式是

  • form_for @person构建人员表单实例f
  • 使用人员表单实例f构建关联表单f.fields_for :addresses实例addresses_form
  • 使用关联表单输入上传文件addresses_form.input ...

最后,我猜simple_fields_for为simple_form的extra helpers,也许只适用于form_for帮助。

以下是给定模型和控制器的示例:

应用程序/模型/ user.rb

class User < ActiveRecord::Base
  mount_uploader :avatar, AvatarUploader
  has_one :profile
end

应用程序/模型/ profile.rb

class Profile < ActiveRecord::Base
  belongs_to :user
  accepts_nested_attributes_for :user
end

实际上,有两种方法可以修改模型的嵌套属性。

  1. 以相同的形式修改模型和嵌套属性
  2. 您应该使用模型和关联构建复杂的表单

    更多信息:http://guides.rubyonrails.org/form_helpers.html#building-complex-forms

    应用程序/控制器/ profile_controller.rb

    class ProfileController < ApplicationController
    
      private
    
      def profile_params
        params.require(:profile).permit(:title, user_attributes: [:avatar])
      end
    end
    

    应用程序/视图/简档/ edit.html.erb

    <%= form_for :profile do |f| %>
      Avatar:
      <ul>
        <%= f.fields_for :user do |uf| %>
          <li>
            <%= uf.label :avatar %>
            <%= uf.file_field :avatar %>
          </li>
        <% end %>
      </ul>
    <% end %>
    
    1. 仅修改嵌套属性
    2. 你可以为这个关联属性构建一个普通的表单,但是当你必须初始化它时它很棘手和模糊,所以我不想在这里提供示例代码而不是让你感到困惑。

答案 2 :(得分:0)

我通过将头像从用户移动到个人资料来解决问题。

答案 3 :(得分:0)

我有同样的错误。如果你在Mac上,请尝试再次安装imagemagik。我用的是自制软件。您只需要运行此命令:

brew install imagemagick