Rails 6,无法将 s3_direct_upload gem 上传到 UPLOAD,查看工作正常

Posted

技术标签:

【中文标题】Rails 6,无法将 s3_direct_upload gem 上传到 UPLOAD,查看工作正常【英文标题】:Rails 6, can not get s3_direct_upload gem to UPLOAD, view works fine 【发布时间】:2021-11-28 17:54:38 【问题描述】:

我有 2 个应用程序已从旧版本的 Rails(3.2 和 4.2.1)重写为 Rails v6.1.4.1,它们都使用 s3_direct_upload gem。

在这两个应用程序上,我在 Webdev 控制台、Rails 控制台、日志或我能找到的任何地方都没有收到任何错误。在两个应用程序的情况下,存储桶都显示得很好。 我检查了 CORS 设置,一切正常。这两个应用程序目前都在 Heroku 上运行,代码与现在相同,但正在运行。

有谁知道 s3_​​direct_upload gem 是否真的适用于 Rails 6?

我得到了文件选择窗口,我选择了文件名,它显示了文件名,但它没有开始上传并显示进度条,它就像我当时什么都没做一样。我能找到的任何地方都没有错误。当我将原始应用程序并排放置时,我应该会看到一个快速进度条出现然后消失,页面刷新并显示新文件。在我重写的 2 个应用程序中,它永远不会超过文件选择并显示我选择的文件名。我将显示一般文件,以便至少可以看到:

这就是问题 1,s3_direct_upload gem 在 Rails 6 中工作吗?

以下是所需的基本文件:

s3_direct_upload.rb

S3DirectUpload.config do |c|
  c.access_key_id     = Rails.configuration.aws[:access_key_id]
  c.secret_access_key = Rails.configuration.aws[:secret_access_key]
  c.bucket            = Rails.configuration.aws[:bucket]
  c.region            = "ENV['AWS_REGION']"
  c.url               = "https://#c.bucket.s3.amazonaws.com/" 
end

aws.rb

require 'aws-sdk-v1'
# Rails.configuration.aws is used by AWS, Paperclip, and S3DirectUpload
Rails.configuration.aws = YAML.load(ERB.new(File.read("#Rails.root/config/aws.yml")).result)[Rails.env].symbolize_keys!
AWS.config(logger: Rails.logger)
AWS.config(Rails.configuration.aws)

回形针.rb

Paperclip::Attachment.default_options.merge!(
  url:                  ":s3_domain_url",
  path:                 ":class/:attachment/:id/:style/:filename",
  storage:              :s3,
  s3_credentials:       Rails.configuration.aws,
  s3_permissions:       :private,
  s3_protocol:          "https",
  s3_region:            ENV["AWS_REGION"]
)

aws.rb

require 'aws-sdk-v1'
# Rails.configuration.aws is used by AWS, Paperclip, and S3DirectUpload
Rails.configuration.aws = YAML.load(ERB.new(File.read("#Rails.root/config/aws.yml")).result)[Rails.env].symbolize_keys!
AWS.config(logger: Rails.logger)
AWS.config(Rails.configuration.aws)

aws.yml(我将存储桶名称更改为 mybucket,图片有效,因此我知道存储桶有效)

defaults: &defaults
development:
  <<: *defaults
  region: <%=ENV["AWS_REGION"]%> 
  bucket: "mybucket"
  access_key_id: <%=ENV["AWS_ACCESS_KEY_DPFR"]%>
  secret_access_key: <%=ENV["AWS_SECRET_KEY_DPFR"]%>
test:
  <<: *defaults
  region: <%=ENV["AWS_REGION"]%> 
  bucket: "mybucket-test"
production:
  region: <%=ENV["AWS_REGION"]%> 
  access_key_id: <%=ENV["AWS_ACCESS_KEY_DPFR"]%>
  secret_access_key: <%=ENV["AWS_SECRET_KEY_DPFR"]%>
  bucket: "mybucket"

以下是相关的 gem:(如果有人想查看整个 gemfile,请告诉我)

gem 'paperclip-aws', '~> 1.6', '>= 1.6.8'
gem 'aws-sdk-v1', '~> 1.67'
gem 's3_direct_upload', '~> 0.1.7'

