https://greed-yb.tistory.com/266
목록, 상세 보기는 이전글 참고하길 바람
수정페이지 - html
<style>
.input-file-button{
padding: 7px 25px;
background-color: rgba(255, 174, 0, 0.35);
border-radius: 4px;
color: white;
cursor: pointer;
line-height: 1.5;
}
</style>
<!-- Begin Page Content -->
<div class="container-fluid">
<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">사내 게시판</h1>
</div>
<!-- Content Row -->
<div>
<!-- DataTales Example -->
<div class="card shadow mb-4">
<div class="card-header py-3">
<h5 class="m-0 font-weight-bold text-dark">글 작성</h5>
</div>
<div class="card-body">
<form class="user" id="form" name="form" autocomplete="off" enctype="multipart/form-data">
<div class="form-group">
<input type="text" class="form-control form-control-user" id="title" name="title" placeholder="제목">
<input type="hidden" id="no" th:value="${no}">
</div>
<div class="form-group">
<textarea class="form-control" id="summernote" name="content" type="text"></textarea>
</div>
<div class="fileDiv" >
<input type="text" name="fileName" readonly>
<label class="input-file-button" for="inputFile">업로드</label>
<input type="file" id="inputFile" style="display:none" onchange="selectFile(this);"/>
<button type="button" class="btn btn-danger" onclick="deleteFile()"> 파일 삭제</button>
<!-- <button type="button" class="btn btn-primary"> 파일 추가</button>-->
</div>
<div class="form-group">
<div style="text-align: center">
<button type="button" class="btn btn-secondary" onclick="updateBtn()">수정</button>
<button type="button" class="btn btn-info" onclick="backBtn()">뒤로</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<script>
// 스크립트 경로는 thymeleaf layout 를 사용중이라 본인에 맞게 바꿔야 한다
<script th:src="@{/bootstrap/vendor/datatables/jquery.dataTables.min.js}"></script>
<script th:src="@{/bootstrap/vendor/datatables/dataTables.bootstrap4.min.js}"></script>
<script th:src="@{/bootstrap/js/companyBoardUpdate.js}"></script>
<!-- summernote -->
<script th:src="@{/bootstrap/js/summernote/summernote-lite.js}"></script>
<script th:src="@{/bootstrap/js/summernote/lang/summernote-ko-KR.js}"></script>
<link rel="stylesheet" th:href="@{/bootstrap/js/summernote/summernote-lite.css}">
</script>
파일 추가 버튼으로 첨부파일을 계속 추가하도록 하려고 했는데
당장 필요가 없고 하다 보니 하기 싫어져서... 패스했다
수정 .js
// 유저 정보
let user_info = [];
// 파일첨부
let ajaxFilePath = "";
// 게시글 정보
let board_info = [];
function backBtn(){
window.history.back();
}
function updateBtn(){
if(!confirm("수정 하시겠습니까?")){
return false;
}
let ob = new Object();
ob.no = $("#no").val();
ob.title = $("#title").val();
ob.content = $("#summernote").val();
ob.fileName = $("input[name=fileName]").val();
ob.filePath = ajaxFilePath;
$.ajax({
url : "/companyBoard/writeUpdate",
type : "POST",
dataType : "text",
contentType : "application/json; charset=utf-8",
data : JSON.stringify(ob),
success : function (data){
if(data == 'success'){
location.href="/companyBoardRead/"+ob.no;
}else{
alert("저장 오류 발생");
}
}
});
}
// 이미지 업로드
function imageUpload(file){
let formData = new FormData();
formData.append("file", file);
$.ajax({
url : "/upload/imageUpload",
type : "POST",
data : formData,
// contentType, processData 설정 안하면 TypeError: Illegal invocation 오류가 발생한다
contentType: false,
processData: false,
encType : "multipart/form-data",
success : function (data) {
// 글에 이미지 넣을때 크기 설정
// $("#summernote").summernote("insertImage", "/uploadPath/image/"+data, function (data){
// data.css("width" , "100%");
// });
// 글에 이미지 넣기
$("#summernote").summernote("insertImage", "/uploadPath/image/"+data);
},
error(e){
console.log("error : "+ e);
}
});
}
// 이미지 삭제
function deleteImageFile(fileName) {
let formData = new FormData();
formData.append("file", fileName);
$.ajax({
url : "/upload/imageDelete",
type : "POST",
data : formData,
// contentType, processData 설정 안하면 TypeError: Illegal invocation 오류가 발생한다
contentType: false,
processData: false,
encType : "multipart/form-data"
});
}
// 유저 정보
function userInfo(){
$.ajax({
url : "/userInfo",
type : "GET",
success : function (data) {
if(data != null){
user_info = data;
}
},
error(e){
console.log("error : "+ e);
}
});
}
// 게시글 정보
function companyBoardInfo(no){
$.ajax({
url : "/companyBoard/updateInfo",
type : "GET",
data : {"no" : no},
success : function (data) {
if(data != null){
board_info = data;
$("#title").val(board_info.title);
$("#writer").val(board_info.writer);
ajaxFilePath= board_info.filePath;
$("input[name=fileName]").val(board_info.fileName);
// summernote 에 데이터 insert
$("#summernote").summernote("code", board_info.content);
}
},
error(e){
console.log("error : "+ e);
}
});
}
// 파일 첨부
function selectFile(e){
let file = e.files[0];
let fileName = e.closest('.fileDiv').firstElementChild;
fileName.value = file.name;
let formData = new FormData();
formData.append("file", file);
$.ajax({
url : "/upload/imageUpload",
type : "POST",
data : formData,
// contentType, processData 설정 안하면 TypeError: Illegal invocation 오류가 발생한다
contentType: false,
processData: false,
encType : "multipart/form-data",
success : function (data) {
// UUID로 변경한 파일 명을 가져온다
ajaxFilePath = data;
},
error(e){
console.log("error : "+ e);
}
});
}
function deleteFile(){
$("input[name=fileName]").val("");
// 이미지라고 적었지만 파일은 다 삭제된다
// 첨부파일 기능은 구현하지 않으려다가 넣어서 이름이 맞지 않는다
deleteImageFile(ajaxFilePath);
}
$(document).ready(function (){
// 유저정보 가져오기
userInfo();
// textarea summernote 적용하기
$("#summernote").summernote({
codeviewFilter: false, // 코드 보기 필터 비활성화
codeviewIframeFilter: false, // 코드 보기 iframe 필터 비활성화
height: 400, // 에디터 높이
minHeight: null, // 최소 높이
maxHeight: null, // 최대 높이
lang: "ko-KR", // 에디터 한글 설정
focus : true, // 에디터 포커스 설정
toolbar: [
['fontname', ['fontname']], // 글꼴 설정
['fontsize', ['fontsize']], // 글자 크기
['style', ['bold', 'italic', 'underline','strikethrough', 'clear']], // 글자 스타일 설정
['color', ['forecolor','color']], // 글자색
['table', ['table']], // 표 생성
['insert', ['picture', 'link','video']], // 이미지, 링크 , 동영상
['para', ['ul', 'ol', 'paragraph']], // 문단 스타일 설정
['height', ['height']], // 줄간격
['view', ['codeview','fullscreen', 'help']] // 코드보기, 전체화면, 도움말
],
fontNames: ['Arial', 'Arial Black', 'Comic Sans MS', 'Courier New','맑은 고딕','궁서','굴림체','굴림','돋음체','바탕체'], // 추가한 글꼴
fontSizes: ['8','9','10','11','12','14','16','18','20','22','24','28','30','36','50','72'], // 추가한 폰트사이즈
callbacks : {
// 파일 업로드
onImageUpload : function (files) {
for(let i=0; i < files.length; i++){
// 이미지가 여러개일 경우
imageUpload(files[i]);
}
},
// 파일 삭제
onMediaDelete: function ($target){
if(confirm("이미지를 삭제하시겠습니까?")){
let fileName = $target.attr('src').split('/').pop();
deleteImageFile(fileName);
}
}
}
});
// 수정 글 정보 가져오기
let no = $("#no").val();
companyBoardInfo(no);
});
파일 업로드 및 첨부파일 다운로드
package com.example.practice.util.imageUpload;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.io.FileUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
@RestController
@Tag(name="Swagger upload", description = "upload")
@RequestMapping("/upload/*")
public class ImageUpload {
// 파일 업로드 경로
final Path FILE_ROOT = Paths.get("./").toAbsolutePath().normalize();
private String uploadPath = FILE_ROOT.toString() + "/upload/image/";
@Operation(summary = "이미지 업로드 ", description = "이미지를 서버에 업로드한다.")
@PostMapping("/imageUpload")
public ResponseEntity<?> imageUpload(@RequestParam MultipartFile file) throws Exception{
try {
// 업로드 파일의 이름
String originalFileName = file.getOriginalFilename();
// 업로드 파일의 확장자
String fileExtension = originalFileName.substring(originalFileName.lastIndexOf("."));
// 업로드 된 파일이 중복될 수 있어서 파일 이름 재설정
String reFileName = UUID.randomUUID().toString() + fileExtension;
// 업로드 경로에 파일명을 변경하여 저장
file.transferTo(new File(uploadPath, reFileName));
// 파일이름을 재전송
return ResponseEntity.ok(reFileName);
}catch (Exception e) {
e.printStackTrace();
return ResponseEntity.badRequest().body("업로드 에러");
}
}
@Operation(summary = "파일 삭제 ", description = "서버에 저장된 파일을 삭제한다.")
@PostMapping("/imageDelete")
public void imageDelete(@RequestParam String file) throws Exception{
try {
Path path = Paths.get(uploadPath, file);
Files.delete(path);
}catch (Exception e) {
e.printStackTrace();
}
}
@Operation(summary = "첨부파일 다운로드 ", description = "첨부된 파일을 다운로드한다.")
@GetMapping("/downloadFile/{filePath}")
public void readFileResource(@PathVariable String filePath, HttpServletResponse response) throws Exception{
File downloadFile=new File(uploadPath + filePath);
byte fileByte[]= FileUtils.readFileToByteArray(downloadFile);
response.setContentType("application/octet-stream");
response.setContentLength(fileByte.length);
response.setHeader("Content-Disposition","attachment;fileName=\""+ URLEncoder.encode(filePath,"UTF-8")+"\";");
response.setHeader("Content-Transfer-Encoding","binary");
response.getOutputStream().write(fileByte);
response.getOutputStream().flush();
response.getOutputStream().close();
}
}
Controller , service, mapper, vo 는 이전글에 모두 입력을 해버려서
현재 페이지에서는 생략했다
Summernote로 간단하게 구현 테스트만 진행하려다가
이것저것 추가하면서 방향을 잡았더니
제대로 된 계획 및 설계가 없이 코딩을 하게 되었다
쿼리 부분만 봐도 defalut로 주면 될 값들도 중간중간 추가하다 보니
쿼리에서 직접 데이터를 넣어주고 있는 상황까지 나왔으니...
그냥 이렇게 구현하면 된다 정도만 보면 될 것이다
데이터를 바인딩하는 부분에서 model도 사용하고 ajax로 여러 번 controller를 갔다 온 것은
그저 여러 방법을 사용하고 싶었을 뿐 큰 이유는 없다
'개발 > Spring' 카테고리의 다른 글
[SpringBoot] Chart.js 를 이용하여 실시간 라인 차트 구현하기 (0) | 2024.07.17 |
---|---|
[SpringBoot] Chart.js 를 이용하여 실시간 (반도넛)차트 구현하기 (0) | 2024.07.16 |
[SpringBoot] Summernote Editor(게시판 글 상세보기) 적용하기 - 2 (0) | 2024.07.15 |
[SpringBoot] Summernote Editor(이미지,동영상 삽입) 적용하기 -1 (0) | 2024.07.11 |
[SpringBoot] 로그인 시 아이디 기억하기(쿠키 적용) (1) | 2024.07.10 |
댓글