Редактор изображений ToastUI loadImageFromURL не работает

Обратите внимание, что это вопрос с самостоятельным ответом.

Этот вопрос касается редактора изображений ToastUI версии 3.3.0, но он также может относиться к более новым версиям.

При загрузке изображения с помощью этот официальный пример:

// Create image editor
var imageEditor = new tui.component.ImageEditor('#my-image-editor canvas', {
    cssMaxWidth: 1000, // Component default value: 1000
    cssMaxHeight: 800  // Component default value: 800
});

// Load image
imageEditor.loadImageFromURL('img/sampleImage.jpg', 'My sample image')

Редактор не загружает изображение. Функция не выдает и не возвращает ничего, указывающего на сбой, и вы не получаете никаких сообщений об ошибках. Он возвращает обещание, которое разрешается, как указано в документации.

Он только загружает изображение, указав его в начальной конфигурации, и вы не можете изменить его впоследствии:

// Create image editor
var imageEditor = new tui.component.ImageEditor('#my-image-editor canvas', {
     includeUI: {
         loadImage: {
             path: 'img/sampleImage.jpg',
             name: 'My sample image'
         },
     },
    cssMaxWidth: 1000, // Component default value: 1000
    cssMaxHeight: 800  // Component default value: 800
});

Похоже, что функция loadImageFromURL не работает и, согласно у других пользователей loadImageFromFile такая же проблема.

Вопросы по этому поводу поднимались на GitHub, но в основном игнорировались. Прошел уже месяц, а, к сожалению, до сих пор не исправили.

Итак, вопрос в том, как заставить редактор изображений работать, пока эта проблема существует.

Вот скрипт, показывающий, что он не работает: https://fiddle.sencha.com/#view/editor&fiddle/2org


person Forivin    schedule 11.01.2019    source источник


Ответы (2)


TL;DR:
Вот рабочая скрипка: https://fiddle.sencha.com/#view/editor&fiddle/2p0o


Длинная версия:

Есть четыре проблемы:

  • Вам нужно загрузить исходное изображение, иначе вы не сможете использовать элементы управления редактированием.
  • Вам нужно подождать, пока объект редактора изображений будет готов, прежде чем вызывать loadImageFromURL, иначе вы можете получить ошибку или тихий сбой.
  • Когда изображение загружается, вам нужно сообщить редактору изображения новый размер, иначе изображение будет скрыто или будет иметь неправильный размер.
  • Если вы загружаете внешнее изображение, внешний сервер должен установить заголовок Access-Control-Allow-Origin и явно разрешить вашему домену доступ к нему, иначе редактор изображений не сможет получить к нему доступ.

Эту первую проблему можно решить, загрузив пустое изображение следующим образом:

var imageEditor = new tui.ImageEditor('#tui-image-editor-container', {
    includeUI: {
        loadImage: {
            path: 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',
            name: 'Blank'
        },
        theme: whiteTheme,
        menuBarPosition: 'bottom'
    },
    cssMaxWidth: 700,
    cssMaxHeight: 700
});

Вторую проблему можно решить, дождавшись выхода графического редактора из состояния блокировки с помощью недокументированного функционала. Вы можете исправить свой loadImageFromURL во время выполнения следующим образом:

imageEditor.loadImageFromURL = (function() {
    var cached_function = imageEditor.loadImageFromURL;
    function waitUntilImageEditorIsUnlocked(imageEditor) {
        return new Promise((resolve,reject)=>{
            const interval = setInterval(()=>{
                if (!imageEditor._invoker._isLocked) {
                    clearInterval(interval);
                    resolve();
                }
            }, 100);
        })
    }
    return function() {
        return waitUntilImageEditorIsUnlocked(imageEditor).then(()=>cached_function.apply(this, arguments));
    };
})();

Третью проблему можно решить, взяв объект, с которым разрешается обещание, возвращенное loadImageFromURL, и передав новое и старое свойства ширины/высоты в функцию ui.resizeEditor следующим образом:

imageEditor.loadImageFromURL("https://upload.wikimedia.org/wikipedia/en/thumb/8/80/Wikipedia-logo-v2.svg/526px-Wikipedia-logo-v2.svg.png", "SampleImage").then(result=>{
    imageEditor.ui.resizeEditor({
        imageSize: {oldWidth: result.oldWidth, oldHeight: result.oldHeight, newWidth: result.newWidth, newHeight: result.newHeight},
    });
}).catch(err=>{
    console.error("Something went wrong:", err);
})

