Spec-Zone.ru › Phoenix

Внешние загрузки

Исходный код

Это руководство продолжает настройку, начатую в руководстве сервера по загрузкам.

Загрузку файлов во внешние облачные сервисы, такие как Amazon S3, Google Cloud и другие, можно реализовать с помощью параметра :external в allow_upload/3.

Передайте функцию с арностью 2, чтобы сервер мог генерировать метаданные для каждой записи загрузки. Эти метаданные передаются пользовательской функции JavaScript на клиенте.

Как правило, при вызове этой функции вы будете создавать предварительно подписанный URL, соответствующий вашему поставщику облачного хранилища. Он обеспечит конечному пользователю временный доступ для непосредственной загрузки данных в ваше облачное хранилище.

Загрузка по HTTP частями

Для любого сервиса, который поддерживает загрузку больших файлов с помощью HTTP-запросов с передачей по частям и заголовками Content-Range, можно использовать библиотеку UpChunk JS от Mux, которая выполнит всю сложную работу по загрузке файла. Для загрузки небольших файлов или быстрого начала работы рассмотрите вариант непосредственной загрузки в S3.

Вам нужно лишь связать экземпляр UpChunk с обратными вызовами UploadEntry в LiveView, а LiveView позаботится обо всем остальном.

Установите UpChunk, сохранив его содержимое в assets/vendor/upchunk.js или установив его с помощью npm:

$ npm install --prefix assets --save @mux/upchunk

Настройте средство загрузки в Phoenix.LiveView.mount/3:

def mount(_params, _session, socket) do
  {:ok,
   socket
   |> assign(:uploaded_files, [])
   |> allow_upload(:avatar, accept: :any, max_entries: 3, external: &presign_upload/2)}
end

Передайте параметр :external в Phoenix.LiveView.allow_upload/3. Для него требуется функция с арностью 2, которая создает подписанный URL, куда клиент будет отправлять байты записи загрузки. Эта функция должна возвращать либо {:ok, meta, socket}, либо {:error, meta, socket}, где meta должен быть картой.

Например, если вы используете контекст, предоставляющий функцию start_session, то можете написать что-то вроде этого:

defp presign_upload(entry, socket) do
  {:ok, %{"Location" => link}} =
    SomeTube.start_session(%{
      "uploadType" => "resumable",
      "x-upload-content-length" => entry.client_size
    })

  {:ok, %{uploader: "UpChunk", entrypoint: link}, socket}
end

Наконец, на стороне клиента мы используем UpChunk, чтобы создать загрузку по временному URL, сгенерированному на сервере, и привязать обработчики ее событий к обратным вызовам записи:

import * as UpChunk from "@mux/upchunk"

let Uploaders = {}

Uploaders.UpChunk = function(entries, onViewError){
  entries.forEach(entry => {
    // create the upload session with UpChunk
    let { file, meta: { entrypoint } } = entry
    let upload = UpChunk.createUpload({ endpoint: entrypoint, file })

    // stop uploading in the event of a view error
    onViewError(() => upload.pause())

    // upload error triggers LiveView error
    upload.on("error", (e) => entry.error(e.detail.message))

    // notify progress events to LiveView
    upload.on("progress", (e) => {
      if(e.detail < 100){ entry.progress(e.detail) }
    })

    // success completes the UploadEntry
    upload.on("success", () => entry.progress(100))
  })
}

// Don't forget to assign Uploaders to the liveSocket
let liveSocket = new LiveSocket("/live", Socket, {
  uploaders: Uploaders,
  params: {_csrf_token: csrfToken}
})

Непосредственная загрузка в S3

Согласно разделу часто задаваемых вопросов S3, максимальный размер объекта, который можно загрузить в S3 за один запрос PUT, составляет 5 ГБ. Для загрузки файлов большего размера рассмотрите разбиение на части, описанное выше.

В этом руководстве предполагается, что у вас уже есть корзина S3 с правильной конфигурацией CORS, которая разрешает загрузку непосредственно в корзину.

Пример конфигурации CORS:

[
    {
        "AllowedHeaders": [ "*" ],
        "AllowedMethods": [ "PUT", "POST" ],
        "AllowedOrigins": [ "*" ],
        "ExposeHeaders": []
    }
]

Вместо этого в "allowedOrigins" можно указать свой домен. Дополнительную информацию о настройке CORS для корзин S3 можно найти на сайте AWS.

Чтобы применить все ограничения для файлов при загрузке в S3, необходимо выполнить POST-запрос multipart form с данными файла. Перед продолжением подготовьте следующие сведения о S3:

  1. aws_access_key_id
  2. aws_secret_access_key
  3. bucket_name
  4. region

Сначала реализуем часть LiveView:

def mount(_params, _session, socket) do
  {:ok,
    socket
    |> assign(:uploaded_files, [])
    |> allow_upload(:avatar, accept: :any, max_entries: 3, external: &presign_upload/2)}
end

defp presign_upload(entry, socket) do
  uploads = socket.assigns.uploads
  bucket = "phx-upload-example"
  key = "public/#{entry.client_name}"

  config = %{
    region: "us-east-1",
    access_key_id: System.fetch_env!("AWS_ACCESS_KEY_ID"),
    secret_access_key: System.fetch_env!("AWS_SECRET_ACCESS_KEY")
  }

  {:ok, fields} =
    SimpleS3Upload.sign_form_upload(config, bucket,
      key: key,
      content_type: entry.client_type,
      max_file_size: uploads[entry.upload_config].max_file_size,
      expires_in: :timer.hours(1)
    )

  meta = %{uploader: "S3", key: key, url: "http://#{bucket}.s3-#{config.region}.amazonaws.com", fields: fields}
  {:ok, meta, socket}
