[ASP.Net Core] 네이버에디터2 연동하기 - 에디터 이미지 첨부하기
네이버에디터를 실무에서 자주 연동해서 쓰는데
필자두 PHP, JSP 등에 네이버에디터를 연결하여 잘 쓰고 있다.
http://naver.github.io/smarteditor2/user_guide/
https://github.com/naver/smarteditor2
이번에는 ASP.Net Core6 에서 MVC 형태의 코드상에서 네이버에디터를 연동해 보았다.
기존의 네이버에디터2 소스를 보면 기본적으로 PHP 에 맞추어 있어서 이외의 언어에서는 이미지 첨부시에 파일업로드 이부분을 백엔드 언어에 맞게 각각 개별 프로그램을 짜주어야 한다.
일단 에디터상에서 이미지 첨부시에 주요한 파일들을 개괄적으로 살펴보고 잠깐 수정하고,
필자가 따로 만든 이미지업로드 되는 소스부분을 연동하면 정상적으로 돌아간다.
필자의 네이버에디터 파일들을 static 파일 부분에 복사하여 준비하였다.
에디터의 업로드시에 업로드되는 폴더 위치
/wwwroot/files/Smarteditor2
에디터 파일들의 위치
/wwwroot/smarteditor2
중요한 에디터 관련 파일들
/wwwroot/smarteditor2/sample/photo_uploader/ 안에
1. attach_photo.js - 이미지 업로드관련 자바스크립트 동작 파일
2. photo_uploader.html - 이미지 첨부 아이콘 클릭시 팝업되는 html 디자인파일
3. callback.html - 콜백 단순 연동 파일 (소스 수정이 필요가 없다. 그대로 쓰면된다.)
그리고, 네이버에디터가 붙는 글쓰기 폼페이지
WriteNaverEditor.cshtml 파일
ServiceCotroller.cs EditorUploadFiles 메소드가 파일업로드 처리하는 부분
/Service/EditorUploadFiles (필자의 라우팅 형태)
[ WriteNaverEditor.cshtml ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
<form name="f" method="post" enctype="multipart/form-data">
<textarea name="content" id="content" rows="10" cols="100" style="width:100%; height:300px; display:none;"></textarea>
<div class="row">
<div class="update ml-auto mr-auto" style="text-align:center;">
<a href="#" class="btn btn-default" onclick="submitContents(this);">글쓰기</a>
<a href="#" class="btn btn-default" onclick="location.href='./Index'">목록</a>
</div>
</div>
</form>
<script type="text/javascript" src="/smarteditor2/js/HuskyEZCreator.js"></script>
<script>
var oEditors = [];
var sLang = "ko_KR"; // 언어 (ko_KR/ en_US/ ja_JP/ zh_CN/ zh_TW), default = ko_KR
// 추가 글꼴 목록
//var aAdditionalFontSet = [["MS UI Gothic", "MS UI Gothic"], ["Comic Sans MS", "Comic Sans MS"],["TEST","TEST"]];
nhn.husky.EZCreator.createInIFrame({
oAppRef: oEditors,
elPlaceHolder: "content",
sSkinURI: "/smarteditor2/SmartEditor2Skin.html",
htParams: {
bUseToolbar: true, // 툴바 사용 여부 (true:사용/ false:사용하지 않음)
bUseVerticalResizer: true, // 입력창 크기 조절바 사용 여부 (true:사용/ false:사용하지 않음)
bUseModeChanger: true, // 모드 탭(Editor | HTML | TEXT) 사용 여부 (true:사용/ false:사용하지 않음)
//bSkipXssFilter : true, // client-side xss filter 무시 여부 (true:사용하지 않음 / 그외:사용)
//aAdditionalFontList : aAdditionalFontSet, // 추가 글꼴 목록
fOnBeforeUnload: function () {
//alert("완료!");
},
I18N_LOCALE: sLang
}, //boolean
fOnAppLoad: function () {
//예제 코드
//oEditors.getById["content"].exec("PASTE_HTML", ["로딩이 완료된 후에 본문에 삽입되는 text입니다."]);
},
fCreator: "createSEditor2"
});
function pasteHTML(filepath) {
// var sHTML = "<span style='color:#FF0000;'>이미지도 같은 방식으로 삽입합니다.<\/span>";
var sHTML = '<span style="color:#FF0000;"><img src="' + filepath + '"></span>';
oEditors.getById["content"].exec("PASTE_HTML", [sHTML]);
}
function showHTML() {
var sHTML = oEditors.getById["content"].getIR();
alert(sHTML);
}
function submitContents(elClickedObj) {
oEditors.getById["content"].exec("UPDATE_CONTENTS_FIELD", []); // 에디터의 내용이 textarea에 적용됩니다.
// 에디터의 내용에 대한 값 검증은 이곳에서 document.getElementById("ir1").value를 이용해서 처리하면 됩니다.
try {
var form2 = document.f;
if (document.getElementById("content").value == "<p> </p>") {
alert("내용을 입력해 주세요.");
oEditors.getById["content"].exec("FOCUS", []);
return;
}
form2.action = "/Notice/Write";
//elClickedObj.form.submit();
form2.submit();
} catch (e) { alert(e); }
}
function setDefaultFont() {
var sDefaultFont = '궁서';
var nFontSize = 24;
oEditors.getById["content"].setDefaultFont(sDefaultFont, nFontSize);
}
function writeReset() {
document.f.reset();
oEditors.getById["content"].exec("SET_IR", [""]);
}
function list() {
location.href = "/Notice/Index";
}
</script>
|
cs |
[ photo_uploader.html ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Script-Type" content="text/javascript">
<meta http-equiv="Content-Style-Type" content="text/css">
<title>사진 첨부하기 :: SmartEditor2</title>
<style type="text/css">
/* NHN Web Standard 1Team JJS 120106 */
/* Common */
body,p,h1,h2,h3,h4,h5,h6,ul,ol,li,dl,dt,dd,table,th,td,form,fieldset,legend,input,textarea,button,select{margin:0;padding:0}
body,input,textarea,select,button,table{font-family:'돋움',Dotum,Helvetica,sans-serif;font-size:12px}
img,fieldset{border:0}
ul,ol{list-style:none}
em,address{font-style:normal}
a{text-decoration:none}
a:hover,a:active,a:focus{text-decoration:underline}
/* Contents */
.blind{visibility:hidden;position:absolute;line-height:0}
#pop_wrap{width:383px}
#pop_header{height:26px;padding:14px 0 0 20px;border-bottom:1px solid #ededeb;background:#f4f4f3}
.pop_container{padding:11px 20px 0}
#pop_footer{margin:21px 20px 0;padding:10px 0 16px;border-top:1px solid #e5e5e5;text-align:center}
h1{color:#333;font-size:14px;letter-spacing:-1px}
.btn_area{word-spacing:2px}
.pop_container .drag_area{overflow:hidden;overflow-y:auto;position:relative;width:341px;height:129px;margin-top:4px;border:1px solid #eceff2}
.pop_container .drag_area .bg{display:block;position:absolute;top:0;left:0;width:341px;height:129px;background:#fdfdfd url(./img/bg_drag_image.png) 0 0 no-repeat}
.pop_container .nobg{background:none}
.pop_container .bar{color:#e0e0e0}
.pop_container .lst_type li{overflow:hidden;position:relative;padding:7px 0 6px 8px;border-bottom:1px solid #f4f4f4;vertical-align:top}
.pop_container :root .lst_type li{padding:6px 0 5px 8px}
.pop_container .lst_type li span{float:left;color:#222}
.pop_container .lst_type li em{float:right;margin-top:1px;padding-right:22px;color:#a1a1a1;font-size:11px}
.pop_container .lst_type li a{position:absolute;top:6px;right:5px}
.pop_container .dsc{margin-top:6px;color:#666;line-height:18px}
.pop_container .dsc_v1{margin-top:12px}
.pop_container .dsc em{color:#13b72a}
.pop_container2{padding:46px 60px 20px}
.pop_container2 .dsc{margin-top:6px;color:#666;line-height:18px}
.pop_container2 .dsc strong{color:#13b72a}
.upload{margin:0 4px 0 0;_margin:0;padding:6px 0 4px 6px;border:solid 1px #d5d5d5;color:#a1a1a1;font-size:12px;border-right-color:#efefef;border-bottom-color:#efefef;length:300px;}
:root .upload{padding:6px 0 2px 6px;}
</style>
</head>
<body>
<div id="pop_wrap">
<!-- header -->
<div id="pop_header">
<h1>사진 첨부하기</h1>
</div>
<!-- //header -->
<!-- container -->
<!-- [D] HTML5인 경우 pop_container 클래스와 하위 HTML 적용
그밖의 경우 pop_container2 클래스와 하위 HTML 적용 -->
<div id="pop_container2" class="pop_container2">
<!-- content -->
<form id="editor_upimage" name="editor_upimage" action="/Service/EditorUploadFiles" method="post" enctype="multipart/form-data" onSubmit="return false;">
<div id="pop_content2">
<input type="file" class="upload" id="uploadInputBox" name="Filedata">
<p class="dsc" id="info"><strong>10MB</strong>이하의 이미지 파일만 등록할 수 있습니다.<br>(JPG, GIF, PNG, BMP)</p>
</div>
</form>
<!-- //content -->
</div>
<div id="pop_container" class="pop_container" style="display:none;">
<!-- content -->
<div id="pop_content">
<p class="dsc"><em id="imageCountTxt">0장</em>/10장 <span class="bar">|</span> <em id="totalSizeTxt">0MB</em>/50MB</p>
<!-- [D] 첨부 이미지 여부에 따른 Class 변화
첨부 이미지가 있는 경우 : em에 "bg" 클래스 적용 //첨부 이미지가 없는 경우 : em에 "nobg" 클래스 적용 -->
<div class="drag_area" id="drag_area">
<ul class="lst_type" >
</ul>
<em class="blind">마우스로 드래그해서 이미지를 추가해주세요.</em><span id="guide_text" class="bg"></span>
</div>
<div style="display:none;" id="divImageList"></div>
<p class="dsc dsc_v1"><em>한 장당 10MB, 1회에 50MB까지, 10개</em>의 이미지 파일을<br>등록할 수 있습니다. (JPG, GIF, PNG, BMP)</p>
</div>
<!-- //content -->
</div>
<!-- //container -->
<!-- footer -->
<div id="pop_footer">
<div class="btn_area">
<a href="#"><img src="./img/btn_confirm.png" width="49" height="28" alt="확인" id="btn_confirm"></a>
<a href="#"><img src="./img/btn_cancel.png" width="48" height="28" alt="취소" id="btn_cancel"></a>
</div>
</div>
<!-- //footer -->
</div>
<script type="text/javascript" src="jindo.min.js" charset="utf-8"></script>
<script type="text/javascript" src="jindo.fileuploader.js" charset="utf-8"></script>
<script type="text/javascript" src="attach_photo.js" charset="utf-8"></script>
</body>
</html>
|
cs |
59라인과 61라인에서 경로와 file name 값을 적절히 수정한다.
[ attach_photo.js ] - 소스중에서 일부분만 수정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
//File API 지원 여부로 결정
function checkDragAndDropAPI() {
/*
try{
if( !oNavigator.ie ){
if(!!oNavigator.safari && oNavigator.version <= 5){
bSupportDragAndDropAPI = false;
}else{
bSupportDragAndDropAPI = true;
}
} else {
bSupportDragAndDropAPI = false;
}
}catch(e){
bSupportDragAndDropAPI = false;
}
*/
bSupportDragAndDropAPI = false;
}
function callFileUploader (){
oFileUploader = new jindo.FileUploader(jindo.$("uploadInputBox"),{
sUrl: '/Service/EditorUploadFiles', //업로드 처리경로
sCallback: '/smarteditor2/sample/photo_uploader/callback.html', //업로드 이후에 iframe이 redirect될 콜백페이지의 주소
//sCallback: location.href.replace(/\/[^\/]*$/, '') + '/callback.html', //업로드 이후에 iframe이 redirect될 콜백페이지의 주소
sFiletype : "*.jpg;*.png;*.bmp;*.gif", //허용할 파일의 형식. ex) "*", "*.*", "*.jpg", 구분자(;)
sMsgNotAllowedExt : 'JPG, GIF, PNG, BMP 확장자만 가능합니다', //허용할 파일의 형식이 아닌경우에 띄워주는 경고창의 문구
bAutoUpload : false, //파일이 선택됨과 동시에 자동으로 업로드를 수행할지 여부 (upload 메소드 수행)
bAutoReset : true // 업로드한 직후에 파일폼을 리셋 시킬지 여부 (reset 메소드 수행)
}).attach({
select : function(oCustomEvent) {
//파일 선택이 완료되었을 때 발생
// oCustomEvent (이벤트 객체) = {
// sValue (String) 선택된 File Input의 값
// bAllowed (Boolean) 선택된 파일의 형식이 허용되는 형식인지 여부
// sMsgNotAllowedExt (String) 허용되지 않는 파일 형식인 경우 띄워줄 경고메세지
// }
// 선택된 파일의 형식이 허용되는 경우만 처리
if(oCustomEvent.bAllowed === true){
goStartMode();
}else{
goReadyMode();
oFileUploader.reset();
}
// bAllowed 값이 false인 경우 경고문구와 함께 alert 수행
// oCustomEvent.stop(); 수행시 bAllowed 가 false이더라도 alert이 수행되지 않음
},
success : function(oCustomEvent) {
// alert("success");
// 업로드가 성공적으로 완료되었을 때 발생
// oCustomEvent(이벤트 객체) = {
// htResult (Object) 서버에서 전달해주는 결과 객체 (서버 설정에 따라 유동적으로 선택가능)
// }
var aResult = [];
aResult[0] = oCustomEvent.htResult;
setPhotoToEditor(aResult);
//버튼 비활성화
goReadyMode();
oFileUploader.reset();
window.close();
},
error : function(oCustomEvent) {
//업로드가 실패했을 때 발생
//oCustomEvent(이벤트 객체) = {
// htResult : { (Object) 서버에서 전달해주는 결과 객체. 에러발생시 errstr 프로퍼티를 반드시 포함하도록 서버 응답을 설정하여야한다.
// errstr : (String) 에러메시지
// }
//}
//var wel = jindo.$Element("info");
//wel.html(oCustomEvent.htResult.errstr);
alert(oCustomEvent.htResult.errstr);
}
});
}
|
cs |
bSupportDragAndDropAPI 를 false 를 하면 이미지 팝업 html 창을 단일 파일업로드 html 창으로 선택할 수 있다.
23라인과 24라인에서 sUrl 과 sCallback 파일 경로 위치를 알맞게 수정한다.
/Service/EditorUploadFiles (필자의 라우팅 경로)
[ ServiceContoller.cs ] - 실제 파일업로드 처리를 하는 c# 파일
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
using ChilgokReserveSystem.Api.Models.Board.Notice;
using Microsoft.AspNetCore.Mvc;
using Dul.Web;
using System.Net.Http.Headers;
using System.IO;
using System.IO.Pipes;
using System.Xml.Linq;
namespace ChilgokReserveSystem.Controllers.Service
{
public class ServiceController : Controller
{
private IWebHostEnvironment environment;
public ServiceController(IWebHostEnvironment environment)
{
this.environment = environment;
}
[HttpPost]
[RequestFormLimits(ValueCountLimit = Int32.MaxValue)]
public async Task<IActionResult> EditorUploadFiles(ICollection<IFormFile> Filedata, string callback_func)
{
string url = "/smarteditor2/sample/photo_uploader/callback.html?callback_func=" + callback_func;
//string url = "callback.html?callback_func=";
//string url = "";
if (ModelState.IsValid)
{
var uploadFolder = String.Empty;
string fileName1 = String.Empty;
int fileSize1 = 0;
uploadFolder = Path.Combine(environment.WebRootPath, "files/Smarteditor2");
foreach (var file in Filedata)
{
if (file.Length > 0)
{
fileSize1 = Convert.ToInt32(file.Length);
// 파일명 중복 처리
fileName1 = Dul.FileUtility.GetFileNameWithNumbering(
uploadFolder, Path.GetFileName(
ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"')));
// 파일 업로드
using (var fileStream = new FileStream(
Path.Combine(uploadFolder, fileName1), FileMode.Create))
{
await file.CopyToAsync(fileStream);
}
url += "&bNewLine=true";
//url += "&sFileName=" + System.Web.HttpUtility.UrlDecode(fileName1);
url += "&sFileName=" + fileName1;
url += "&sFileURL=/files/Smarteditor2/" + fileName1;
}
}
} else
{
url += "&errstr=error";
}
return LocalRedirect(url);
//return Content(url);
}
}
}
|
cs |