6

我在直接使用 ActiveStorage 的 DirectUpload 对象时遇到了一些麻烦。我正在遵循 RailsGuides 中的示例,但我一定遗漏了一些东西。这是我的问题的快速布局:

  1. 我正在努力实现的目标。
  2. 我已经尝试过的事情。
  3. 我目前的问题是什么。

1. 我想要完成的事情

使用 ActiveStroage,我试图让用户在一个简单的表单上选择多个文件,并在选择文件后自动启动直接上传。

这是与最终用户交互的表单:

_media_upload_form.html.erb

<%= form_with url: elements_upload_path, local: true, id: "upload-elements" do %>
  <span class="btn btn-primary btn-file">
    <%= form.file_field :images, multiple: true, direct_upload: true %>
    Select File(s)
  </span>
<% end %>

2. 我已经尝试过的事情

要在用户选择文件后完成自动文件上传,我必须直接与 DirectUpload 对象交互。这个提示可以在ActiveStroage RailsGuides上找到。让它与以下 JS 代码一起使用没有问题:

direct_uploads.js

import { DirectUpload } from "activestorage"

const input = document.querySelector('input[type=file]')

const onDrop = (event) => {
  event.preventDefault()
  const files = event.dataTransfer.files;
  Array.from(files).forEach(file => uploadFile(file))
}

input.addEventListener('change', (event) => {
  Array.from(input.files).forEach(file => uploadFile(file))
  input.value = null
})

const uploadFile = (file) {
  const url = input.dataset.directUploadUrl
  const upload = new DirectUpload(file, url)

  upload.create((error, blob) => {
    if (error) {
      // Handle the error
    } else {
      const hiddenField = document.createElement('input')
      hiddenField.setAttribute("type", "hidden");
      hiddenField.setAttribute("value", blob.signed_id);
      hiddenField.name = input.name
      document.querySelector('form').appendChild(hiddenField)
    }
  })
}

所以,我完成了一个目标。我有文件一被选择就上传。现在,我的下一个目标是访问事件,因此我知道上传何时完成、进度等。知道上传何时完成尤其重要,这样我就可以提交表单并创建对象并将其附加到上传的文件中。所以,使用这样的东西:

addEventListener("direct-upload:progress", event => {
  // ...
})

不起作用,因为我直接访问 DirectUpload 对象。至少,到目前为止,这是我的经验。有点疑惑为什么,我注意到ActiveStroage RailsGuides中的一个细节(我最初忽略了)说您可以通过创建自己的 DirectUpload 上传类来绑定处理程序。因此,使用指南中提供的示例,我创建了以下内容:

my_uploader.js

import { DirectUpload } from "activestorage"

class MyUploader {
  constructor(file, url) {
    this.upload = new DirectUpload(this.file, this.url, this)
  }

  upload(file) {
    this.upload.create((error, blob) => {
      if (error) {
        // Handle the error
      } else {
        const hiddenField = document.createElement('input')
        hiddenField.setAttribute("type", "hidden");
        hiddenField.setAttribute("value", blob.signed_id);
        hiddenField.name = input.name
        document.querySelector('form').appendChild(hiddenField)
      }
    })
  }

  directUploadWillStoreFileWithXHR(request) {
    request.upload.addEventListener("progress",
      event => this.directUploadDidProgress(event))
  }

  directUploadDidProgress(event) {
    console.log("Upload has some progress ....")
  }
}

// ... all ES6 export calls ...

direct_uploads.js

import { DirectUpload } from "activestorage"
import { MyUploader } from "my_uploader"

const input = document.querySelector('input[type=file]')

const onDrop = (event) => {
  event.preventDefault()
  const files = event.dataTransfer.files;
  Array.from(files).forEach(file => uploadFile(file))
}

input.addEventListener('change', (event) => {
  Array.from(input.files).forEach(file => uploadFile(file))
  input.value = null
})

const uploadFile = (file) {
  const url = input.dataset.directUploadUrl
  const upload = new MyUploader(file, url)
}

3. 我目前的问题是什么

我认为我的问题是我错过了一些东西,也许是一步。正在调用 MyUploader 构造函数,但不再上传文件。只有构造函数被调用,就是这样。不再调用实际的上传过程。我不知道如何让自定义 MyUploader 继续上传过程,就像 DirectUpload 对象一样。

任何人都可以提供的任何方向将不胜感激。

谢谢!

4

3 回答 3

5

在离开我的项目一段时间并以新的眼光回来后,我看到了答案。

DirectUpload可以带三个参数,第三个是MyUploader对象。

所以答案是这样的:

const my_uploader = new MyUploader(file, url)
const upload = new DirectUpload(file, url, my_uploader)
于 2018-05-27T05:14:31.167 回答
1

我认为您的初始代码的问题在于this.upload构造函数与您的函数具有相同的名称upload

查看 Rails 源代码,它们使用的代码几乎与您在MyUploader类中使用的代码相同,direct_upload_controller.js但它们调用了该方法start

于 2018-07-30T23:32:36.700 回答
0

这是一个对我有用的例子,感谢 Matt D 和 cseelus。

# app/javascript/src/my_uploader.rb

import { DirectUpload } from "@rails/activestorage"

class MyUploader {
  constructor(file, url) {
    this.upload = new DirectUpload(file, url, this)
  }

  start(input) {
    this.upload.create((error, blob) => {
      if (error) {
        // handle the error
      } else {
        const hiddenField = document.createElement('input')
        hiddenField.setAttribute("type", "hidden");
        hiddenField.setAttribute("value", blob.signed_id);
        hiddenField.name = input.name
        document.querySelector('form').appendChild(hiddenField)
      }
    })
  }

  directUploadWillStoreFileWithXHR(request) {
    request.upload.addEventListener(
      "progress", event => this.directUploadDidProgress(event)
    )
  }

  directUploadDidProgress(event) {
    let progress = ((event.loaded / event.total) * 100).toFixed(1)
    console.log("Progress ... " + progress)
  }
}

export { MyUploader }

然后在其他一些自定义文件中调用上传器:

# app/javascript/src/custom.js

import { MyUploader } from "./my_uploader"

$(document).ready(function() {

  function remoteFileUpload(event) {
    Array.from(this.files).forEach(file => uploadFile(this, file))
  }

  const uploadFile = (input, file) => {
    const url = input.dataset.directUploadUrl
    const my_uploader = new MyUploader(file, url)
    my_uploader.start(input)
  }

  $('input[type=file]').on('change', remoteFileUpload);
});
于 2022-02-19T05:26:11.860 回答