1

我在将多个文件上传到 AWS 时遇到问题。我有两个模型课程 && 附件。课程has_many :attachments,我正在设置一个包含课程字段的表单,我想将多个附件上传到该课程。一切都正确上传,除非我上传多个文件,它会创建一个新课程和附件。我正在工作:

https://gorails.com/episodes/multiple-file-uploads-with-shrine?autoplay=1

https://u.osu.edu/hasnan.1/2014/03/30/rails-4-multiple-file-upload-with-carrierwave-nested-form-and-jquery-file-upload/

楷模

课程模型

class Lesson < ApplicationRecord
  belongs_to :user
  has_many :attachments, dependent: :destroy
  accepts_nested_attributes_for :attachments
end

附件模型

class Attachment < ApplicationRecord
  belongs_to :lesson, optional: true
  include AttachmentUploader::Attachment.new(:media)
end

控制器

课程控制器

Class LessonsController < ApplicationController
  # truncated for brevity.

  def new
    @lesson = current_user.lessons.build
  end


  def create
    @lesson = current_user.lessons.build(lesson_params)

    respond_to do |format|
      if @lesson.save

        if params[:media]
          params[:media].each { |media|
            @lesson.attachments.create(media_data: media)
          }
        end

        format.html { redirect_to @lesson, notice: 'Lesson was successfully created.' }
        format.json { render :show, status: :created, location: @lesson }
      else
        puts "\n\n\n#{@lesson.errors.full_messages.to_sentence}\n\n\n"
        format.html { render :new, notice: @lesson.errors }
      end
    end

  end



  private

    def set_lesson
      @lesson = Lesson.find(params[:id])
    end

    def lesson_params
      params.require(:lesson).permit(
        :title, 
        :content, 
        :document, 
        :category_id,
        :pinned, 
        :bootsy_image_gallery_id, 
        attachments_attributes: {media: []},
      )
    end

end

附件控制器

class AttachmentsController < ApplicationController
  before_action :set_attachment, only: [:edit, :update, :destroy]

  def create
    @attachment = Attachment.new(attachment_params)
    @attachment.save
  end


  private

    def set_attachment
      @attachment = Attachment.find(params[:id])
    end

    def attachment_params
      params.fetch(:attachment, {})
    end
end

再培训局

_form.html.erb

<%= @lesson.errors.full_messages.first if @lesson.errors.any? %>
<%= form_for @lesson do |f| %>
<div class="control-group">
  <%= f.label :title %>
  <div class="controls">
    <%= f.text_field :title, required: true %>
  </div>

  <div>
    <%= f.label :content %>
    <%= f.bootsy_area :content, editor_options: { html: false }, rows: "20", cols: "100" %>
  </div>

  <div>
    <%= f.label 'File under at least one class' %>
    <%= f.collection_select :category_id, Subject.all, :id, :name, { promt: "Choose a Class" } %>
  </div>

  <div>
    <%= f.label :pinned %>
    <%= f.label :pinned, "Yes", value: "Yes"  %>
    <%= f.radio_button :pinned, true%>
    <%= f.label :pinned, "No", value: "No" %>
    <%= f.radio_button :pinned, false, checked: true %>
  </div>
  <hr>


    <div>
      <%= f.label 'Or Upoad a file' %>

      <%
        ######################
        # This is where I have the attachment file field. 
        ######################      
      %>

      <%= file_field_tag "media[]", type: :file, multiple: true %>

    </div>

  <br>
    <%= f.submit nil %>
    <%= link_to 'Cancel', lessons_path%>

  <div class="form-actions btn-a">
    <%= link_to 'Cancel', lessons_path, class: "btn btn-default" %>
  </div>

路线

Rails.application.routes.draw do
  mount AttachmentUploader::UploadEndpoint => "/attachments/upload"


  resources :lessons do
    member do
      put "like",     to: "lessons#upvote"
      put "dislike",  to: "lessons#downvote"
    end
    resources :comments
    resources :attachments
  end

  root 'static#index'

end

JS

$(document).on("turbolinks:load", function () {
  $("[type=file]").fileupload({
    add: function (e, data) {
      data.progressBar = $('<div class="progress" style="width: 300px"><div class="progress-bar"></div></dov>').insertAfter("#file-upload");
      var options = {
        extension: data.files[0].name.match(/(\.\w+)?$/)[0],
        _: Date.now() // revent caching
      }

      $.getJSON("/attachments/upload/cache/presign", options, function (result) {
        data.formData = result['fields'];
        data.url = result['url'];
        data.paramName = "file";
        data.submit();
      });
    },
    progress: function (e, data) {
      var progress = parseInt(data.loaded / data.total * 100, 10);
      var percentage = progress.toString() + '%';
      data.progressBar.find(".progress-bar").css("width", percentage).html(percentage);
    },
    done: function (e, data) {
      console.log("done", data);
      data.progressBar.remove();

      var document = {
        id: data.formData.key.match(/cache\/(.+)/)[1],
        storage: 'cache',
        metadata: {
          size: data.files[0].size,
          filename: data.files[0].name.match(/[^\/\\]+$/)[0],
          mime_type: data.files[0].type
        }
      }

      form = $(this).closest("form");
      form_data = new FormData(form[0]);
      form_data.append($(this).attr("name"), JSON.stringify(document))

      $.ajax(form.attr("action"), {
        contentType: false,
        processData: false,
        data: form_data,
        method: form.attr("method"),
        dataType: "json",
        success: function(response) {
          var $img = $("<img/>", { src: response.image_url, width: 400 });
          var $div = $("<div/>").append($img);
          $("#photos").append($div);
        }
      });
    }
  });
});
4

1 回答 1

1

The reason this is happening is because the Javascript looks for the URL of the file field's parent form. You have the form for creating a new Lesson, which means every time you upload a file, it will create a new lesson.

Some options to consider are:

  1. Place the upload form in a place that's only available after the Lesson has been created. This lets you create a form_for [@lesson, Attachment.new] which will point to the right URL and let you upload files really quickly, but the Lesson must be created first.

  2. You could adjust the JS not to submit an AJAX request right away and instead attach hidden fields with the image data.

My screencast covered this method because you had to create the Album first before uploading on the Album's show page. I think this ends up being a better user experience so they aren't worrying about file uploads failing and causing their other form data to possibly get lost or not saved.

于 2017-09-25T20:50:29.790 回答