这里是相关的 js s3_direct_upload.js.coffee。 (我什至尝试转换为海峡 JS 与咖啡并没有区别。警报在那里,因为我想确保它正在读取文件并且它确实收到了警报。

#= require jQuery-fileupload/basic
#= require jQuery-fileupload/vendor/tmpl

alert("We are in the S3_Direct_Upload coffee file");



$ = jQuery

$.fn.S3Uploader = (options) ->

  # support multiple elements
  if @length > 1
    @each ->
      $(this).S3Uploader options

    return this

  $uploadForm = this

  settings =
    path: ''
    additional_data: null
    before_add: null
    remove_completed_progress_bar: true
    remove_failed_progress_bar: false
    progress_bar_target: null
    click_submit_target: null
    allow_multiple_files: true
    dropZone: null

  $.extend settings, options

  current_files = []
  forms_for_submit = []
  if settings.click_submit_target
    settings.click_submit_target.click ->
      form.submit() for form in forms_for_submit
      false

  $wrapping_form = $uploadForm.closest('form')
  if $wrapping_form.length > 0
    $wrapping_form.off('submit').on 'submit', ->
      $wrapping_form.find('.s3_uploader input').prop "disabled", true
      true

  setUploadForm = ->
    $uploadForm.find("input[type='file']").fileupload

      dropZone: settings.dropzone_target
      add: (e, data) ->
        file = data.files[0]
        file.unique_id = Math.random().toString(36).substr(2,16)

        unless settings.before_add and not settings.before_add(file)
          current_files.push data
          if $('#template-upload').length > 0
            data.context = $($.trim(tmpl("template-upload", file)))
            $(data.context).appendTo(settings.progress_bar_target || $uploadForm)
          else if !settings.allow_multiple_files
            data.context = settings.progress_bar_target
          if settings.click_submit_target
            if settings.allow_multiple_files
              forms_for_submit.push data
            else
              forms_for_submit = [data]
          else
            data.submit()

      start: (e) ->
        $uploadForm.trigger("s3_uploads_start", [e])

      progress: (e, data) ->
        if data.context
          progress = parseInt(data.loaded / data.total * 100, 10)
          data.context.find('.bar').css('width', progress + '%')

      done: (e, data) ->
        content = build_content_object $uploadForm, data.files[0], data.result

        callback_url = $uploadForm.data('callback-url')
        if callback_url
          content[$uploadForm.data('callback-param')] = content.url

          $.ajax
            type: $uploadForm.data('callback-method')
            url: callback_url
            data: content
            beforeSend: ( xhr, settings )       ->
              event = $.Event('ajax:beforeSend')
              $uploadForm.trigger(event, [xhr, settings])
              return event.result
            complete:   ( xhr, status )         ->
              event = $.Event('ajax:complete')
              $uploadForm.trigger(event, [xhr, status])
              return event.result
            success:    ( data, status, xhr )   ->
              event = $.Event('ajax:success')
              $uploadForm.trigger(event, [data, status, xhr])
              return event.result
            error:      ( xhr, status, error )  ->
              event = $.Event('ajax:error')
              $uploadForm.trigger(event, [xhr, status, error])
              return event.result

        data.context.remove() if data.context && settings.remove_completed_progress_bar # remove progress bar
        $uploadForm.trigger("s3_upload_complete", [content])

        current_files.splice($.inArray(data, current_files), 1) # remove that element from the array
        $uploadForm.trigger("s3_uploads_complete", [content]) unless current_files.length

      fail: (e, data) ->
        content = build_content_object $uploadForm, data.files[0], data.result
        content.error_thrown = data.errorThrown

        data.context.remove() if data.context && settings.remove_failed_progress_bar # remove progress bar
        $uploadForm.trigger("s3_upload_failed", [content])

      formData: (form) ->
        data = $uploadForm.find("input").serializeArray()
        fileType = ""
        if "type" of @files[0]
          fileType = @files[0].type
        data.push
          name: "content-type"
          value: fileType

        key = $uploadForm.data("key")
          .replace('timestamp', new Date().getTime())
          .replace('unique_id', @files[0].unique_id)
          .replace('cleaned_filename', cleaned_filename(@files[0].name))
          .replace('extension', @files[0].name.split('.').pop())

        # substitute upload timestamp and unique_id into key
        key_field = $.grep data, (n) ->
          n if n.name == "key"

        if key_field.length > 0
          key_field[0].value = settings.path + key

        # IE <= 9 doesn't have XHR2 hence it can't use formData
        # replace 'key' field to submit form
        unless 'FormData' of window
          $uploadForm.find("input[name='key']").val(settings.path + key)
        data

  build_content_object = ($uploadForm, file, result) ->
    content = 
    if result # Use the S3 response to set the URL to avoid character encodings bugs
      content.url            = $(result).find("Location").text()
      content.filepath       = $('<a />').attr('href', content.url)[0].pathname
    else # IE <= 9 retu      rn a null result object so we use the file object instead
      domain                 = $uploadForm.find('input[type=file]').data('url')
      key                    = $uploadForm.find('input[name=key]').val()
      content.filepath       = key.replace('/filename', '').replace('/cleaned_filename', '')
      content.url            = domain + key.replace('/filename', encodeURIComponent(file.name))
      content.url            = content.url.replace('/cleaned_filename', cleaned_filename(file.name))

    content.filename         = file.name
    content.filesize         = file.size if 'size' of file
    content.lastModifiedDate = file.lastModifiedDate if 'lastModifiedDate' of file
    content.filetype         = file.type if 'type' of file
    content.unique_id        = file.unique_id if 'unique_id' of file
    content.relativePath     = build_relativePath(file) if has_relativePath(file)
    content = $.extend content, settings.additional_data if settings.additional_data
    content

  cleaned_filename = (filename) ->
    filename.replace(/\s/g, '_').replace(/[^\w.-]/gi, '')

  has_relativePath = (file) ->
    file.relativePath || file.webkitRelativePath

  build_relativePath = (file) ->
    file.relativePath || (file.webkitRelativePath.split("/")[0..-2].join("/") + "/" if file.webkitRelativePath)

  #public methods
  @initialize = ->
    # Save key for IE9 Fix
    $uploadForm.data("key", $uploadForm.find("input[name='key']").val())

    setUploadForm()
    this

  @path = (new_path) ->
    settings.path = new_path

  @additional_data = (new_data) ->
    settings.additional_data = new_data

  @initialize()

我尝试让 webpacker 服务器的 JS 文件没有区别,我开始怀疑 s3_direct_upload gem 不适用于较新的 rails,因为这几乎是唯一的区别,或者需要进行一些配置更改在过去 3 天里我找不到。

任何帮助将不胜感激,除了图片上传之外,我还有 100% 的其他应用程序在这 2 个应用程序上工作。

谢谢你, 斯科特

【问题讨论】:

【参考方案1】:

我可以确认您是否提取了最新版本的 s3_direct_upload gem,它确实使用 Rails 6、aws-sdk-v1 和 Paperclip 正确上传到 Amazon S3。

为此,您必须将 s3_direct_upload 作为插件而不是 GEM 拉取,您可以通过将其放入 gemfile 中来做到这一点:

gem 's3_direct_upload', github: 'waynehoover/s3_direct_upload', ref: '6f6decc75fdf89888d7f729fc89f78e90d91cece'

他们修复了一个以 $utf8 开头的密钥的策略问题,该问题导致更新中出现 POST 错误 403 以及其他一些问题。据说要使用最新版本,您还必须在 file_field_tag 中添加一个额外条目。

data:  url: s3_uploader_url 

例如,我的 file_field_tag 是这样设置的:(我使用 HAML)

= file_field_tag(:file, id: "before_photo", multiple: true, data:  url: s3_uploader_url )

使用 GIT 存储库中的最新版本并将 data: 属性添加到 file_field_tag 后,它开始正常工作。上传正在上传到上传存储桶中的 Amazon S3。

【讨论】:

以上是关于Rails 6,无法将 s3_direct_upload gem 上传到 UPLOAD,查看工作正常的主要内容,如果未能解决你的问题,请参考以下文章

Rails 6.1:Heroku 上的作业将使用 Async 而不是 Sidekiq

Heroku 推送错误:“无法检测到 rake 任务”(Rails 6.1)

Rails 5/6:如何在 webpacker 中包含 JS 函数?

Rails 6.1.0 [webpack-cli] TypeError:无法读取未定义的属性“插件”

Rails 6 无法导入纱线依赖项:未捕获错误:模块解析失败:意外字符“#”(1:0)

无法在 Rails 中更新动作文本富文本