1

我有一个带有实时聊天功能的 Rails 应用程序(1v1)。我想通过 AJAX 实现文件上传功能,在提交文件上传表单时,文件名将显示在聊天中,而无需重新加载整个页面。

我正在尝试使用 remotipart gem 来通过 AJAX 提交 refile 文件上传表单。文件直接上传到 S3,所以那些根本不会打到我的应用程序(我不需要显示它们),我只是通过聊天将文件名/U​​RL 发送给其他用户。

由于 refile gem,当我选择文件时,这些上传就开始了。因此,当我提交(“发送文件”按钮)表单时,它只是将文件属性(message.message_attachment/id、url、大小等)和 message_id 保存到数据库中。

在此处输入图像描述在此处输入图像描述在此处输入图像描述

在图片上你可以看到我想要实现的。聊天输入文本(message.body)有一个单独的表单,文件上传有另一个表单(message.message_attachment)。到目前为止,聊天与 private_pub gem 完美配合。选择文件也正确发生(包括将文件上传到 S3 和 204 状态响应),但提交表单(通过单击“发送文件”将消息保存到数据库)仅适用于整页重新加载。

我尝试的解决方案:

  1. 如果我没有将 'authenticity_token: true' 放入表单助手中进行上传,那么我会收到无效的 authencity token 错误。
  2. 如果我正在使用与聊天正常工作的消息控制器,那么我会在表单中收到缺少模板错误。
  3. 如果我把respond_to js(有或没有渲染:'create.js.erb')放在那里,我会收到未知格式错误。

对于聊天消息,我使用相同的 create.js.erb,但由于 private_pub gem,我不必使用 respond_to js。我只需要订阅和发布到/使用conversation_path(@path)。

我觉得我非常接近解决方案,因为它适用于整页重新加载。我不知道private_pub解决方案(不需要respond_to js)和remotipart w/refile(需要respond_to js)之间的兼容性是否会导致问题。

消息模型:

create_table "messages", force: :cascade do |t|
    t.text     "body"
    t.integer  "conversation_id"
    t.integer  "user_id"
    t.string   "message_attachment"
    t.string   "message_attachment_id"
    t.string   "message_attachment_filename"
    t.integer  "message_attachment_size"
    t.string   "message_attachment_content_type"
    ......
end

创建.js.erb

<% publish_to @path do %>
    var id = "<%= @conversation.id %>";
    var chatbox = $(".chatboxcontent");

    chatbox.append("<%= j render(partial: @message ) %>");
    chatbox.scrollTop(chatbox[0].scrollHeight);
    $(".chatboxtextarea").val("");

    var filechosen = $('.choosefile').val();
    if (filechosen != "") {
        $(".refile_form").find("input[type=submit]").hide();
        $('.choosefile').val("");
        $('#progresspercent').hide();
    }
<% end %>

消息/_show.html.erb

<div class="chatboxcontent">
  <% if @messages.any? %>
      <%= render @messages %>
  <% end %>
</div>
<div class="chatboxinput">
  <%= form_for([@conversation, @message], :remote => true, :html => {id: "conversation_form_#{@conversation.id}"}) do |f| %>
      <%= f.text_area :body, class: "chatboxtextarea", "data-cid" => @conversation.id %>
  <% end %>
  <%= form_for([@conversation, @message], html: {class: "refile_form"}, remote: true, authenticity_token: true) do |form| %>
      <span class="btn btn-success btn-sm btn-file">Choose file
      <%= form.attachment_field :message_attachment, direct: true, presigned: true, class: "choosefile" %></span>
      <%= form.submit "Send File", class: "btn btn-primary btn-sm btn-submit-refile" %>
  <% end %>
</div>

<%= subscribe_to conversation_path(@conversation) %>

在实现文件上传之前用于聊天的消息控制器:

def create
    @conversation = Conversation.find(params[:conversation_id])
    @message = @conversation.messages.build(message_params)
    @message.user_id = current_user.id
    @message.save!
    @path = conversation_path(@conversation)
end

消息控制器可以正常工作,并重新加载整页以进行上传:

def create
    @conversation = Conversation.find(params[:conversation_id])
    @message = @conversation.messages.build(message_params)
    @message.user_id = current_user.id
    @message.save!
    @path = conversation_path(@conversation)
    if @message.message_attachment_id
      respond_to do |format|
        format.html { redirect_to :back }
        # I've tried w/ and with no format.js as well.
        #format.js { render: 'create.js.erb }
      end
    end
  end

更新:

问题已解决:app.js 中的错字//=需要 jquery.remotipart...... - 由于 private_pub gem,不需要 response_to js - authencity_token: true 对于表单助手也没有必要,这要归功于 remotipart gem - remotipart 需要能够通过 AJAX 提交文件表单

4

0 回答 0