Четвертая проблема может немного сбить с толку. Позволь мне объяснить. На веб-сайтах вы можете включить практически любое внешнее изображение, которое вы хотите, используя тег <img>, но если вы хотите получить доступ к внешнему изображению с помощью JavaScript, сервер, предоставляющий изображение, должен явно разрешить вам сделать это с помощью заголовка access-control-allow-origin. Например, на Amazon S3 серверы не разрешают это по умолчанию. Вы должны вручную настроить сервер, чтобы ваш или любой домен мог получить к нему доступ. См. здесь. Если вы используете другой сервер, вы можете, например, установить access-control-allow-origin на *, как это сделала Википедия на это изображение. Затем вы (и редактор изображений) можете получить доступ к этому изображению из JavaScript любого домена.

person Forivin    schedule 11.01.2019
comment
Спасибо за прекрасный ответ. Это сэкономило мне много времени. @Форивин - person Vishnudev; 27.03.2020
comment
Сумасшедший, что это не встроено в библиотеку. Большое спасибо за ваш ответ. - person user2031423; 21.07.2020
comment
Для последней сборки imageEditor._isLocked работает, а imageEditor._invoker._isLocked нет. - person Steven Spungin; 01.04.2021

Для тех, кто использует Rails, когда дело дошло до четвертой проблемы, указанной @Forivin, вот что я сделал, чтобы заставить ее работать.

Проблема в том, что когда Toast вызывает изображение, хранящееся на S3, я получаю ошибку CORS в Chrome, но с firefox все в порядке. Об этом есть много статей, по сути, я обнаружил, что лучший способ — использовать прокси в моем коде. У меня все еще может быть источник CORS, указывающий на мой хост, и, поскольку вызов поступает с моего хоста через прокси, S3 и Chrome довольны. Моя конфигурация S3 CORS выглядит так (разрешает поддомены):

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>http://*.mycompany.com</AllowedOrigin>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>DELETE</AllowedMethod>
    <AllowedMethod>HEAD</AllowedMethod>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

В вашем проекте rails сделайте следующее:

Добавьте гем стоечного прокси в свой Gemfile

gem 'rack-proxy'

Создайте прокси-файл. Путь s3 кодируется URI и добавляется в конец маршрута. Маршрут просто используется для прокси, поэтому он может быть любым, так как он будет перенаправлен на s3.

app/proxy/s3_proxy.rb

class S3Proxy < Rack::Proxy

  def perform_request(env)
    if env['REQUEST_PATH'] =~ %r{^/my/dummy/path}
      s3_path = CGI.unescape(env['REQUEST_PATH'][15..-1])

      uri = URI.parse(s3_path)
      env['HTTP_HOST'] = uri.host
      env['SERVER_PORT'] = uri.port
      env['REQUEST_PATH'] = s3_path
      env['REQUEST_URI'] = s3_path
      env['PATH_INFO'] = s3_path
      env['rack.url_scheme'] = 'https'

      super(env)
    else
      @app.call(env)
    end
  end

end

Добавить в файл application.rb:

require "./app/proxy/s3_proxy"

class Application < Rails::Application
  ...

  config.middleware.use S3Proxy
end

routes.rb

get "/my/dummy/path/:s3_url", to: "my_controller#dummy_path"

Метод контроллера в my_controller.rb. Неважно, что здесь отображается, так как оно будет перенаправлено через прокси. Вероятно, мы могли бы обойтись без метода, поскольку прокси все равно изменится.

  def dummy_path
    render plain: ""
  end

И, наконец, в моем коде Vue я вызываю редактор Toast, сначала заполняя его пустым белым изображением. Затем, когда компонент смонтирован, я загружаю образ s3, перезаписываю существующий образ и изменяю размер холста. Я обнаружил, что мне нужна небольшая задержка, когда он монтируется перед чтением образа s3. Изображение s3 — это предварительно подписанный URL-адрес, который я передаю в реквизитах.

<template lang="pug">
.v-image-editor-tool
  tui-image-editor(:include-ui='useDefaultUI' :options="editorOptions" ref="tuiImageEditor")
</template>

<script lang="coffee">
import { ImageEditor } from '@toast-ui/vue-image-editor'
import 'tui-image-editor/dist/tui-image-editor.min.css'

export default app =
  props: ['imageUrl']
  data: ->
    useDefaultUI: true
    editorOptions:
      cssMaxWidth: 700
      cssMaxHeight: 700
      usageStatistics: false
      includeUI:
        loadImage:
          path: 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
          name: 'Blank'
        menuBarPosition: 'bottom'

  mounted: ->
    fn = => this.$refs.tuiImageEditor.invoke('loadImageFromURL', @imageUrl, 'Image').then (result) =>
      this.$refs.tuiImageEditor.invoke('ui.resizeEditor', { imageSize: { newWidth: result.newWidth, newHeight: result.newHeight }})
    setTimeout(fn, 600)

  components:
    'tui-image-editor': ImageEditor
</script>

person rlawrenz    schedule 12.12.2019