kjohnston/s3_relay

View on GitHub
app/assets/javascripts/s3_relay.coffee

Summary

Maintainability
Test Coverage
displayFailedUpload = (progressColumn=null) ->
  if progressColumn
    progressColumn.text("File could not be uploaded")
  else
    alert("File could not be uploaded")

publishEvent = (target, name, detail) ->
  $(target).trigger( name, detail )

saveUrl = (container, uuid, filename, contentType, publicUrl, progressColumn, fileColumn) ->
  privateUrl = null

  $.ajax
    type: "POST"
    url: "/s3_relay/uploads"
    data:
      parent_type: container.data("parentType")
      parent_id: container.data("parentId")
      association: container.data("association")
      uuid: uuid
      filename: filename
      content_type: contentType
      public_url: publicUrl
    success: (data, status, xhr) ->
      privateUrl = data.private_url
      if privateUrl == null
        displayFailedUpload(progressColumn)
      else
        fileColumn.html("<a href='#{privateUrl}' target='_blank'>#{filename}</a>")

        virtualAttr = "#{container.data('parentType')}[new_#{container.data('association')}_uuids]"
        hiddenField = "<input type='hidden' name='#{virtualAttr}[]' value='#{uuid}' />"
        container.append(hiddenField)
      publishEvent(container, "upload:success", [ uuid, filename, privateUrl ])
    error: (xhr) ->
      console.log xhr.responseText

  return privateUrl

uploadFiles = (container) ->
  fileInput = $("input.s3r-field", container)
  files = fileInput.get(0).files
  uploadFile(container, file) for file in files
  fileInput.val("")

uploadFile = (container, file) ->
  fileName = file.name

  # Assign unique value to each request so Safari doesn't consolidate them
  @s3r_upload_index ||= 0
  @s3r_upload_index += 1

  $.ajax
    type: "GET"
    url: "/s3_relay/uploads/new?s3r_upload_index=#{s3r_upload_index}"
    success: (data, status, xhr) ->
      formData = new FormData()
      xhr = new XMLHttpRequest()
      endpoint = data.endpoint
      disposition = container.data("disposition")

      formData.append("AWSAccessKeyID", data.awsaccesskeyid)
      formData.append("x-amz-server-side-encryption", data.x_amz_server_side_encryption)
      formData.append("key", data.key)
      formData.append("success_action_status", data.success_action_status)
      formData.append("acl", data.acl)
      formData.append("policy", data.policy)
      formData.append("signature", data.signature)
      formData.append("content-type", file.type)
      formData.append("content-disposition", "#{disposition}; filename=\"#{fileName}\"")
      formData.append("file", file)

      uuid = data.uuid

      uploadList = $(".s3r-upload-list", container)
      uploadList.prepend("<tr id='#{uuid}'><td><div class='s3r-file-url'>#{fileName}</div></td><td class='s3r-progress'><div class='s3r-bar' style='display: none;'><div class='s3r-meter'></div></div></td></tr>")
      fileColumn = $(".s3r-upload-list ##{uuid} .s3r-file-url", container)
      progressColumn = $(".s3r-upload-list ##{uuid} .s3r-progress", container)
      progressBar = $(".s3r-bar", progressColumn)
      progressMeter = $(".s3r-meter", progressColumn)

      xhr.upload.addEventListener "progress", (ev) ->
        if ev.loaded
          percentage = ((ev.loaded / ev.total) * 100.0).toFixed(0)
          progressBar.show()
          progressMeter.css "width", "#{percentage}%"
        else
          progressColumn.text("Uploading...")  # IE can't get position

      xhr.onreadystatechange = (ev) ->
        if xhr.readyState is 4
          progressColumn.text("")  # IE can't get position
          progressBar.remove()

          if xhr.status == 201
            contentType = file.type
            publicUrl = $("Location", xhr.responseXML).text()
            saveUrl(container, uuid, fileName, contentType, publicUrl, progressColumn, fileColumn)
          else
            displayFailedUpload(progressColumn)
            console.log $("Message", xhr.responseXML).text()

      xhr.open "POST", endpoint, true
      xhr.send formData
    error: (xhr) ->
      displayFailedUpload()
      console.log xhr.responseText

jQuery ->

  $(document).on "change", ".s3r-field", ->
    $this = $(this)

    if !!window.FormData
      uploadFiles($this.parent())
    else
      $this.hide()
      $this.parent().append("<p>Your browser can't handle file uploads, please switch to <a href='http://google.com/chrome'>Google Chrome</a>.</p>")