end

Здесь мы реализовали функцию presign_upload/2 и передали ее в :external как захваченную анонимную функцию. Она генерирует предварительно подписанный URL для загрузки и возвращает результат :ok с полезной нагрузкой, содержащей метаданные для клиента, а также неизмененный сокет.

Затем добавим отсутствующий модуль SimpleS3Upload для генерации предварительно подписанных URL для S3. Создайте файл simple_s3_upload.ex. Возьмите содержимое файла из этого модуля без зависимостей под названием SimpleS3Upload, написанного Крисом Маккордом.

Совет: если вы столкнулись с ошибками в модуле :crypto или блокировкой ACL со стороны S3, решения можно найти в комментариях к gist выше.

Затем добавим средство загрузки на стороне клиента JavaScript. Метаданные должны содержать ключ :uploader, указывающий имя клиентского средства загрузки JavaScript. В данном случае это "S3", как показано выше.

Добавьте новый файл uploaders.js в следующий каталог assets/js/ рядом с app.js. Содержимое этого клиентского средства загрузки S3:

let Uploaders = {}

Uploaders.S3 = function(entries, onViewError){
  entries.forEach(entry => {
    let formData = new FormData()
    let {url, fields} = entry.meta
    Object.entries(fields).forEach(([key, val]) => formData.append(key, val))
    formData.append("file", entry.file)
    let xhr = new XMLHttpRequest()
    onViewError(() => xhr.abort())
    xhr.onload = () => xhr.status === 204 ? entry.progress(100) : entry.error()
    xhr.onerror = () => entry.error()
    xhr.upload.addEventListener("progress", (event) => {
      if(event.lengthComputable){
        let percent = Math.round((event.loaded / event.total) * 100)
        if(percent < 100){ entry.progress(percent) }
      }
    })

    xhr.open("POST", url, true)
    xhr.send(formData)
  })
}

export default Uploaders;

Мы определяем функцию Uploaders.S3, которая получает наши записи. Затем она выполняет AJAX-запрос для каждой записи, используя функции entry.progress() и entry.error(), чтобы передавать события загрузки обратно в LiveView. Имя средства загрузки должно совпадать с именем, которое мы возвращаем в метаданных :uploader в LiveView.

Наконец, перейдите в app.js и добавьте ключ uploaders: Uploaders в конструктор LiveSocket, чтобы указать Phoenix, где искать средства загрузки, возвращенные во внешних метаданных.

// for uploading to S3
import Uploaders from "./uploaders"

let liveSocket = new LiveSocket("/live",
   Socket, {
     params: {_csrf_token: csrfToken},
     uploaders: Uploaders
  }
)

Теперь значение "S3", возвращаемое сервером, будет соответствовать значению на клиенте. Чтобы отладить JavaScript на стороне клиента при загрузке, откройте инструменты разработчика браузера и просмотрите вкладку консоли или сети, чтобы увидеть журналы ошибок.

Непосредственная загрузка в S3-совместимое хранилище

В этом разделе предполагается, что вы правильно установили и настроили в проекте ExAws и ExAws.S3 и можете выполнять примеры на этой странице без ошибок.

Большинство S3-совместимых платформ, например Cloudflare R2, не поддерживают POST при загрузке файлов, поэтому вместо подписанного POST нам нужно использовать PUT с подписанным URL и отправлять файл напрямую в сервис. Для этого нужно изменить функцию presign_upload/2 и Uploaders.S3, выполняющий загрузку.

Новый presign_upload/2:

def presign_upload(entry, socket) do
  config = ExAws.Config.new(:s3)
  bucket = "bucket"
  key = "public/#{entry.client_name}"

  {:ok, url} =
    ExAws.S3.presigned_url(config, :put, bucket, key,
      expires_in: 3600,
      query_params: [{"Content-Type", entry.client_type}]
    )
   {:ok, %{uploader: "S3", key: key, url: url}, socket}
end

Новый Uploaders.S3:

Uploaders.S3 = function (entries, onViewError) {
  entries.forEach(entry => {
    let xhr = new XMLHttpRequest()
    onViewError(() => xhr.abort())
    xhr.onload = () => xhr.status === 200 ? entry.progress(100) : entry.error()
    xhr.onerror = () => entry.error()

    xhr.upload.addEventListener("progress", (event) => {
      if(event.lengthComputable){
        let percent = Math.round((event.loaded / event.total) * 100)
        if(percent < 100){ entry.progress(percent) }
      }
    })

    let url = entry.meta.url
    xhr.open("PUT", url, true)
    xhr.send(entry.file)
  })
}
← Предыдущая страница Привязки
Следующая страница → Привязки форм

Пакет Hex Предпросмотр Hex (текущий файл) Просмотреть llms.txt Скачать версию ePub

Создано с помощью ExDoc (v0.40.3) для языка программирования Elixir

© 2014 Chris McCord
Licensed under the MIT License.
https://phoenix-live-view.hexdocs.pm/external-uploads.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API