記事のディレクトリ
使用Editor.md画像アップロードの問題と解決策
春ブーツ2.2.4.RELEASE
問題と解決策
情報:前と後のプロジェクトでは、クロスドメインを含む、分離の終わりです。
ファイルが保存されるフォルダの試料を用いた試験では、例Editor.md「画像upload.html」を提供しました。
「ローカルアップロード」ボタンをクリックして、画像やアップロードを選択し、サーバは画像を受け取ることができます。
画像が正常にアップロードし、正しいデータをサーバー側応答した場合、通常の状況下では、その後、追加の画像ダイアログ絵アドレスが大幅にサーバーのURLに背中を返却する必要があります。
私は成功した写真をアップロードするときただし、クライアント(ブラウザ)は、セキュリティエラープロンプトが表示され、「例外SecurityErrorを:許可は、アクセスプロパティに拒否された」ドキュメント「ONクロスオリジンのオブジェクト以下のように、」:
する旨の「クロスオリジン・オブジェクトの」ドキュメント:「アクセス許可のアクセスプロパティに拒否されたとSecurityError」メッセージが表示されたら、推測「クロスオリジンのオブジェクトは、」「クロスは、ソースオブジェクト、」その後で、「ソースオブジェクト間のアクセスを持っていません。」
画像dialog.jsファイルを開き、ライン164を探し、してみてくださいここで見ることができます:
var body = (uploadIframe.contentWindow ? uploadIframe.contentWindow : uploadIframe.contentDocument).document.body;
コードの文字通りの意味から、それは推測取るべきであるdocument
のをbody
。
Aはファジービット?ああ、又は第一uploadIframe.contentWindow
およびuploadIframe.contentDocument
コンソールに出力し、彼らが何であるかを参照してください。修正画像dialog.jsファイルとして、次のとおりです。
if(log) { // 自定义的变量,var log = true;。如果后期处于调试阶段,又不想打印这些信息,将其设置 false 即可。
console.log("uploadIframe.contentWindow", uploadIframe.contentWindow);
console.log("uploadIframe.contentDocument", uploadIframe.contentDocument);
}
var body = (uploadIframe.contentWindow ? uploadIframe.contentWindow : uploadIframe.contentDocument).document.body;
修正後は、意図的にプロンプトをトリガ、再び完全なとブラウザに保存され、アップロードの写真です:
少し手がかりは、uploadIframe.contentDocument
されnull
、その後、我々は最初にそれに注意を払うことができません。
再看看 uploadIframe.contentWindow
指向一个 Window
,且 URL 指向服务器端的上传接口,刚好也是 image-upload.html 文件中 editormd
配置的 imageUploadURL
参数值:
var testEditor = editormd("test-editormd", {
... // 其他配置
imageUpload : true,
imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
imageUploadURL : "http://localhost:8080/attachment/image/upload",
...
});
通过选取页面中的元素发现,以上提到 URL 刚好和图片上传的表单 action
一样:
由此猜想,上文提及的安全错误“SecurityError: Permission denied to access property “document” on cross-origin object”是由于 image-dialog.js 文件中的代码尝试访问图片上传的表单中的元素导致的:
通过查看 image-dialog.js 文件中的源码,可以发现 submitHandler
函数(第 1 ~ 18 行)绑定在 type="submit"
对象(暂时这么称呼,此处描述不准确)的点击事件(第 20 行):
var submitHandler = function() {
...
uploadIframe.onload = function() {
...
if(log) {
console.log("uploadIframe.contentWindow", uploadIframe.contentWindow);
console.log("uploadIframe.contentDocument", uploadIframe.contentDocument);
}
var body = (uploadIframe.contentWindow ? uploadIframe.contentWindow : uploadIframe.contentDocument).document.body;
...
return false;
};
};
dialog.find("[type=\"submit\"]").bind("click", submitHandler).trigger("click");
猜想 dialog.find("[type=\"submit\"]")
是一个提交按钮,可能是添加图片对话框中的本地上传按钮,因为当我们点击本地上传之后,就触发安全错误,而该错误又是执行 (uploadIframe.contentWindow ? uploadIframe.contentWindow : uploadIframe.contentDocument).document.body;
导致的,见以上 代码的第 12 行。
我们验证一下,修改 image-dialog.js 文件,看看 dialog.find("[type=\"submit\"]")
是什么:
if (log) {
console.log('dialog.find("[type=\"submit\"]")', dialog.find("[type=\"submit\"]"));
}
dialog.find("[type=\"submit\"]").bind("click", submitHandler).trigger("click");
再次上传图片,可以看到 dialog.find("[type=\"submit\"]")
返回的对象确实和本地上传按钮有关系!
根据 dialog.find("[type=\"submit\"]")
提供的线索,继续查看 image-dialog.js 文件,发现 type="submit"
的本地上传按钮,即有可能是在这块代码定义:
var dialogContent = ( (settings.imageUpload) ? "<form action=\"" + action +"\" target=\"" + iframeName + "\" method=\"post\" enctype=\"multipart/form-data\" class=\"" + classPrefix + "form\">" : "<div class=\"" + classPrefix + "form\">" ) +
( (settings.imageUpload) ? "<iframe name=\"" + iframeName + "\" id=\"" + iframeName + "\" guid=\"" + guid + "\"></iframe>" : "" ) +
"<label>" + imageLang.url + "</label>" +
"<input type=\"text\" data-url />" + (function(){
return (settings.imageUpload) ? "<div class=\"" + classPrefix + "file-input\">" +
"<input type=\"file\" name=\"" + classPrefix + "image-file\" accept=\"image/*\" />" +
"<input type=\"submit\" value=\"" + imageLang.uploadButton + "\" />" +
"</div>" : "";
})() +
"<br/>" +
"<label>" + imageLang.alt + "</label>" +
"<input type=\"text\" value=\"" + selection + "\" data-alt />" +
"<br/>" +
"<label>" + imageLang.link + "</label>" +
"<input type=\"text\" value=\"http://\" data-link />" +
"<br/>" +
( (settings.imageUpload) ? "</form>" : "</div>");
请耐心看一下代码,此处有两个关键点:
<form>
元素的action
属性,见第 1 行。- 第 7 行,
<input>
元素的类型(type)是submit
。现在它的value
是未知的。
我们可以看看这两者是什么,修改 image-dialog.js 文件,将它们打印到控制台(第 1 ~ 4 行):
if (log) {
console.log("action: ", action);
console.log("imageLang.uploadButton: ", imageLang.uploadButton);
}
var dialogContent = ( (settings.imageUpload) ? "<form action=\"" + action +"\" ... +
... // 其它,省略
从控制台的输出,我们可以看到,action
对应服务器的上传接口,而 imageLang.uploadButton
,即上文提到的 <input>
元素的 value
值,刚好对应添加图片对话框的本地上传按钮:
我们通过选取页面中的元素,可以看到图片上传表单的 action
和本地上传提交按钮与上面提到的信息刚好吻合:
由此,可以推测 var dialogContent = ( (settings.imageUpload) ? "<form action=\"" + action + ... );
主要功能是创建图片上传表单。
由于图片上传表单的 action
与 image-dialog.js 文件不同源,导致 image-dialog.js 文件中的代码不能访问图片上传表单的元素。
那么,该如何解决安全错误呢?
嗯,不妨使用 AJAX 代替表单提交功能。
如果使用 AJAX 上传图片,就不需要设置图片上传表单的 action
,因此,我们可以注释 editormd
配置中的 imageUploadURL
属性:
var testEditor = editormd("test-editormd", {
...
imageUpload : true,
imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
// imageUploadURL : "http://localhost:8080/attachment/image/upload",
...
});
这样,添加图片表单 action
就变成这样子:
上图中的添加图片表单 action
中的 guid
是一个时间戳,其在 image-dialog.js 文件中定义:
if (editor.find("." + dialogName).length < 1)
{
var guid = (new Date).getTime();
var action = settings.imageUploadURL + (settings.imageUploadURL.indexOf("?") >= 0 ? "&" : "?") + "guid=" + guid;
...
}
或者,我们可以直接修改 image-dialog.js 文件中 action
变量,将其置空(第 8 行):
if (editor.find("." + dialogName).length < 1)
{
var guid = (new Date).getTime();
var action = settings.imageUploadURL + (settings.imageUploadURL.indexOf("?") >= 0 ? "&" : "?") + "guid=" + guid;
...
action = "";
var dialogContent = ( (settings.imageUpload) ? "<form action=\"" + action + "\" ...
}
清除了表单的 action
属性之后,我们需要禁用本地上传的提交功能。修改 image-dialog.js 文件,在 submitHandler
函数中的末尾添加 return false;
,并关闭加载效果:
var submitHandler = function() {
var uploadIframe = document.getElementById(iframeName);
uploadIframe.onload = function() {
...
};
loading(false); // 关闭加载效果
return false;
};
现在,通过本地上传按钮选择一张图片并打开,是不会将图片上传至服务器。
接下来,让我们看看 uploadIframe.onload
干了什么。这里有三个关键信息:
- 第 8 行的
json.success
; - 第 9 行的
json.url
; - 第 12 行的
json.message
。
var submitHandler = function() {
...
uploadIframe.onload = function() {
...
if(!settings.crossDomainUpload) {
if (json.success === 1) { // 上传成功
dialog.find("[data-url]").val(json.url);
}
else {
alert(json.message);
}
}
return false;
};
loading(false); // 关闭加载效果
return false;
};
这三个关键信息,刚好对应 image-upload.html 示例中提到的 JSON 数据格式(第 8 ~ 13 行):
var testEditor = editormd("test-editormd", {
...
imageUpload : true,
imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
// imageUploadURL : "http://localhost:8080/attachment/image/upload",
/*
上传的后台只需要返回一个 JSON 数据,结构如下:
{
success : 0 | 1, // 0 表示上传失败,1 表示上传成功
message : "提示的信息,上传成功或上传失败及错误信息等。",
url : "图片地址" // 上传成功时才返回
}
*/
});
回到 image-dialog.js 文件中的 submitHandler
函数,让我们看看 dialog.find("[data-url]")
是什么,将其打印到控制台(第 3 行):
var submitHandler = function() {
if(log) {
console.log('dialog.find("[data-url]")', dialog.find("[data-url]"));
}
...
loading(false); // 关闭加载效果
return false;
};
可以看到,dialog.find("[data-url]")
对应图片地址:
だから、dialog.find("[data-url]").val(json.url);
役割がある-画像が正常にアップロードする場合、サーバーが返すURLフィルの追加写真ダイアログ画像アドレス(9行目):
var submitHandler = function() {
...
uploadIframe.onload = function() {
...
if(!settings.crossDomainUpload) {
if (json.success === 1) { // 上传成功
dialog.find("[data-url]").val(json.url);
}
else {
alert(json.message);
}
}
return false;
};
loading(false); // 关闭加载效果
return false;
};
場合はこれまでのところ、上記の分析ほとんど憶測。次に、私たちは確認してみましょう。修正画像dialog.jsファイルsubmitHandler
機能、およびコメントuploadIframe.onload
関連するコード:
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;
};
AJAXのアップロードの写真を使用します:
var submitHandler = function() {
var form = dialog.find("[enctype=\"multipart/form-data\"]")[0];
var formData = new FormData(form);
$.ajax({
type: 'post',
// url: "http://localhost:8080/attachment/image/upload", // 你的服务器端的图片上传接口。如果你设置了 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;
};
我々が正常に画像をアップロードする場合さて、あなたは絵アドレスを見ることができます:
クリックして、[OK]のボタンを、絵も値下げエディタに対応することができます。