Rails [求助] Rails 5.2 使用 carrierwave 上传图片,怎么在选择完图片后,自动上传?

Sylor-huang · 2018年08月12日 · 最后由 Sylor-huang 回复于 2018年08月12日 · 2395 次阅读

我参考了:http://railscasts-china.com/episodes/file-uploading-by-carrierwave 这个教程来做图片上传,并使用多态表存储图片相关内容。

在这个教程里,图片上传是在 form 表单提交后,再上传图片到服务器。

我现在想要的是能不能在使用 carrierwave 选择图片后,后台自动上传到服务器?类似于 RubyChina 的 markdown 里的图片上传。

我的部分代码是:

Gemfiles:

gem 'rails', '~> 5.2'
gem 'carrierwave', '~> 1.2', '>= 1.2.2'
gem 'mini_magick'

img 模型,img.rb:

class Img < ActiveRecord::Base
  has_many :attachments, as: :attachmentable, :dependent => :destroy
end

Attachment 模型 attachment.rb:

class Attachment < ApplicationRecord
  mount_uploader :attachment, ImageUploader
  belongs_to :attachmentable, :polymorphic => true
end

控制器 imgs_controller:

class ImgsController < ApplicationController

  def index
    @imgs = Img.all
  end

  def show
    @img = Img.find(params[:id])
  end

  def new
    @img = Img.new
  end

  def create
    @img = Img.new(img_params)
    if @img.save
      Attachment.create(:attachment => params[:attachment], :attachmentable =>@img) if params[:attachment]  #这里怎么修改为form_for
      redirect_to @img
    end
  end

  def destroy
    @img = Img.find(params[:id])
    @img.destroy
    redirect_to @img
  end

  private

  def img_params
    params.require(:img).permit(:img_name)
  end
end

imgs 的 html:

# new.html.erb

<%= form_for(@img,:html => {:multipart => true}) do |f| %>
  <%= f.text_field :img_name %>
  <%= file_field_tag :attachment %>
  <%= f.submit "upload" %>
<% end %>



# show.html.erb

<% @img.attachments.each do |f| %>
  <%= image_tag f.attachment.url %>
<% end %>

按照上述代码,在new.html.erb中提交,可以上传图片,并正确显示。

但是上传图片是需要在 img 的 create 控制器中,运行: Attachment.create(:attachment => params[:attachment], :attachmentable =>@img) if params[:attachment], 才可以上传。

怎么样才能让 <%= file_field_tag :attachment %> 选择图片后,自动上传图片?

或者说:Attachment.create(:attachment => params[:attachment], :attachmentable =>@img) if params[:attachment]可以转化为 form_for 形式的吗?

比如说这样的:(这个只是例子,不能正常运行)

<%= form_for :attachments do |m| %>
  <%= m.file_field :attachment %> 
  <%= m.submit %> 
<% end %>

请路过的大佬,不吝指教,非常感谢~


参考 Homeland 的 markdown 图片上传代码,我增加了相应内容如下:

attachments 控制器:attachments_controller.rb

class AttachmentsController < ApplicationController
  def create
    @attachment = Attachment.new
    if @attachment.save
      render json: { ok: true, url: @attachment.attachment.url }
    else
      render json: { ok: false }
    end
  end
end

路由routes.rb:

resources :attachments, only:[:create]

new.html.erb点击上传按钮: <a id="editor-upload-attachment" title="上传图片" class="nav-link" href="#"><i class="fa fa-image"></i> </a>

markdown.js

let localImgUpload = $("#editor-upload-attachment");
localImgUpload.on("click",function (e) {
        e.preventDefault()
        let formData = new FormData();
        formData.append("attachment[file]",file);  ##这里的formData 该是哪些内容
        $.ajax({
            url : '/attachments',
            type: "POST",
            data: formData,
            dataType: "JSON",
            processData: false,
            contentType: false,
            success: function (res) {
                if (res.ok === true){
                    console.log(res.url);
                }
            }
        });
    });

@Rei 因为我用的是 Jquery,所以我没看懂:https://github.com/ruby-china/homeland/blob/master/app/assets/javascripts/editor.coffee#L63 这行的 item 和 filename 的值,在直接上传的情况下是哪些值。类似于您的这行:https://github.com/getcampo/campo/blob/7d3574fcb8bf7d4c1fa63fd20f25ec4a9b714243/app/assets/javascripts/controllers/editor_controller.coffee#L10

如果在按照我那个多态上传的话,formData.append(),该怎么写呢?再次感谢 @Rei.

Rei 回复

@Rei 非常感谢您的指导,但是我还是有些地方不是很明白,我更新了下问题,可以请您帮我看下,该怎么修改吗?~~

又看了一次问题,似乎要的只是

我现在想要的是能不能在使用 carrierwave 选择图片后,后台自动上传到服务器

如果只是这样的话,那么只要把表单改成 remote,在 file input 触发 change 事件的时候提交表单就行:

<%= form_for(@img,:html => {:multipart => true, id: 'img-form'}, :remote => true) do |f| %>
  <%= f.text_field :img_name %>
  <%= file_field_tag :attachment, id: 'attachment-input' %>
  <%= f.submit "upload" %>
<% end %>
<script>
  $('#attachment-input').on('change', function() {
    Rails.fire(document.querySelector('#img-form'), 'submit'); // 必须用 Rails.fire,原生的 submit 方法不会触发 rails ujs remote form
  });
</script>

至于你想简化表单的参数,那么应该重新考虑模型建立,Img 模型的作用是什么?如果 Img 模型没必要,那么直接创建 Attachment 就好了。

PS:Attachment 模型多态关联也是有它的用处,Rails 的 ActiveStorage 有 Attachment - Blob 完整的实现,如果需要这种模型可以直接用 ActiveStorage。

Rei 回复

@Rei 感谢您的指导。是的,Img 模型是没有用的,这个只是我用来试验的,主要这个还是用到 markdown 里上传图片的。然后,ActiveStorage 确实很好用,但是返回的 url 我不会自定义,返回的 ulr 太长了,所以我就用 carrierwave 了。再次非常感谢您的指导,这个问题困扰了好几天。

5 楼 已删除
需要 登录 后方可回复, 如果你还没有账号请 注册新账号