설명
textarea에 image 및 tag 적용이 되지 않음
적용이 불가능하지는 않은듯하나 예외 사항이 많아 적용이 쉽지 않음
div contentEditable="true" 형태를 사용하여 image 및 text를 함께 작성
div contentEditable 형태는 form 형태로 전달하기 힘들기 때문에 hidden text를 사용하여 db에 내용을 전달
Page를 불러 올때 hidden 형태의 타입의 값을 div contentEditable 부분에 가져와서 보여줌
html
DB 에서 데이터를 조회하여 templates에 result.content_detail 값을 전달하여 처리하는 형태
content_detail은 hidden형태로 값을 전달하는 중간 역할
content_detail_edit는 실제로 데이터(이미지 및 텍스트)를 화면에 출력하는 역할
content_detail_image는 file 타입으로 이미지의 정보를 처리
content_img_width는 이미지의 width정보를 전달하기 위한 용도
<div class="form-group col-md-10">
<div class="input-group">
<div class="input-group-prepend iw-100" data-toggle="tooltip" data-container="body" title="content 내용">
<button type="button" class="btn btn-warning iw-100" onclick="expandText()">Content *</button>
</div>
<input type="hidden" name="content_detail" id="content_detail" value = "{{ result.content_detail }}"/>
<div contentEditable="true" style="min-height: 100px;height: 100%;max-width: 100%;white-space: pre-wrap; word-break: break-word;"class="form-control" id="content_detail_edit" required> </div>
</div>
</div>
<div class="form-group col-md-2">
<div class="input-group">
<div class="custom-file">
<input type="file" class="custom-file-input" id="content_detail_image" name="content_detail_image">
<label class="custom-file-label" for="content_detail_image">Choose file</label>
</div>
<div class="input-group-append">
<span class="input-group-text" onclick="goContent_Detail_Image();">Upload</span>
</div>
</div>
Image Width <input style="width: 75px;" type="text" name="content_img_width" class="form-control" id="content_img_width" value="800" />
</div>
javascript
function 내부에서 bsCustomFileInput.init();를 호출하여 파일 객체 초기화 진행
function 내부에서 hidden type의 데이터를 contentDeitable 타입의 값에 복사하여 page에 출력시 사용
goContent_Detail_Image함수에서
file객체의 정보를 ajax 형태로 python으로 전달
python으로 서버에 이미지 생성 성공시 저장된 위치 및 파일명을 기반으로, templates의 content_detail 및 content_detail_edit에 이미치 tag를 추가하여 출력
이미지는 content_img_width에 설정된 값으로 생성(너무 크게 생성되는것을 방지하기 위해 1024를 max로 처리)
goContent_Edit함수에서
content_detail_edit에 수정한 내용을 DB에 저장할 때는 content_detail로 값을 변경하여 전달
<!-- Tempusdominus Bootstrap 4 -->
<link rel="stylesheet" href="/static/plugins/tempusdominus-bootstrap-4/css/tempusdominus-bootstrap-4.min.css">
<!-- jQuery -->
<script src="/static/plugins/jquery/jquery.min.js"></script>
<!-- Bootstrap 4 -->
<script src="/static/plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- bs-custom-file-input -->
<script src="/static/plugins/bs-custom-file-input/bs-custom-file-input.min.js"></script>
$(function () {
// File 객체 초기화
bsCustomFileInput.init();
// hidden type의 데이터를 contentEditable 타입의 값에 복사
Src_Val = $('#content_detail').val()
document.getElementById('content_detail_edit').innerHTML = Src_Val;
document.getElementById("content_detail_edit").focus();
});
function goContent_Detail_Image(){
let content_id = $('#content_id').val()
let file = $('#content_detail_image')[0].files[0]
let form_data = new FormData()
form_data.append("id_give", content_id)
form_data.append("file_give", file)
// Image를 서버에 저장
$.ajax({
url:'/content/detail_image',
type:'POST',
data: form_data,
cache: false,
contentType: false,
processData: false,
success: function(data){
showAlert('content detail image insert successful!!!', data.filename, 2, 2000);
// 이미지 최대 width는 1024로 제한
img_width = $('#content_img_width').val()
if ( img_width > 1024 ) img_width = 1024;
Src_Val = $('#content_detail').val()
Change_Val = Src_Val + '<br> <img width = "' + img_width + '" src="/static/img/content/' + data.filename + '">'
document.getElementById('content_detail').innerHTML = Change_Val;
document.getElementById('content_detail_edit').innerHTML = Change_Val;
},
error: function(xhr, ajaxOptions, thrownError) {
var fail_title = "content detail img upload Failed!!";
var fail_msg = '';
var err_msg = JSON.parse(xhr.responseText);
if (thrownError == 'UNKNOWN'){
fail_msg = err_msg.message + ' (' + xhr.status + ')'
} else {
fail_msg = err_msg.message + ' (' + thrownError + ',' + xhr.status + ')';
}
showAlert(fail_title,fail_msg,3,1500);
}
});
return false;
}
function goContent_Edit(){
// ** Content 수정 ** //
// content detail text 전환
Src_Val = document.getElementById('content_detail_edit').innerHTML;
$('#content_detail').val(Src_Val);
var param = $('#content_form').serialize();
// content 수정 요청
$.ajax({
url:'/content/edit',
type:'POST',
data: param,
success: function(data){
showAlert('content Edit Successful!!!', data.message, 2, 2000);
},
error: function(xhr, ajaxOptions, thrownError) {
var fail_title = "content edit Failed!!";
var fail_msg = '';
var err_msg = JSON.parse(xhr.responseText);
if (thrownError == 'UNKNOWN'){
fail_msg = err_msg.message + ' (' + xhr.status + ')'
} else {
fail_msg = err_msg.message + ' (' + thrownError + ',' + xhr.status + ')';
}
showAlert(fail_title,fail_msg,3,1500);
}
});
return false;
};
python
이미지 파일명은 content_id + prefix_date + filename을 조합하여, 이미지 content별 구분과 등록 순서를 확인 가능하도록 네이밍 처리함
이미지를 서버에 저장한 이후 화면에 이미지를 출력하기 위해 파일명을 리턴
from werkzeug import secure_filename
@bts_blueprint.route('content/detail_image', methods=['POST'])
def content_detail_image():
print("## content_detail_image ##")
try:
file = request.files['file_give']
content_id = request.form['id_give']
#extention = file.filename.split('.')[-1]
UploadPath = 'static/img/content/'
now_date = datetime.datetime.now()
prefix_date = now_date.strftime('_%Y%m%d_%H%M%S_')
ImgFullname = UploadPath + content_id + prefix_date + file.filename
#file.save(UploadPath, secure_filename(file.filename))
file.save(ImgFullname)
return jsonify(filename=content_id + prefix_date + file.filename), 200
except (RuntimeError, TypeError, NameError, SQLAlchemyError) as e:
print("#### content detail img upload Failed !! " + str(e))
return jsonify(message=str(e)), 1002
참고
https://velog.io/@wrs0707/%EB%82%98%ED%99%80%EB%A1%9C%EC%9D%BC%EA%B8%B0%EC%9E%A5-%EB%A7%8C%EB%93%A4%EA%B8%B0
https://blog.naver.com/ynskoh/221064660966