ToastUI 图像编辑器 loadImageFromURL 不起作用
ToastUI Image Editor loadImageFromURL doesn't work
请注意这是一个自我回答的问题。
这个问题是关于 ToastUI 图像编辑器 v3.3.0,但它也可能适用于较新的版本。
当您使用 this official example 加载图像时:
// 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')
编辑器不会加载图像。该函数既不会抛出,也不会 returns 任何指示失败的信息,您也不会收到任何错误消息。它 returns 一个按照文档中指定的方式解决的承诺。
它仅通过在初始配置中指定图像来加载图像,之后您无法更改它:
// 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 上提出,但基本上已被忽略。现在已经一个月了,不幸的是它仍然没有修复。
所以问题是当这个问题存在时,图像编辑器是如何被欺骗工作的。
这里有一个 fiddle 表明它不起作用:https://fiddle.sencha.com/#view/editor&fiddle/2org
长话短说:
这是一个有效的 fiddle:https://fiddle.sencha.com/#view/editor&fiddle/2p0o
长版:
有四个问题:
- 您需要加载初始图像,否则无法使用编辑控件。
- 您需要等到图像编辑器object准备就绪后再调用
loadImageFromURL
,否则您可能会遇到错误或静默失败
- 加载图像时,您需要将新尺寸告知图像编辑器,否则图像将被隐藏或尺寸不正确。
- 如果加载外部图像,外部服务器必须设置
Access-Control-Allow-Origin
header 并明确允许您的域访问它,否则图像编辑器无法访问它。
第一个问题可以通过像这样加载空白图像来解决:
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
返回的承诺解决的 object 并将新旧 width/height 属性传递给 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
header。
例如,在 Amazon S3 上,服务器默认不允许这样做。您必须手动设置服务器以允许您或任何域访问它。参见 here。
如果您使用不同的服务器,您可以将 access-control-allow-origin
设置为 *
,就像维基百科在 this image 上所做的那样。然后您(和图像编辑器)可以从任何域的 JavaScript 访问该图像。
对于那些正在使用 Rails 的人,当涉及到@Forivin 指出的第四个问题时,这就是我为使其正常工作所做的工作。
问题是当 Toast 调用存储在 S3 上的图像时,我会在 Chrome 上收到 CORS 错误,但 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
中添加 rack-proxy gem
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 图像并覆盖现有图像并调整 canvas 的大小。我发现在读取 s3 图像之前安装它时需要稍微延迟。 s3 图像是我在 props.
中传递的预签名 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>
在打开之前将此属性添加到您要编辑的图像标签中:
crossorigin="anonymous"
如此处所述:
https://github.com/nhn/tui.image-editor/issues/68#issuecomment-930106372
请注意这是一个自我回答的问题。
这个问题是关于 ToastUI 图像编辑器 v3.3.0,但它也可能适用于较新的版本。
当您使用 this official example 加载图像时:
// 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')
编辑器不会加载图像。该函数既不会抛出,也不会 returns 任何指示失败的信息,您也不会收到任何错误消息。它 returns 一个按照文档中指定的方式解决的承诺。
它仅通过在初始配置中指定图像来加载图像,之后您无法更改它:
// 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 上提出,但基本上已被忽略。现在已经一个月了,不幸的是它仍然没有修复。
所以问题是当这个问题存在时,图像编辑器是如何被欺骗工作的。
这里有一个 fiddle 表明它不起作用:https://fiddle.sencha.com/#view/editor&fiddle/2org
长话短说:
这是一个有效的 fiddle:https://fiddle.sencha.com/#view/editor&fiddle/2p0o
长版:
有四个问题:
- 您需要加载初始图像,否则无法使用编辑控件。
- 您需要等到图像编辑器object准备就绪后再调用
loadImageFromURL
,否则您可能会遇到错误或静默失败 - 加载图像时,您需要将新尺寸告知图像编辑器,否则图像将被隐藏或尺寸不正确。
- 如果加载外部图像,外部服务器必须设置
Access-Control-Allow-Origin
header 并明确允许您的域访问它,否则图像编辑器无法访问它。
第一个问题可以通过像这样加载空白图像来解决:
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
返回的承诺解决的 object 并将新旧 width/height 属性传递给 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
header。
例如,在 Amazon S3 上,服务器默认不允许这样做。您必须手动设置服务器以允许您或任何域访问它。参见 here。
如果您使用不同的服务器,您可以将 access-control-allow-origin
设置为 *
,就像维基百科在 this image 上所做的那样。然后您(和图像编辑器)可以从任何域的 JavaScript 访问该图像。
对于那些正在使用 Rails 的人,当涉及到@Forivin 指出的第四个问题时,这就是我为使其正常工作所做的工作。
问题是当 Toast 调用存储在 S3 上的图像时,我会在 Chrome 上收到 CORS 错误,但 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 图像并覆盖现有图像并调整 canvas 的大小。我发现在读取 s3 图像之前安装它时需要稍微延迟。 s3 图像是我在 props.
中传递的预签名 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>
在打开之前将此属性添加到您要编辑的图像标签中:
crossorigin="anonymous"
如此处所述: https://github.com/nhn/tui.image-editor/issues/68#issuecomment-930106372