Editor.md 是一款开源的、可嵌入的 Markdown 在线编辑器(组件),基于 CodeMirror、jQuery 和 Marked 构建。
但是,在使用Editor.md的时候配置图片上传时候遇到了一个问题:在图片上传时,出现跨域问题!
Uncaught DOMException: Blocked a frame with origin “http://127.0.0.1:8848” from accessing a cross-origin frame.
at HTMLIFrameElement.uploadIframe.onload (http://127.0.0.1:8848/YLBlog/plugins/image-dialog/image-dialog.js:164:129)
但是,我服务器后台能接收到前端传来的参数
由此证明,图片数据是能传递过来服务器的,但是为什么前端会报跨域问题呢?
首先看一下报错的代码,打开 image-dialog.js:164
uploadIframe.onload = function() {
loading(false);
var body = (uploadIframe.contentWindow ? uploadIframe.contentWindow : uploadIframe.contentDocument).document.body;
var json = (body.innerText) ? body.innerText : ( (body.textContent) ? body.textContent : null);
json = (typeof JSON.parse !== "undefined") ? JSON.parse(json) : eval("(" + json + ")");
if(!settings.crossDomainUpload)
{
if (json.success === 1)
{
dialog.find("[data-url]").val(json.url);
}
else
{
alert(json.message);
}
}
return false;
};
164行的代码是
var body = (uploadIframe.contentWindow ? uploadIframe.contentWindow : uploadIframe.contentDocument).document.body;
第一眼看到这行代码,不知道是获取什么,先把 uploadIframe.contentWindow 和 uploadIframe.contentDocument 这两个打印输出一下,看一下是什么东西,在代码里面加入console.log()代码,再上传图片看看!
由于调用uploadIframe.contentWindow会报跨域错误,所以,只能看到uploadIframe.contentDocument的值为null
所以,无法得到uploadIframe.contentWindow的值,经过一番百度搜索,知道这个值其实就是Editor.md配置的服务器请求接口值
然后,再进一步了解到,image-dialog.js这个文件访问图片上传中的数据造成的跨域问题。
知道了问题所在,就好办了,开始分析源码。
在该函数的接下一行:会调用该函数
dialog.find("[type=\"submit\"]").bind("click", submitHandler).trigger("click");
首先,是寻找一个type类型是submit提交的元素,然后绑定事件click点击调用submitHandler函数
我们来输出一下,这行代码找到的sunmit的元素是什么:
console.log(dialog.find("[type=\"submit\"]"));
大概就是创建一个表单,上传到接口,接着继续在image-dialog.js中寻找与表单有关的代码,相关的有如下代码:
var dialogContent = ( (settings.imageUpload) ? "
分析一下,应该就是获取前端的表单,然后再创建一个表单提交到后端中,但是由于前端的表单和image-dialog.js不是同源,所以会报跨域错误!!
既然用表单提交会报错,那么就采用ajax来提交!!
首先,去掉前端js中的图片接口配置!
/**上传图片相关配置如下*/
imageUpload : true,
imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
// imageUploadURL : baseUrl+'/upload/test',//注意你后端的上传图片服务地址
为什么还有guid呢,分析了image-dialog.js发现,这是自动生成的时间戳!
var guid = (new Date).getTime();
var action = settings.imageUploadURL + (settings.imageUploadURL.indexOf("?") >= 0 ? "&" : "?") + "guid=" + guid;
我们只需要修改action方法即可!
未修改之前:
if (settings.crossDomainUpload)
{
action += "&callback=" + settings.uploadCallbackURL + "&dialog_id=editormd-image-dialog-" + guid;
}
修改之后:
action ="";
此时还需要关闭本地上传的默认提交功能:
var submitHandler = function() {
var uploadIframe = document.getElementById(iframeName);
uploadIframe.onload = function() {
loading(false);
var body = (uploadIframe.contentWindow ? uploadIframe.contentWindow : uploadIframe.contentDocument).document.body;
var json = (body.innerText) ? body.innerText : ( (body.textContent) ? body.textContent : null);
json = (typeof JSON.parse !== "undefined") ? JSON.parse(json) : eval("(" + json + ")");
if(!settings.crossDomainUpload)
{
if (json.success === 1)
{
dialog.find("[data-url]").val(json.url);
}
else
{
alert(json.message);
}
}
return false;
};
loading(false);//新增这里
return false;//新增这里
};
此时,我们应该留意到uploadIframe.onload = function()里面有三个参数:success、message、url 对应Editor.md中提示的参数
// {
// success : 0 | 1, // 0 表示上传失败,1 表示上传成功
// message : "提示的信息,上传成功或上传失败及错误信息等。",
// url : "图片地址" // 上传成功时才返回
// }
继续看uploadIframe.onload = function() 里面的代码:
loading(false);//加载关闭
var body = (uploadIframe.contentWindow ? uploadIframe.contentWindow : uploadIframe.contentDocument).document.body;//跨域获取action的接口数据
var json = (body.innerText) ? body.innerText : ( (body.textContent) ? body.textContent : null);
json = (typeof JSON.parse !== "undefined") ? JSON.parse(json) : eval("(" + json + ")");
if(!settings.crossDomainUpload)
{
if (json.success === 1)
{
dialog.find("[data-url]").val(json.url);//???在函数头打印输出一下
}
else
{
alert(json.message);
}
}
return false;
发现find寻找这个元素是 图片地址 那一栏!作用就是将服务器返回的json格式数据url填入!
分析完毕,开始采用ajax代替原来的form表单提交!
首先把var submitHandler = function()函数的代码都注释掉:
var submitHandler = function() {
// var uploadIframe = document.getElementById(iframeName);
// uploadIframe.onload = function() {
// loading(false);
// var body = (uploadIframe.contentWindow ? uploadIframe.contentWindow : uploadIframe.contentDocument).document.body;
// var json = (body.innerText) ? body.innerText : ( (body.textContent) ? body.textContent : null);
// json = (typeof JSON.parse !== "undefined") ? JSON.parse(json) : eval("(" + json + ")");
// if(!settings.crossDomainUpload)
// {
// if (json.success === 1)
// {
// dialog.find("[data-url]").val(json.url);
// }
// else
// {
// alert(json.message);
// }
// }
// return false;
// };
loading(false);
return false;
};
改为:
var submitHandler = function() {
var form = dialog.find("[enctype=\"multipart/form-data\"]")[0];
var formData = new FormData(form);
$.ajax({
type: 'post',
// url: "http://localhost:8080/upload/test", // 你的服务器端的图片上传接口。如果你设置了 imageUploadURL,那么可以使用下面的方式
url: settings.imageUploadURL + (settings.imageUploadURL.indexOf("?") >= 0 ? "&" : "?") + "guid=" + guid,
data: formData,
cache: false,
processData: false,
contentType: false,
success: function(data, textStatus, jqXHR) {
// console.log(data);
// console.log(textStatus);
// console.log(jqXHR);
if (data.success === 1) {
// 上传成功
dialog.find("[data-url]").val(data.url); // 设置图片地址
}
else {
alert(data.message); // 上传失败,弹出警告信息
}
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
// console.log(XMLHttpRequest);
// console.log(textStatus);
// console.log(errorThrown);
}
});
loading(false);
return false;
};