Загрузка файлов с помощью ActiveStorage в Vue.js

Я хочу добавить загрузку файлов в модель карты (включая один и несколько файлов) с помощью новой функции Rails 5.2 ActiveStorage. Я сильнее разбираюсь в Rails и использую Vue.js для этого - раньше я бы использовал Paperclip для этого. Однако я установил файл config/storage.yml и необходимые миграции для ActiveStorage. Я также установил связь между на Карте и обновил CardController, чтобы разрешить files: [].

Созданный мной компонент card.vue в настоящее время работает хорошо; Карточка может иметь название и описание, и описание может быть обновлено. Эти записи сохраняются в базе данных. Моя проблема заключается в том, чтобы выяснить, как привязать загрузку файлов к карте, и логику, необходимую для загрузки нескольких файлов при сохранении.

В настоящее время я использую <input name="files" type="file" data-direct-upload-url="/rails/active_storage/direct_uploads" direct_upload="true" /> для создания поля ввода на карточке. Однако после выбора изображения PDF на моем локальном компьютере и нажатия кнопки «Сохранить» журналы показывают, что ничего не происходит [в отношении вставки новой строки в active_storage_attachments или создания большого двоичного объекта]. Как я могу расширить метод сохранения, чтобы принять файл?

card.rb

class Card < ApplicationRecord
  has_many_attached :files
end

CardController

class CardsController < ApplicationController
  private
    def card_params
      params.require(:card).permit(:list_id, :title, :position, :description, files: [])
    end
end

card.vue

<template>
  <div>
    <div @click="editing=true" class="card card-body">
      <h4>
        {{card.title}}
      </h4>
    </div>

    <div v-if="editing" class="modal-backdrop show"></div>

    <div v-if="editing" @click="closeModal" class="modal show" style="display: block">
      <div class="modal-dialog">
        <div class="modal-content">
          <div class="modal-header">
            <div>
              <h4>
                {{card.title}}
              </h4>
            </div>
          </div>
          <div class="modal-body">
            <div>
              <h5>{{card.description}}</h5>
            </div>
            <textarea v-model="description" class="form-control"></textarea>
          </div>
          <div class="modal-footer">
            <input name="files" type="file" data-direct-upload-url="/rails/active_storage/direct_uploads" direct_upload="true" />
            <button @click="save" type="button" class="button button-secondary">Save changes</button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>

export default {
  props: ["card", "list"],

  data: function() {
    return {
      editing: false,
      title: this.card.title,
      description: this.card.description,
      files: []
    }
  },

  methods: {
    save: function() {
      var data = new FormData
      data.append("card[title]", this.title)
      data.append("card[description]", this.description)

      Rails.ajax({
        url: `/cards/${this.card.id}`,
        type: "PATCH",
        data: data,
        dataType: "json",
        success: (data) => {
          const list_index = window.store.lists.findIndex((item) => item.id == this.list.id)
          const card_index = window.store.lists[list_index].cards.findIndex((item) => item.id == this.card.id)
          window.store.lists[list_index].cards.splice(card_index, 1, data)

          this.editing = false
        }
      })
    }
  }
}
</script>

person VegaStudios    schedule 24.11.2018    source источник


Ответы (2)


Попробуйте изменить имя ввода, чтобы оно соответствовало разрешенным параметрам:

<input name="card[files][]" type="file" ... />

Обновлено:

Как я ответил, вам нужно изменить свои параметры, чтобы они соответствовали разрешенным.
Теперь, используя Vue, вы можете удалить name и добавить файлы в FormData при изменении события следующим образом:

<template>
  <div>
    ...
    <div class="modal-footer">
      <input ref="files" type="file" @change="onFilesChange"
        data-direct-upload-url="/rails/active_storage/direct_uploads"
        direct_upload="true" multiple />
      <button @click="save" type="button" class="button button-secondary">Save changes</button>
    </div>
    ...   
  </div>
</template>

<script>

export default {
  props: ["card", "list"],

  data: function() {
    return {
      editing: false,
      title: this.card.title,
      description: this.card.description,
      form: new FormData
    }
  },

  methods: {
    onFilesChange: function() {
      let files = this.$refs.files.files
      for(let file of files) {
        this.form.append('card[files][]', file)
      }
    },
    save: function() {
      this.form.append("card[title]", this.title)
      this.form.append("card[description]", this.description)

      Rails.ajax({
        url: `/cards/${this.card.id}`,
        type: "PATCH",
        data: this.form,
        dataType: "json",
        success: (data) => {
          // ...
          this.editing = false
        }
      })
    }
  }
}
</script>

Примечание: не забывайте свойство multiple на входе, если вы хотите загрузить несколько файлов.

person Sovalina    schedule 24.11.2018
comment
Хорошо, позвольте мне попробовать, вы думаете, этого достаточно, чтобы привязать файл? - person VegaStudios; 24.11.2018
comment
Журналы, возвращаемые после обновления поля ввода и попытки прикрепить файл, не показывают ничего, относящегося к создаваемому BLOB-объекту. Все еще немного застрял в том, как привязать файл - person VegaStudios; 25.11.2018
comment
Привет, @Sovalina, этот обновленный фрагмент отлично работает. Он вставляет файл в ActiveStorage :: Blob и ActiveStorage :: Attachment, и через мою ассоциацию делает record_id вложения идентификатором карты. Сейчас я попытаюсь отобразить этот файл для предварительного просмотра. - person VegaStudios; 01.12.2018
comment
Есть ли у вас какие-либо советы? - person VegaStudios; 01.12.2018
comment
@VegaStudios, рад слышать, что это работает. Насчет подсказок, вы про как предварительно просмотреть файлы перед загрузкой? - person Sovalina; 01.12.2018
comment
Да точно, ссылки в документе используют image_url, и перевод этого для работы во Vue сначала не работал. Мне нужно использовать привязку или что-то еще? Просто хочу отобразить файл для загрузки в модальном окне (отдельно от кнопки ввода) - person VegaStudios; 01.12.2018
comment
У меня прямая загрузка работает нормально, а вложения сохраняются в S3. Вы знаете, @sovalina, как я могу отобразить ссылку на card[files][] в <template> представлении? - person VegaStudios; 14.12.2018

Я обычно делаю это так:

let input_file = document.querySelectorAll('input[type="file"]')

// ...

formData.append('card[files]', input_file.files[0]) // or loop through if need be, also it might be card[files][0], I can't remember

Не уверен, нужно ли вам много файлов или только один. И это при условии, что вы не хотите отправлять его асинхронно, как кажется в вашем коде.

person okay56k    schedule 29.11.2018