Search

κ²Œμ‹œνŒ - 파일 μ—…λ‘œλ“œ

κ²Œμ‹œνŒ - 파일 μ—…λ‘œλ“œ

μŠ€νƒ€ μ’€ λˆŒλŸ¬μ£Όμ„Έμš”

Spring Boot (Back-End)

Spring Boot 둜 파일 μ—…λ‘œλ“œ, 쑰회, μ‚­μ œ κΈ°λŠ₯을 κ΅¬ν˜„ν•©λ‹ˆλ‹€.

κ΅¬ν˜„ κΈ°λŠ₯

β€’
파일 μ—…λ‘œλ“œ
β—¦
λ°μ΄ν„°λ² μ΄μŠ€μ— 등둝
β—¦
파일 μ‹œμŠ€ν…œμ— 등둝
β€’
︎ 파일 쑰회
β—¦
파일 λͺ©λ‘ 쑰회
β—¦
λ‹€μš΄λ‘œλ“œ
β—¦
썸넀일 이미지 보기
β€’
파일 μ‚­μ œ
β—¦
κ²Œμ‹œκΈ€μ— μ’…μ†λœ μ—¬λŸ¬ 파일 μ‚­μ œ
β—¦
선택 파일 μ‚­μ œ
β—¦
κ°œλ³„ 파일 μ‚­μ œ

React (Front-End)

React 둜 파일 μ—…λ‘œλ“œ, 쑰회, μ‚­μ œ UI 및 API λ₯Ό κ΅¬ν˜„ν•©λ‹ˆλ‹€.
β€’
파일 μ—…λ‘œλ“œ
β—¦
파일 등둝 UI μΆ”κ°€
β—¦
파일 μ—…λ‘œλ“œ μš”μ²­ API
β€’
︎ 파일 쑰회
β—¦
파일 λͺ©λ‘ 쑰회 UI 및 API μΆ”κ°€
β—¦
λ‹€μš΄λ‘œλ“œ UI 및 API μΆ”κ°€
β—¦
썸넀일 이미지 UI μΆ”κ°€
β€’
파일 μ‚­μ œ
β—¦
κ²Œμ‹œκΈ€μ— μ’…μ†λœ μ—¬λŸ¬ 파일 μ‚­μ œ (κ²Œμ‹œκΈ€ μ‚­μ œ μ‹œ, λ°±μ—”λ“œμ—μ„œ μ²˜λ¦¬ν•¨)
β—¦
μ‚­μ œν•  파일 μ²΄ν¬λ°•μŠ€ UI μΆ”κ°€ 및 μˆ˜μ • μš”μ²­ μ‹œ, μ‚­μ œν•  파일 번호 리슀트 데이터 μΆ”κ°€
β—¦
κ°œλ³„ 파일 μ‚­μ œ UI 및 API μΆ”κ°€

Spring Boot (Back-End)

Spring Boot 둜 파일 μ—…λ‘œλ“œ, 쑰회, μ‚­μ œ κΈ°λŠ₯을 κ΅¬ν˜„ν•©λ‹ˆλ‹€.

파일 μ—…λ‘œλ“œ

λ°μ΄ν„°λ² μ΄μŠ€μ— 등둝

1.
데이터 베이슀 - ν…Œμ΄λΈ” 생성
2.
SQL 쿼리 Mapper μž‘μ„±
3.
DTO
4.
Mapper
5.
Service
6.
Controller

데이터 베이슀 - ν…Œμ΄λΈ” 생성

CREATE TABLE `file` ( `no` int NOT NULL AUTO_INCREMENT, -- 파일 번호 (μžλ™μ¦κ°€) `parent_table` varchar(45) NOT NULL, -- λΆ€λͺ¨ ν…Œμ΄λΈ”λͺ… (예: 'board') `parent_no` int NOT NULL, -- λΆ€λͺ¨ ν…Œμ΄λΈ”μ—μ„œμ˜ 번호 `file_name` text NOT NULL, -- μ €μž₯된 파일λͺ… `origin_name` text, -- 원본 파일λͺ… `file_path` text NOT NULL, -- 파일 경둜 `file_size` int NOT NULL DEFAULT '0', -- 파일 크기 (κΈ°λ³Έκ°’ 0) `reg_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, -- λ“±λ‘μΌμ‹œ `upd_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, -- μˆ˜μ •μΌμ‹œ `file_code` int NOT NULL DEFAULT '0', -- 파일 μ½”λ“œ (κΈ°λ³Έκ°’ 0) PRIMARY KEY (`no`) -- μ£Όν‚€ μ„€μ • ) COMMENT='파일';
SQL
볡사

SQL 쿼리 Mapper μž‘μ„±

β€’
FileMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace="맀퍼 μΈν„°νŽ˜μ΄μŠ€ 경둜" --> <mapper namespace="com.joeun.server.mapper.FileMapper"> <!-- 파일 λͺ©λ‘ --> <select id="list" resultType="Files"> SELECT * FROM file ORDER BY reg_date DESC </select> <!-- 파일 λͺ©λ‘ - λΆ€λͺ¨ ν…Œμ΄λΈ” κΈ°μ€€ --> <!-- * 파일이 μ’…μ†λ˜λŠ” ν…Œμ΄λΈ”μ„ κΈ°μ€€μœΌλ‘œ 파일 λͺ©λ‘μ„ 쑰회 --> <!-- * κ²Œμ‹œκΈ€ 번호 10 πŸ“„ 파일 번호 1 πŸ“„ 파일 번호 2 πŸ“„ 파일 번호 3 --> <select id="listByParent" resultType="Files"> SELECT * FROM file WHERE parent_table = #{parentTable} AND parent_no = #{parentNo} ORDER BY reg_date DESC </select> <!-- 파일 쑰회 --> <select id="select" resultType="Files"> SELECT * FROM file WHERE no = #{no} </select> <!-- 파일 등둝 --> <insert id="insert"> INSERT INTO file( parent_table, parent_no, file_name, origin_name, file_path, file_size, file_code ) VALUES ( #{parentTable}, #{parentNo}, #{fileName}, #{originName}, #{filePath}, #{fileSize}, #{fileCode} ) </insert> <!-- 파일 μˆ˜μ • --> <update id="update"> UPDATE file SET parent_table = #{parentTable} ,parent_no = #{parentNo} ,file_name = #{fileName} ,origin_name = #{originName} ,file_path = #{filePath} ,file_size = #{fileSize} ,file_code = #{fileCode} WHERE no = #{no} </update> <!-- 파일 μ‚­μ œ --> <delete id="delete"> DELETE FROM file WHERE no = #{no} </delete> <!-- 파일 λͺ©λ‘ μ‚­μ œ - λΆ€λͺ¨ ν…Œμ΄λΈ” κΈ°μ€€ 파일 λͺ©λ‘ μ‚­μ œ --> <delete id="deleteByParent"> DELETE FROM file WHERE parent_table = #{parentTable} AND parent_no = #{parentNo} </delete> <!-- κ²Œμ‹œκΈ€ 번호 μ΅œλŒ“κ°’ --> <select id="maxPk" resultType="int"> SELECT MAX(no) FROM file </select> </mapper>
SQL
볡사

DTO

β€’
Files.java
package com.joeun.server.dto; import java.util.Date; import lombok.Data; @Data public class Files { private int no; private String parentTable; private int parentNo; private String fileName; private String originName; private String filePath; private long fileSize; private Date regDate; private Date updDate; private int fileCode; }
Java
볡사

Mapper

β€’
FileMapper.java
package com.joeun.server.mapper; import java.util.List; import org.apache.ibatis.annotations.Mapper; import com.joeun.server.dto.Files; @Mapper public interface FileMapper { // 파일 λͺ©λ‘ public List<Files> list() throws Exception; // 파일 쑰회 public Files select(int no) throws Exception; // 파일 등둝 public int insert(Files file) throws Exception; // 파일 μˆ˜μ • public int update(Files file) throws Exception; // 파일 μ‚­μ œ public int delete(int no) throws Exception; // 파일 번호(κΈ°λ³Έν‚€) μ΅œλŒ“κ°’ public int maxPk() throws Exception; // 파일 λͺ©λ‘ - λΆ€λͺ¨ κΈ°μ€€ public List<Files> listByParent(Files file) throws Exception; // 파일 μ‚­μ œ - λΆ€λͺ¨ κΈ°μ€€ public int deleteByParent(Files file) throws Exception; }
Java
볡사

Service

β€’
FileService.java
β€’
FileServiceImpl.java
β€’
BoardServiceImpl.java
β€’
FileService.java
package com.joeun.server.service; import java.util.List; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import jakarta.servlet.http.HttpServletResponse; public interface FileService { // 파일 λͺ©λ‘ public List<Files> list() throws Exception; // 파일 쑰회 public Files select(int no) throws Exception; // 파일 등둝 public int insert(Files file) throws Exception; // 파일 μˆ˜μ • public int update(Files file) throws Exception; // 파일 μ—…λ‘œλ“œ public int uploadFiles(Files file, List<MultipartFile> fileList) throws Exception; // 파일 μ—…λ‘œλ“œ public int upload(Files file) throws Exception; }
Java
볡사
β€’
FileServiceImpl.java
package com.joeun.server.service; import java.io.File; import java.io.FileInputStream; import java.util.List; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.FileCopyUtils; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import com.joeun.server.mapper.FileMapper; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class FileServiceImpl implements FileService { @Autowired private FileMapper fileMapper; @Value("${upload.path}") // application.properties 에 μ„€μ •ν•œ μ—…λ‘œλ“œ 경둜 속성λͺ… private String uploadPath; // μ—…λ‘œλ“œ 경둜 @Override public List<Files> list() throws Exception { List<Files> fileList = fileMapper.list(); return fileList; } @Override public Files select(int no) throws Exception { Files file = fileMapper.select(no); return file; } @Override public int insert(Files file) throws Exception { int result = fileMapper.insert(file); return result; } @Override public int update(Files file) throws Exception { int result = fileMapper.update(file); return result; } @Override public int uploadFiles(Files fileInfo, List<MultipartFile> fileList) throws Exception { int result = 0; for (MultipartFile file : fileList) { result += uploadFile( fileInfo, file ); } log.info(result + "개 νŒŒμΌμ„ μ—…λ‘œλ“œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override @Transactional public int upload(Files file) throws Exception { int result = uploadFile(file, file.getFile()); if( result > 0 ) result = fileMapper.maxPk(); return result; } public int uploadFile(Files fileInfo, MultipartFile file) throws Exception { int result = 0; if( file.isEmpty() ) return result; // 파일 정보 : μ›λ³ΈνŒŒμΌλͺ…, 파일 μš©λŸ‰, 파일 데이터 String originName = file.getOriginalFilename(); long fileSize = file.getSize(); // μ—…λ‘œλ“œ 경둜 // 파일λͺ… 쀑볡 방지 방법(μ •μ±…) // - λ‚ μ§œ_파일λͺ….ν™•μž₯자 // - UID_파일λͺ….ν™•μž₯자 // UID_강아지.png String fileName = UUID.randomUUID().toString() + "_" + originName; // c:/upload/UID_강아지.png String filePath = uploadPath + "/" + fileName; // - DB 에 파일 정보 등둝 Files uploadedFile = new Files(); uploadedFile.setParentTable(fileInfo.getParentTable()); uploadedFile.setParentNo(fileInfo.getParentNo()); uploadedFile.setFileName(fileName); uploadedFile.setFilePath(filePath); uploadedFile.setOriginName(originName); uploadedFile.setFileSize(fileSize); uploadedFile.setFileCode(0); // DB 에 데이터 등둝 result = fileMapper.insert(uploadedFile); return result; } }
Java
볡사
β€’
BoardServiceImpl.java
package com.joeun.server.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Board; import com.joeun.server.dto.Files; import com.joeun.server.mapper.BoardMapper; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class BoardServiceImpl implements BoardService { @Autowired private BoardMapper boardMapper; @Autowired private FileService fileService; @Override public List<Board> list() throws Exception { List<Board> boardList = boardMapper.list(); return boardList; } @Override public Board select(int no) throws Exception { Board board = boardMapper.select(no); return board; } @Override @Transactional public int insert(Board board) throws Exception { int result = boardMapper.insert(board); // μƒˆλ‘œ μƒμ„±λœ λ°μ΄ν„°μ˜ pk κ°€μ Έμ˜΄ int parentNo = boardMapper.maxPk(); board.setNo(parentNo); // βœ…(New) 파일 μ—…λ‘œλ“œ result += uploadFiles(board); return result; } @Override public int update(Board board) throws Exception { int result = boardMapper.update(board); // 파일 μ—…λ‘œλ“œ result += uploadFiles(board); return result; } @Override public int delete(int no) throws Exception { int result = boardMapper.delete(no); return result; } @Override public int updateViews(int count, int no) throws Exception { int result = boardMapper.updateViews(count, no); return result; } // βœ…(New) 파일 μ—…λ‘œλ“œ public int uploadFiles(Board board) throws Exception { String parentTable = "board"; int parentNo = board.getNo(); int result = 0; List<MultipartFile> fileList = board.getFiles(); if( fileList != null && !fileList.isEmpty() ) { Files fileInfo = new Files(); fileInfo.setParentTable(parentTable); fileInfo.setParentNo(parentNo); result = fileService.uploadFiles(fileInfo, fileList); } return result; } }
Java
볡사

Controller

β€’
FileController.java
package com.joeun.server.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import lombok.extern.slf4j.Slf4j; @Slf4j @Controller @RequestMapping("/file") public class FileController { }
Java
볡사

파일 μ‹œμŠ€ν…œμ— 등둝

1.
DTO
2.
Service

DTO

β€’
Files.java
package com.joeun.server.dto; import java.util.Date; import org.springframework.web.multipart.MultipartFile; import lombok.Data; @Data public class Files { private int no; private String parentTable; private int parentNo; private String fileName; private String originName; private String filePath; private long fileSize; private Date regDate; private Date updDate; private int fileCode; MultipartFile file; // βœ… μΆ”κ°€ }
Java
볡사

Service

β€’
FileServiceImpl.java
μΆ”κ°€ν•  μ½”λ“œ
// βœ…(New) νŒŒμΌμ—…λ‘œλ“œ // - μ„œλ²„ μΈ‘, 파일 μ‹œμŠ€ν…œμ— 파일 볡사 File uploadFile = new File(uploadPath, fileName); FileCopyUtils.copy(fileData, uploadFile); // 파일 μ—…λ‘œλ“œ
Java
볡사
package com.joeun.server.service; import java.io.File; import java.io.FileInputStream; import java.util.List; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.FileCopyUtils; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import com.joeun.server.mapper.FileMapper; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class FileServiceImpl implements FileService { @Autowired private FileMapper fileMapper; @Value("${upload.path}") // application.properties 에 μ„€μ •ν•œ μ—…λ‘œλ“œ 경둜 속성λͺ… private String uploadPath; // μ—…λ‘œλ“œ 경둜 @Override public List<Files> list() throws Exception { List<Files> fileList = fileMapper.list(); return fileList; } @Override public Files select(int no) throws Exception { Files file = fileMapper.select(no); return file; } @Override public int insert(Files file) throws Exception { int result = fileMapper.insert(file); return result; } @Override public int update(Files file) throws Exception { int result = fileMapper.update(file); return result; @Override public List<Files> listByParent(Files file) throws Exception { List<Files> fileList = fileMapper.listByParent(file); return fileList; } @Override public int deleteByParent(Files fileInfo) throws Exception { int result = 0; List<Files> fileList = fileMapper.listByParent(fileInfo); for (Files file : fileList) { File deleteFile = new File(file.getFilePath()); if( !deleteFile.exists() ) continue; if( !deleteFile.delete() ) continue; int deleted = fileMapper.delete(file.getNo()); if( deleted > 0 ) result++; } log.info(result + "개의 νŒŒμΌμ„ μ‚­μ œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int uploadFiles(Files fileInfo, List<MultipartFile> fileList) throws Exception { int result = 0; for (MultipartFile file : fileList) { result += uploadFile( fileInfo, file); } log.info(result + "개 νŒŒμΌμ„ μ—…λ‘œλ“œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int deleteByNoList(List<Integer> deleteFileNoList) throws Exception { int result = 0; if( deleteFileNoList != null && !deleteFileNoList.isEmpty() ) for (Integer deleteFileNo : deleteFileNoList) { if( deleteFileNo == null ) continue; fileMapper.delete(deleteFileNo); log.info(deleteFileNo + "번 파일 μ‚­μ œ"); result++; } return result; } @Override @Transactional public int upload(Files file) throws Exception { int result = uploadFile(file, file.getFile()); if( result > 0 ) result = fileMapper.maxPk(); return result; } public int uploadFile(Files fileInfo, MultipartFile file) throws Exception { int result = 0; if( file.isEmpty() ) return result; // 파일 정보 : μ›λ³ΈνŒŒμΌλͺ…, 파일 μš©λŸ‰, 파일 데이터 String originName = file.getOriginalFilename(); long fileSize = file.getSize(); byte[] fileData = file.getBytes(); // μ—…λ‘œλ“œ 경둜 // 파일λͺ… 쀑볡 방지 방법(μ •μ±…) // - λ‚ μ§œ_파일λͺ….ν™•μž₯자 // - UID_파일λͺ….ν™•μž₯자 // UID_강아지.png String fileName = UUID.randomUUID().toString() + "_" + originName; // c:/upload/UID_강아지.png String filePath = uploadPath + "/" + fileName; // βœ…(New) νŒŒμΌμ—…λ‘œλ“œ // - μ„œλ²„ μΈ‘, 파일 μ‹œμŠ€ν…œμ— 파일 볡사 File uploadFile = new File(uploadPath, fileName); FileCopyUtils.copy(fileData, uploadFile); // 파일 μ—…λ‘œλ“œ // - DB 에 파일 정보 등둝 Files uploadedFile = new Files(); uploadedFile.setParentTable(fileInfo.getParentTable()); uploadedFile.setParentNo(fileInfo.getParentNo()); uploadedFile.setFileName(fileName); uploadedFile.setFilePath(filePath); uploadedFile.setOriginName(originName); uploadedFile.setFileSize(fileSize); uploadedFile.setFileCode(0); result = fileMapper.insert(uploadedFile); return result; } }
Java
볡사

︎ 파일 쑰회

파일 λͺ©λ‘ 쑰회

β€’
FileService.java
β€’
FileServiceImpl.java
β€’
BoardServiceImpl.java
β€’
BoardController.java

FileService.java

package com.joeun.server.service; import java.util.List; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import jakarta.servlet.http.HttpServletResponse; public interface FileService { // 파일 λͺ©λ‘ public List<Files> list() throws Exception; // 파일 쑰회 public Files select(int no) throws Exception; // 파일 등둝 public int insert(Files file) throws Exception; // 파일 μˆ˜μ • public int update(Files file) throws Exception; // 파일 μ—…λ‘œλ“œ public int uploadFiles(Files file, List<MultipartFile> fileList) throws Exception; // 파일 μ—…λ‘œλ“œ public int upload(Files file) throws Exception; // βœ…(New) 파일 λͺ©λ‘ - λΆ€λͺ¨ κΈ°μ€€ public List<Files> listByParent(Files file) throws Exception; }
Java
볡사

FileServiceImpl.java

package com.joeun.server.service; import java.io.File; import java.io.FileInputStream; import java.util.List; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.FileCopyUtils; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import com.joeun.server.mapper.FileMapper; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class FileServiceImpl implements FileService { @Autowired private FileMapper fileMapper; @Value("${upload.path}") // application.properties 에 μ„€μ •ν•œ μ—…λ‘œλ“œ 경둜 속성λͺ… private String uploadPath; // μ—…λ‘œλ“œ 경둜 @Override public List<Files> list() throws Exception { List<Files> fileList = fileMapper.list(); return fileList; } @Override public Files select(int no) throws Exception { Files file = fileMapper.select(no); return file; } @Override public int insert(Files file) throws Exception { int result = fileMapper.insert(file); return result; } @Override public int update(Files file) throws Exception { int result = fileMapper.update(file); return result; } @Override public List<Files> listByParent(Files file) throws Exception { List<Files> fileList = fileMapper.listByParent(file); return fileList; } @Override public int deleteByParent(Files fileInfo) throws Exception { int result = 0; List<Files> fileList = fileMapper.listByParent(fileInfo); for (Files file : fileList) { File deleteFile = new File(file.getFilePath()); if( !deleteFile.exists() ) continue; if( !deleteFile.delete() ) continue; int deleted = fileMapper.delete(file.getNo()); if( deleted > 0 ) result++; } log.info(result + "개의 νŒŒμΌμ„ μ‚­μ œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int uploadFiles(Files fileInfo, List<MultipartFile> fileList) throws Exception { int result = 0; for (MultipartFile file : fileList) { result += uploadFile( fileInfo, file); } log.info(result + "개 νŒŒμΌμ„ μ—…λ‘œλ“œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int deleteByNoList(List<Integer> deleteFileNoList) throws Exception { int result = 0; if( deleteFileNoList != null && !deleteFileNoList.isEmpty() ) for (Integer deleteFileNo : deleteFileNoList) { if( deleteFileNo == null ) continue; fileMapper.delete(deleteFileNo); log.info(deleteFileNo + "번 파일 μ‚­μ œ"); result++; } return result; } @Override @Transactional public int upload(Files file) throws Exception { int result = uploadFile(file, file.getFile()); if( result > 0 ) result = fileMapper.maxPk(); return result; } public int uploadFile(Files fileInfo, MultipartFile file) throws Exception { int result = 0; if( file.isEmpty() ) return result; // 파일 정보 : μ›λ³ΈνŒŒμΌλͺ…, 파일 μš©λŸ‰, 파일 데이터 String originName = file.getOriginalFilename(); long fileSize = file.getSize(); byte[] fileData = file.getBytes(); // μ—…λ‘œλ“œ 경둜 // 파일λͺ… 쀑볡 방지 방법(μ •μ±…) // - λ‚ μ§œ_파일λͺ….ν™•μž₯자 // - UID_파일λͺ….ν™•μž₯자 // UID_강아지.png String fileName = UUID.randomUUID().toString() + "_" + originName; // c:/upload/UID_강아지.png String filePath = uploadPath + "/" + fileName; // - μ„œλ²„ μΈ‘, 파일 μ‹œμŠ€ν…œμ— 파일 볡사 File uploadFile = new File(uploadPath, fileName); FileCopyUtils.copy(fileData, uploadFile); // 파일 μ—…λ‘œλ“œ // - DB 에 파일 정보 등둝 Files uploadedFile = new Files(); uploadedFile.setParentTable(fileInfo.getParentTable()); uploadedFile.setParentNo(fileInfo.getParentNo()); uploadedFile.setFileName(fileName); uploadedFile.setFilePath(filePath); uploadedFile.setOriginName(originName); uploadedFile.setFileSize(fileSize); uploadedFile.setFileCode(0); result = fileMapper.insert(uploadedFile); return result; } // βœ… (New) - 파일 λͺ©λ‘ 쑰회 @Override public List<Files> listByParent(Files file) throws Exception { List<Files> fileList = fileMapper.listByParent(file); return fileList; } }
Java
볡사

BoardController.java

μΆ”κ°€ν•  μ½”λ“œ
@GetMapping("/{no}") public ResponseEntity<?> getOne(@PathVariable Integer no, Files files) { log.info("[GET] - /boards/" + no + " - κ²Œμ‹œκΈ€ 쑰회"); try { Board board = boardService.select(no); files.setParentTable("board"); files.setParentNo(no); List<Files> fileList = fileService.listByParent(files); // 파일 정보 Map<String, Object> response = new HashMap<>(); response.put("board", board); response.put("fileList", fileList); if( board == null ) { board = new Board(); board.setTitle("데이터 μ—†μŒ"); } return new ResponseEntity<>(response, HttpStatus.OK); } catch (Exception e) { log.error(null, e); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } }
Java
볡사
package com.joeun.server.controller; import java.util.HashMap; import java.util.List; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Board; import com.joeun.server.dto.Files; import com.joeun.server.service.BoardService; import com.joeun.server.service.FileService; import lombok.extern.slf4j.Slf4j; @Slf4j @RestController @RequestMapping("/boards") public class BoardController { @Autowired private BoardService boardService; @Autowired private FileService fileService; // πŸ‘©β€πŸ’» CRUD λ©”μ†Œλ“œ μžλ™ 생성 : sp-crud // πŸ‘©β€πŸ’» μžλ™ import : alt + shift + O // πŸ‘©β€πŸ’» ν•œ 쀄 μ‚­μ œ : ctrl + shift + K @GetMapping() public ResponseEntity<?> getAll() { log.info("[GET] - /boards - κ²Œμ‹œκΈ€ λͺ©λ‘"); try { List<Board> boardList = boardService.list(); if( boardList == null ) log.info("쑰회된 κ²Œμ‹œκΈ€ μ—†μŒ"); else log.info("κ²Œμ‹œκΈ€ 수 : " + boardList.size()); return new ResponseEntity<>(boardList, HttpStatus.OK); } catch (Exception e) { log.error(null, e); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } } @GetMapping("/{no}") public ResponseEntity<?> getOne(@PathVariable Integer no, Files files) { log.info("[GET] - /boards/" + no + " - κ²Œμ‹œκΈ€ 쑰회"); try { Board board = boardService.select(no); files.setParentTable("board"); files.setParentNo(no); List<Files> fileList = fileService.listByParent(files); // 파일 정보 Map<String, Object> response = new HashMap<>(); response.put("board", board); response.put("fileList", fileList); if( board == null ) { board = new Board(); board.setTitle("데이터 μ—†μŒ"); } return new ResponseEntity<>(response, HttpStatus.OK); } catch (Exception e) { log.error(null, e); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } } @PostMapping() // public ResponseEntity<?> create(@RequestBody Board board) { // Content-Type : application/json public ResponseEntity<?> create(Board board) { // Content-Type : multipart/form-data log.info("[POST] - /boards - κ²Œμ‹œκΈ€ 등둝"); log.info("board : " + board.toString()); List<MultipartFile> files = board.getFiles(); if( files != null ) for (MultipartFile file : files) { log.info("file : " + file.getOriginalFilename()); } try { int result = boardService.insert(board); if( result > 0 ) return new ResponseEntity<>("κ²Œμ‹œκΈ€ 등둝 μ™„λ£Œ", HttpStatus.CREATED); // 201 else return new ResponseEntity<>("κ²Œμ‹œκΈ€ 등둝 μ‹€νŒ¨", HttpStatus.OK); } catch (Exception e) { log.error(null, e); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } } @PutMapping() // public ResponseEntity<?> update(@RequestBody Board board) { // Content-Type : application/json public ResponseEntity<?> update(Board board) { // Content-Type : multipart/form-data log.info("[PUT] - /boards - κ²Œμ‹œκΈ€ μˆ˜μ •"); try { int result = boardService.update(board); log.info("μˆ˜μ • : " + board); if( result > 0 ) return new ResponseEntity<>("κ²Œμ‹œκΈ€ μˆ˜μ • μ™„λ£Œ", HttpStatus.OK); else return new ResponseEntity<>("κ²Œμ‹œκΈ€ μˆ˜μ • μ‹€νŒ¨", HttpStatus.OK); } catch (Exception e) { log.error(null, e); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } } @DeleteMapping("/{no}") public ResponseEntity<?> destroy(@PathVariable Integer no) { log.info("[DELETE] - /boards/" + no + " - κ²Œμ‹œκΈ€ μ‚­μ œ"); try { int result = boardService.delete(no); if( result > 0 ) return new ResponseEntity<>("κ²Œμ‹œκΈ€ μ‚­μ œ μ™„λ£Œ", HttpStatus.OK); else return new ResponseEntity<>("κ²Œμ‹œκΈ€ μ‚­μ œ μ‹€νŒ¨", HttpStatus.OK); } catch (Exception e) { log.error(null, e); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } } }
Java
볡사

λ‹€μš΄λ‘œλ“œ

β€’
FileService.java
β€’
FileServiceImpl.java
β€’
FileController.java

FileService.java

package com.joeun.server.service; import java.util.List; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import jakarta.servlet.http.HttpServletResponse; public interface FileService { // 파일 λͺ©λ‘ public List<Files> list() throws Exception; // 파일 쑰회 public Files select(int no) throws Exception; // 파일 등둝 public int insert(Files file) throws Exception; // 파일 μˆ˜μ • public int update(Files file) throws Exception; // 파일 μ—…λ‘œλ“œ public int uploadFiles(Files file, List<MultipartFile> fileList) throws Exception; // 파일 μ—…λ‘œλ“œ public int upload(Files file) throws Exception; // 파일 λͺ©λ‘ - λΆ€λͺ¨ κΈ°μ€€ public List<Files> listByParent(Files file) throws Exception; // βœ…(New) - 파일 λ‹€μš΄λ‘œλ“œ public int download(int no, HttpServletResponse response) throws Exception; }
Java
볡사

FileServiceImpl.java

package com.joeun.server.service; import java.io.File; import java.io.FileInputStream; import java.util.List; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.FileCopyUtils; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import com.joeun.server.mapper.FileMapper; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class FileServiceImpl implements FileService { @Autowired private FileMapper fileMapper; @Value("${upload.path}") // application.properties 에 μ„€μ •ν•œ μ—…λ‘œλ“œ 경둜 속성λͺ… private String uploadPath; // μ—…λ‘œλ“œ 경둜 @Override public List<Files> list() throws Exception { List<Files> fileList = fileMapper.list(); return fileList; } @Override public Files select(int no) throws Exception { Files file = fileMapper.select(no); return file; } @Override public int insert(Files file) throws Exception { int result = fileMapper.insert(file); return result; } @Override public int update(Files file) throws Exception { int result = fileMapper.update(file); return result; } @Override public List<Files> listByParent(Files file) throws Exception { List<Files> fileList = fileMapper.listByParent(file); return fileList; } @Override public int deleteByParent(Files fileInfo) throws Exception { int result = 0; List<Files> fileList = fileMapper.listByParent(fileInfo); for (Files file : fileList) { File deleteFile = new File(file.getFilePath()); if( !deleteFile.exists() ) continue; if( !deleteFile.delete() ) continue; int deleted = fileMapper.delete(file.getNo()); if( deleted > 0 ) result++; } log.info(result + "개의 νŒŒμΌμ„ μ‚­μ œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int uploadFiles(Files fileInfo, List<MultipartFile> fileList) throws Exception { int result = 0; for (MultipartFile file : fileList) { result += uploadFile( fileInfo, file); } log.info(result + "개 νŒŒμΌμ„ μ—…λ‘œλ“œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int deleteByNoList(List<Integer> deleteFileNoList) throws Exception { int result = 0; if( deleteFileNoList != null && !deleteFileNoList.isEmpty() ) for (Integer deleteFileNo : deleteFileNoList) { if( deleteFileNo == null ) continue; fileMapper.delete(deleteFileNo); log.info(deleteFileNo + "번 파일 μ‚­μ œ"); result++; } return result; } @Override @Transactional public int upload(Files file) throws Exception { int result = uploadFile(file, file.getFile()); if( result > 0 ) result = fileMapper.maxPk(); return result; } public int uploadFile(Files fileInfo, MultipartFile file) throws Exception { int result = 0; if( file.isEmpty() ) return result; // 파일 정보 : μ›λ³ΈνŒŒμΌλͺ…, 파일 μš©λŸ‰, 파일 데이터 String originName = file.getOriginalFilename(); long fileSize = file.getSize(); byte[] fileData = file.getBytes(); // μ—…λ‘œλ“œ 경둜 // 파일λͺ… 쀑볡 방지 방법(μ •μ±…) // - λ‚ μ§œ_파일λͺ….ν™•μž₯자 // - UID_파일λͺ….ν™•μž₯자 // UID_강아지.png String fileName = UUID.randomUUID().toString() + "_" + originName; // c:/upload/UID_강아지.png String filePath = uploadPath + "/" + fileName; // - μ„œλ²„ μΈ‘, 파일 μ‹œμŠ€ν…œμ— 파일 볡사 File uploadFile = new File(uploadPath, fileName); FileCopyUtils.copy(fileData, uploadFile); // 파일 μ—…λ‘œλ“œ // - DB 에 파일 정보 등둝 Files uploadedFile = new Files(); uploadedFile.setParentTable(fileInfo.getParentTable()); uploadedFile.setParentNo(fileInfo.getParentNo()); uploadedFile.setFileName(fileName); uploadedFile.setFilePath(filePath); uploadedFile.setOriginName(originName); uploadedFile.setFileSize(fileSize); uploadedFile.setFileCode(0); result = fileMapper.insert(uploadedFile); return result; } // 파일 λͺ©λ‘ 쑰회 @Override public List<Files> listByParent(Files file) throws Exception { List<Files> fileList = fileMapper.listByParent(file); return fileList; } @Override public int download(int fileNo, HttpServletResponse response) throws Exception { // result // 0 : 파일 λ‹€μš΄λ‘œλ“œ 처리 μ‹€νŒ¨ // 1 : 파일 λ‹€μš΄λ‘œλ“œ 성곡 Files file = fileMapper.select(fileNo); if( file == null ) { // BAD_REQUEST : 400, ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ΄ 잘λͺ»λ˜μ—ˆμŒμ„ μ•Œλ €μ£ΌλŠ” μƒνƒœμ½”λ“œ // response.setStatus(response.SC_BAD_REQUEST); return 0; } String filePath = file.getFilePath(); // 파일 경둜 String fileName = file.getFileName(); // 파일 이름 // λ‹€μš΄λ‘œλ“œ 응닡을 μœ„ν•œ 헀더 μ„ΈνŒ… // - ContentType : application/octet-stream // - Content-Disposition : attachment, filename="파일λͺ….ν™•μž₯자" response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // 파일 λ‹€μš΄λ‘œλ“œ // - 파일 μž…λ ₯ File downloadFile = new File(filePath); FileInputStream fis = new FileInputStream(downloadFile); // - 파일 좜λ ₯ ServletOutputStream sos = response.getOutputStream(); // λ‹€μš΄λ‘œλ“œ FileCopyUtils.copy(fis, sos); fis.close(); sos.close(); return 1; } }
Java
볡사

FileController.java

package com.joeun.server.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import lombok.extern.slf4j.Slf4j; @Slf4j @Controller @RequestMapping("/file") public class FileController { @Autowired private FileService fileService; /** * 파일 λ‹€μš΄λ‘œλ“œ * @param fileNo * @param response * @throws Exception */ @GetMapping("/{no}") public void fileDownload(@PathVariable("no") int no ,HttpServletResponse response) throws Exception { // 파일 쑰회 Files file = fileService.select(no); // 파일이 μ‘΄μž¬ν•˜μ§€ μ•ŠμœΌλ©΄, if( file == null ) { // 응닡 μƒνƒœμ½”λ“œ : 400, ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ΄ 잘λͺ»λ˜μ—ˆμŒμ„ λ‚˜νƒ€λ‚΄λŠ” μƒνƒœμ½”λ“œ response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } String fileName = file.getFileName(); // 파일 λͺ… String filePath = file.getFilePath(); // 파일 경둜 // 파일 λ‹€μš΄λ‘œλ“œλ₯Ό μœ„ν•œ 헀더 μ„ΈνŒ… // - ContentType : application/octet-straem // - Content-Disposition : attachment; fileanme="파일λͺ….ν™•μž₯자" response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // 파일 μž…λ ₯ File downloadFile = new File(filePath); FileInputStream fis = new FileInputStream(downloadFile); ServletOutputStream sos = response.getOutputStream(); // λ‹€μš΄λ‘œλ“œ FileCopyUtils.copy(fis, sos); } }
Java
볡사

썸넀일 이미지 보기

β€’
FileController.java

FileController.java

package com.joeun.server.controller; import java.io.File; import java.io.FileInputStream; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.util.FileCopyUtils; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import com.joeun.server.dto.Files; import com.joeun.server.service.FileService; import com.joeun.server.utils.MediaUtil; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @Slf4j @Controller @RequestMapping("/file") public class FileController { @Autowired private FileService fileService; @Autowired private ResourceLoader resourceLoader; /** * 파일 λ‹€μš΄λ‘œλ“œ * @param fileNo * @param response * @throws Exception */ @GetMapping("/{no}") public void fileDownload(@PathVariable("no") int no ,HttpServletResponse response) throws Exception { // 파일 쑰회 Files file = fileService.select(no); // 파일이 μ‘΄μž¬ν•˜μ§€ μ•ŠμœΌλ©΄, if( file == null ) { // 응닡 μƒνƒœμ½”λ“œ : 400, ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ΄ 잘λͺ»λ˜μ—ˆμŒμ„ λ‚˜νƒ€λ‚΄λŠ” μƒνƒœμ½”λ“œ response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } String fileName = file.getFileName(); // 파일 λͺ… String filePath = file.getFilePath(); // 파일 경둜 // 파일 λ‹€μš΄λ‘œλ“œλ₯Ό μœ„ν•œ 헀더 μ„ΈνŒ… // - ContentType : application/octet-straem // - Content-Disposition : attachment; fileanme="파일λͺ….ν™•μž₯자" response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // 파일 μž…λ ₯ File downloadFile = new File(filePath); FileInputStream fis = new FileInputStream(downloadFile); ServletOutputStream sos = response.getOutputStream(); // λ‹€μš΄λ‘œλ“œ FileCopyUtils.copy(fis, sos); } /** * 이미지 썸넀일 * @param no * @param response * @throws Exception */ @GetMapping("/img/{no}") public void showImg(@PathVariable Integer no, HttpServletResponse response) throws Exception { Files file = fileService.select(no); String filePath = (file != null) ? file.getFilePath() : null; Resource resource = resourceLoader.getResource("classpath:static/img/no-image.png"); File imgFile; if (filePath == null || !(imgFile = new File(filePath)).exists()) { // 파일이 μ‘΄μž¬ν•˜μ§€ μ•Šκ±°λ‚˜ 파일 κ²½λ‘œκ°€ null인 경우 imgFile = resource.getFile(); } String ext = filePath.substring(filePath.lastIndexOf(".") + 1); MediaType mType = MediaUtil.getMediaType(ext); if (mType == null) { // 이미지 νƒ€μž…μ΄ 아닐 경우 response.setContentType(MediaType.IMAGE_PNG_VALUE); // 기본적으둜 PNG둜 μ„€μ • imgFile = resource.getFile(); } else { // 이미지 νƒ€μž…μΌ 경우 response.setContentType(mType.toString()); } FileInputStream fis = new FileInputStream(imgFile); ServletOutputStream sos = response.getOutputStream(); FileCopyUtils.copy(fis, sos); } }
Java
볡사

파일 μ‚­μ œ

κ²Œμ‹œκΈ€μ— μ’…μ†λœ μ—¬λŸ¬ 파일 μ‚­μ œ

β€’
FileService.java
β€’
FileServiceImpl.java
β€’
BoardServiceImpl.java
β€’
FileService.java
package com.joeun.server.service; import java.util.List; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import jakarta.servlet.http.HttpServletResponse; public interface FileService { // 파일 λͺ©λ‘ public List<Files> list() throws Exception; // 파일 쑰회 public Files select(int no) throws Exception; // 파일 등둝 public int insert(Files file) throwsdeleteByParent Exception; // 파일 μˆ˜μ • public int update(Files file) throws Exception; // 파일 μ‚­μ œ public int delete(int no) throws Exception; // 파일 μ—…λ‘œλ“œ public int uploadFiles(Files file, List<MultipartFile> fileList) throws Exception; // 파일 μ—…λ‘œλ“œ public int upload(Files file) throws Exception; // 파일 λͺ©λ‘ - λΆ€λͺ¨ κΈ°μ€€ public List<Files> listByParent(Files file) throws Exception; // 파일 λ‹€μš΄λ‘œλ“œ public int download(int no, HttpServletResponse response) throws Exception; // βœ…(New) 파일 μ‚­μ œ - λΆ€λͺ¨ κΈ°μ€€ public int deleteByParent(Files fileInfo) throws Exception; }
Java
볡사
β€’
FileServiceImpl.java
μΆ”κ°€ν•  μ½”λ“œ
@Override public int deleteByParent(Files fileInfo) throws Exception { int result = 0; List<Files> fileList = fileMapper.listByParent(fileInfo); for (Files file : fileList) { File deleteFile = new File(file.getFilePath()); if( !deleteFile.exists() ) continue; if( !deleteFile.delete() ) continue; int deleted = fileMapper.delete(file.getNo()); if( deleted > 0 ) result++; } log.info(result + "개의 νŒŒμΌμ„ μ‚­μ œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; }
Java
볡사
package com.joeun.server.service; import java.io.File; import java.io.FileInputStream; import java.util.List; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.FileCopyUtils; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import com.joeun.server.mapper.FileMapper; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class FileServiceImpl implements FileService { @Autowired private FileMapper fileMapper; @Value("${upload.path}") // application.properties 에 μ„€μ •ν•œ μ—…λ‘œλ“œ 경둜 속성λͺ… private String uploadPath; // μ—…λ‘œλ“œ 경둜 @Override public List<Files> list() throws Exception { List<Files> fileList = fileMapper.list(); return fileList; } @Override public Files select(int no) throws Exception { Files file = fileMapper.select(no); return file; } @Override public int insert(Files file) throws Exception { int result = fileMapper.insert(file); return result; } @Override public int update(Files file) throws Exception { int result = fileMapper.update(file); return result; } @Override public List<Files> listByParent(Files file) throws Exception { List<Files> fileList = fileMapper.listByParent(file); return fileList; } @Override public int deleteByParent(Files fileInfo) throws Exception { int result = 0; List<Files> fileList = fileMapper.listByParent(fileInfo); for (Files file : fileList) { File deleteFile = new File(file.getFilePath()); if( !deleteFile.exists() ) continue; if( !deleteFile.delete() ) continue; int deleted = fileMapper.delete(file.getNo()); if( deleted > 0 ) result++; } log.info(result + "개의 νŒŒμΌμ„ μ‚­μ œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int uploadFiles(Files fileInfo, List<MultipartFile> fileList) throws Exception { int result = 0; for (MultipartFile file : fileList) { result += uploadFile( fileInfo, file); } log.info(result + "개 νŒŒμΌμ„ μ—…λ‘œλ“œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int deleteByNoList(List<Integer> deleteFileNoList) throws Exception { int result = 0; if( deleteFileNoList != null && !deleteFileNoList.isEmpty() ) for (Integer deleteFileNo : deleteFileNoList) { if( deleteFileNo == null ) continue; fileMapper.delete(deleteFileNo); log.info(deleteFileNo + "번 파일 μ‚­μ œ"); result++; } return result; } @Override @Transactional public int upload(Files file) throws Exception { int result = uploadFile(file, file.getFile()); if( result > 0 ) result = fileMapper.maxPk(); return result; } public int uploadFile(Files fileInfo, MultipartFile file) throws Exception { int result = 0; if( file.isEmpty() ) return result; // 파일 정보 : μ›λ³ΈνŒŒμΌλͺ…, 파일 μš©λŸ‰, 파일 데이터 String originName = file.getOriginalFilename(); long fileSize = file.getSize(); byte[] fileData = file.getBytes(); // μ—…λ‘œλ“œ 경둜 // 파일λͺ… 쀑볡 방지 방법(μ •μ±…) // - λ‚ μ§œ_파일λͺ….ν™•μž₯자 // - UID_파일λͺ….ν™•μž₯자 // UID_강아지.png String fileName = UUID.randomUUID().toString() + "_" + originName; // c:/upload/UID_강아지.png String filePath = uploadPath + "/" + fileName; // - μ„œλ²„ μΈ‘, 파일 μ‹œμŠ€ν…œμ— 파일 볡사 File uploadFile = new File(uploadPath, fileName); FileCopyUtils.copy(fileData, uploadFile); // 파일 μ—…λ‘œλ“œ // - DB 에 파일 정보 등둝 Files uploadedFile = new Files(); uploadedFile.setParentTable(fileInfo.getParentTable()); uploadedFile.setParentNo(fileInfo.getParentNo()); uploadedFile.setFileName(fileName); uploadedFile.setFilePath(filePath); uploadedFile.setOriginName(originName); uploadedFile.setFileSize(fileSize); uploadedFile.setFileCode(0); result = fileMapper.insert(uploadedFile); return result; } // 파일 λͺ©λ‘ 쑰회 @Override public List<Files> listByParent(Files file) throws Exception { List<Files> fileList = fileMapper.listByParent(file); return fileList; } @Override public int download(int fileNo, HttpServletResponse response) throws Exception { // result // 0 : 파일 λ‹€μš΄λ‘œλ“œ 처리 μ‹€νŒ¨ // 1 : 파일 λ‹€μš΄λ‘œλ“œ 성곡 Files file = fileMapper.select(fileNo); if( file == null ) { // BAD_REQUEST : 400, ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ΄ 잘λͺ»λ˜μ—ˆμŒμ„ μ•Œλ €μ£ΌλŠ” μƒνƒœμ½”λ“œ // response.setStatus(response.SC_BAD_REQUEST); return 0; } String filePath = file.getFilePath(); // 파일 경둜 String fileName = file.getFileName(); // 파일 이름 // λ‹€μš΄λ‘œλ“œ 응닡을 μœ„ν•œ 헀더 μ„ΈνŒ… // - ContentType : application/octet-stream // - Content-Disposition : attachment, filename="파일λͺ….ν™•μž₯자" response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // 파일 λ‹€μš΄λ‘œλ“œ // - 파일 μž…λ ₯ File downloadFile = new File(filePath); FileInputStream fis = new FileInputStream(downloadFile); // - 파일 좜λ ₯ ServletOutputStream sos = response.getOutputStream(); // λ‹€μš΄λ‘œλ“œ FileCopyUtils.copy(fis, sos); fis.close(); sos.close(); return 1; } @Override public int deleteByParent(Files fileInfo) throws Exception { int result = 0; List<Files> fileList = fileMapper.listByParent(fileInfo); for (Files file : fileList) { File deleteFile = new File(file.getFilePath()); if( !deleteFile.exists() ) continue; if( !deleteFile.delete() ) continue; int deleted = fileMapper.delete(file.getNo()); if( deleted > 0 ) result++; } log.info(result + "개의 νŒŒμΌμ„ μ‚­μ œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } }
Java
볡사
β€’
BoardServiceImpl.java
μΆ”κ°€ν•  μ½”λ“œ
@Override @Transactional public int delete(int no) throws Exception { int result = boardMapper.delete(no); String parentTable = "board"; int parentNo = no; if( result > 0 ) { Files fileInfo = new Files(); fileInfo.setParentTable(parentTable); fileInfo.setParentNo(parentNo); int fileResult = fileService.deleteByParent(fileInfo); result = fileResult; } return result; }
Java
볡사
package com.joeun.server.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Board; import com.joeun.server.dto.Files; import com.joeun.server.mapper.BoardMapper; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class BoardServiceImpl implements BoardService { @Autowired private BoardMapper boardMapper; @Autowired private FileService fileService; @Override public List<Board> list() throws Exception { List<Board> boardList = boardMapper.list(); return boardList; } @Override public Board select(int no) throws Exception { Board board = boardMapper.select(no); return board; } @Override @Transactional public int insert(Board board) throws Exception { int result = boardMapper.insert(board); // μƒˆλ‘œ μƒμ„±λœ λ°μ΄ν„°μ˜ pk κ°€μ Έμ˜΄ int parentNo = boardMapper.maxPk(); board.setNo(parentNo); result += uploadFiles(board); return result; } @Override public int update(Board board) throws Exception { int result = boardMapper.update(board); // 파일 μ—…λ‘œλ“œ result += uploadFiles(board); return result; } @Override public int updateViews(int count, int no) throws Exception { int result = boardMapper.updateViews(count, no); return result; } public int uploadFiles(Board board) throws Exception { String parentTable = "board"; int parentNo = board.getNo(); int result = 0; List<MultipartFile> fileList = board.getFiles(); if( fileList != null && !fileList.isEmpty() ) { Files fileInfo = new Files(); fileInfo.setParentTable(parentTable); fileInfo.setParentNo(parentNo); result = fileService.uploadFiles(fileInfo, fileList); } return result; } @Override @Transactional public int delete(int no) throws Exception { int result = boardMapper.delete(no); String parentTable = "board"; int parentNo = no; // βœ…(New) - κ²Œμ‹œκΈ€μ— μ’…μ†λœ 파일 μ‚­μ œ if( result > 0 ) { Files fileInfo = new Files(); fileInfo.setParentTable(parentTable); fileInfo.setParentNo(parentNo); int fileResult = fileService.deleteByParent(fileInfo); result = fileResult; } return result; } }
Java
볡사

선택 파일 μ‚­μ œ

β€’
FileService.java
β€’
FileServiceImpl.java
β€’
BoardServiceImpl.java
β€’
FileService.java
package com.joeun.server.service; import java.util.List; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import jakarta.servlet.http.HttpServletResponse; public interface FileService { // 파일 λͺ©λ‘ public List<Files> list() throws Exception; // 파일 쑰회 public Files select(int no) throws Exception; // 파일 등둝 public int insert(Files file) throws Exception; // 파일 μˆ˜μ • public int update(Files file) throws Exception; // 파일 μ‚­μ œ public int delete(int no) throws Exception; // 파일 μ—…λ‘œλ“œ public int uploadFiles(Files file, List<MultipartFile> fileList) throws Exception; // 파일 μ—…λ‘œλ“œ public int upload(Files file) throws Exception; // 파일 λͺ©λ‘ - λΆ€λͺ¨ κΈ°μ€€ public List<Files> listByParent(Files file) throws Exception; // 파일 λ‹€μš΄λ‘œλ“œ public int download(int no, HttpServletResponse response) throws Exception; // 파일 μ‚­μ œ - λΆ€λͺ¨ κΈ°μ€€ public int deleteByParent(Files fileInfo) throws Exception; // 파일 μ‚­μ œ - noList κΈ°μ€€ public int deleteByNoList(List<Integer> noList) throws Exception; }
Java
볡사
β€’
FileServiceImpl.java
μΆ”κ°€ν•  μ½”λ“œ
@Override public int deleteByNoList(List<Integer> deleteFileNoList) throws Exception { int result = 0; if( deleteFileNoList != null && !deleteFileNoList.isEmpty() ) for (Integer deleteFileNo : deleteFileNoList) { if( deleteFileNo == null ) continue; fileMapper.delete(deleteFileNo); log.info(deleteFileNo + "번 파일 μ‚­μ œ"); result++; } return result; }
Java
볡사
package com.joeun.server.service; import java.io.File; import java.io.FileInputStream; import java.util.List; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.FileCopyUtils; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import com.joeun.server.mapper.FileMapper; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class FileServiceImpl implements FileService { @Autowired private FileMapper fileMapper; @Value("${upload.path}") // application.properties 에 μ„€μ •ν•œ μ—…λ‘œλ“œ 경둜 속성λͺ… private String uploadPath; // μ—…λ‘œλ“œ 경둜 @Override public List<Files> list() throws Exception { List<Files> fileList = fileMapper.list(); return fileList; } @Override public Files select(int no) throws Exception { Files file = fileMapper.select(no); return file; } @Override public int insert(Files file) throws Exception { int result = fileMapper.insert(file); return result; } @Override public int update(Files file) throws Exception { int result = fileMapper.update(file); return result; } @Override public List<Files> listByParent(Files file) throws Exception { List<Files> fileList = fileMapper.listByParent(file); return fileList; } @Override public int deleteByParent(Files fileInfo) throws Exception { int result = 0; List<Files> fileList = fileMapper.listByParent(fileInfo); for (Files file : fileList) { File deleteFile = new File(file.getFilePath()); if( !deleteFile.exists() ) continue; if( !deleteFile.delete() ) continue; int deleted = fileMapper.delete(file.getNo()); if( deleted > 0 ) result++; } log.info(result + "개의 νŒŒμΌμ„ μ‚­μ œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int uploadFiles(Files fileInfo, List<MultipartFile> fileList) throws Exception { int result = 0; for (MultipartFile file : fileList) { result += uploadFile( fileInfo, file); } log.info(result + "개 νŒŒμΌμ„ μ—…λ‘œλ“œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int deleteByNoList(List<Integer> deleteFileNoList) throws Exception { int result = 0; if( deleteFileNoList != null && !deleteFileNoList.isEmpty() ) for (Integer deleteFileNo : deleteFileNoList) { if( deleteFileNo == null ) continue; fileMapper.delete(deleteFileNo); log.info(deleteFileNo + "번 파일 μ‚­μ œ"); result++; } return result; } @Override @Transactional public int upload(Files file) throws Exception { int result = uploadFile(file, file.getFile()); if( result > 0 ) result = fileMapper.maxPk(); return result; } public int uploadFile(Files fileInfo, MultipartFile file) throws Exception { int result = 0; if( file.isEmpty() ) return result; // 파일 정보 : μ›λ³ΈνŒŒμΌλͺ…, 파일 μš©λŸ‰, 파일 데이터 String originName = file.getOriginalFilename(); long fileSize = file.getSize(); byte[] fileData = file.getBytes(); // μ—…λ‘œλ“œ 경둜 // 파일λͺ… 쀑볡 방지 방법(μ •μ±…) // - λ‚ μ§œ_파일λͺ….ν™•μž₯자 // - UID_파일λͺ….ν™•μž₯자 // UID_강아지.png String fileName = UUID.randomUUID().toString() + "_" + originName; // c:/upload/UID_강아지.png String filePath = uploadPath + "/" + fileName; // - μ„œλ²„ μΈ‘, 파일 μ‹œμŠ€ν…œμ— 파일 볡사 File uploadFile = new File(uploadPath, fileName); FileCopyUtils.copy(fileData, uploadFile); // 파일 μ—…λ‘œλ“œ // - DB 에 파일 정보 등둝 Files uploadedFile = new Files(); uploadedFile.setParentTable(fileInfo.getParentTable()); uploadedFile.setParentNo(fileInfo.getParentNo()); uploadedFile.setFileName(fileName); uploadedFile.setFilePath(filePath); uploadedFile.setOriginName(originName); uploadedFile.setFileSize(fileSize); uploadedFile.setFileCode(0); result = fileMapper.insert(uploadedFile); return result; } // 파일 λͺ©λ‘ 쑰회 @Override public List<Files> listByParent(Files file) throws Exception { List<Files> fileList = fileMapper.listByParent(file); return fileList; } @Override public int download(int fileNo, HttpServletResponse response) throws Exception { // result // 0 : 파일 λ‹€μš΄λ‘œλ“œ 처리 μ‹€νŒ¨ // 1 : 파일 λ‹€μš΄λ‘œλ“œ 성곡 Files file = fileMapper.select(fileNo); if( file == null ) { // BAD_REQUEST : 400, ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ΄ 잘λͺ»λ˜μ—ˆμŒμ„ μ•Œλ €μ£ΌλŠ” μƒνƒœμ½”λ“œ // response.setStatus(response.SC_BAD_REQUEST); return 0; } String filePath = file.getFilePath(); // 파일 경둜 String fileName = file.getFileName(); // 파일 이름 // λ‹€μš΄λ‘œλ“œ 응닡을 μœ„ν•œ 헀더 μ„ΈνŒ… // - ContentType : application/octet-stream // - Content-Disposition : attachment, filename="파일λͺ….ν™•μž₯자" response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // 파일 λ‹€μš΄λ‘œλ“œ // - 파일 μž…λ ₯ File downloadFile = new File(filePath); FileInputStream fis = new FileInputStream(downloadFile); // - 파일 좜λ ₯ ServletOutputStream sos = response.getOutputStream(); // λ‹€μš΄λ‘œλ“œ FileCopyUtils.copy(fis, sos); fis.close(); sos.close(); return 1; } @Override public int deleteByParent(Files fileInfo) throws Exception { int result = 0; List<Files> fileList = fileMapper.listByParent(fileInfo); for (Files file : fileList) { File deleteFile = new File(file.getFilePath()); if( !deleteFile.exists() ) continue; if( !deleteFile.delete() ) continue; int deleted = fileMapper.delete(file.getNo()); if( deleted > 0 ) result++; } log.info(result + "개의 νŒŒμΌμ„ μ‚­μ œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int deleteByNoList(List<Integer> deleteFileNoList) throws Exception { int result = 0; if( deleteFileNoList != null && !deleteFileNoList.isEmpty() ) for (Integer deleteFileNo : deleteFileNoList) { if( deleteFileNo == null ) continue; fileMapper.delete(deleteFileNo); log.info(deleteFileNo + "번 파일 μ‚­μ œ"); result++; } return result; } }
Java
볡사
β€’
BoardServiceImpl.java
μΆ”κ°€ν•  μ½”λ“œ
@Override public int update(Board board) throws Exception { int result = boardMapper.update(board); // 파일 μ—…λ‘œλ“œ result += uploadFiles(board); // κ°œλ³„ 파일 μ‚­μ œ List<Integer> deleteFileNoList = board.getDeleteFileNoList(); result += fileService.deleteByNoList(deleteFileNoList); return result; }
Java
볡사
package com.joeun.server.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Board; import com.joeun.server.dto.Files; import com.joeun.server.mapper.BoardMapper; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class BoardServiceImpl implements BoardService { @Autowired private BoardMapper boardMapper; @Autowired private FileService fileService; @Override public List<Board> list() throws Exception { List<Board> boardList = boardMapper.list(); return boardList; } @Override public Board select(int no) throws Exception { Board board = boardMapper.select(no); return board; } @Override @Transactional public int insert(Board board) throws Exception { int result = boardMapper.insert(board); // μƒˆλ‘œ μƒμ„±λœ λ°μ΄ν„°μ˜ pk κ°€μ Έμ˜΄ int parentNo = boardMapper.maxPk(); board.setNo(parentNo); result += uploadFiles(board); return result; } @Override public int update(Board board) throws Exception { int result = boardMapper.update(board); // 파일 μ—…λ‘œλ“œ result += uploadFiles(board); // βœ…(New) κ°œλ³„ 파일 μ‚­μ œ List<Integer> deleteFileNoList = board.getDeleteFileNoList(); result += fileService.deleteByNoList(deleteFileNoList); return result; } @Override public int updateViews(int count, int no) throws Exception { int result = boardMapper.updateViews(count, no); return result; } public int uploadFiles(Board board) throws Exception { String parentTable = "board"; int parentNo = board.getNo(); int result = 0; List<MultipartFile> fileList = board.getFiles(); if( fileList != null && !fileList.isEmpty() ) { Files fileInfo = new Files(); fileInfo.setParentTable(parentTable); fileInfo.setParentNo(parentNo); result = fileService.uploadFiles(fileInfo, fileList); } return result; } @Override @Transactional public int delete(int no) throws Exception { int result = boardMapper.delete(no); String parentTable = "board"; int parentNo = no; if( result > 0 ) { Files fileInfo = new Files(); fileInfo.setParentTable(parentTable); fileInfo.setParentNo(parentNo); int fileResult = fileService.deleteByParent(fileInfo); result = fileResult; } return result; } }
Java
볡사

κ°œλ³„ 파일 μ‚­μ œ

β€’
FileService.java
β€’
FileServiceImpl.java
β€’
FileController.java

FileService.java

// 파일 μ‚­μ œ public int delete(int no) throws Exception;
Java
볡사
package com.joeun.server.service; import java.util.List; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import jakarta.servlet.http.HttpServletResponse; public interface FileService { // 파일 λͺ©λ‘ public List<Files> list() throws Exception; // 파일 쑰회 public Files select(int no) throws Exception; // 파일 등둝 public int insert(Files file) throws Exception; // 파일 μˆ˜μ • public int update(Files file) throws Exception; // 파일 μ‚­μ œ public int delete(int no) throws Exception; // 파일 μ—…λ‘œλ“œ public int uploadFiles(Files file, List<MultipartFile> fileList) throws Exception; // 파일 μ—…λ‘œλ“œ public int upload(Files file) throws Exception; // 파일 λͺ©λ‘ - λΆ€λͺ¨ κΈ°μ€€ public List<Files> listByParent(Files file) throws Exception; // 파일 λ‹€μš΄λ‘œλ“œ public int download(int no, HttpServletResponse response) throws Exception; // 파일 μ‚­μ œ - λΆ€λͺ¨ κΈ°μ€€ public int deleteByParent(Files fileInfo) throws Exception; // 파일 μ‚­μ œ - noList κΈ°μ€€ public int deleteByNoList(List<Integer> noList) throws Exception; }
Java
볡사

FileServiceImpl.java

@Override public int delete(int no) throws Exception { Files file = fileMapper.select(no); if( file == null ) return 0; String filePath = file.getFilePath(); File deleteFile = new File(filePath); if( !deleteFile.exists() ) return 0; boolean deleted = deleteFile.delete(); int result = 0; if( deleted ) { result = fileMapper.delete(no); } return result; }
Java
볡사
package com.joeun.server.service; import java.io.File; import java.io.FileInputStream; import java.util.List; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.FileCopyUtils; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import com.joeun.server.mapper.FileMapper; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class FileServiceImpl implements FileService { @Autowired private FileMapper fileMapper; @Value("${upload.path}") // application.properties 에 μ„€μ •ν•œ μ—…λ‘œλ“œ 경둜 속성λͺ… private String uploadPath; // μ—…λ‘œλ“œ 경둜 @Override public List<Files> list() throws Exception { List<Files> fileList = fileMapper.list(); return fileList; } @Override public Files select(int no) throws Exception { Files file = fileMapper.select(no); return file; } @Override public int insert(Files file) throws Exception { int result = fileMapper.insert(file); return result; } @Override public int update(Files file) throws Exception { int result = fileMapper.update(file); return result; } @Override public int delete(int no) throws Exception { Files file = fileMapper.select(no); if( file == null ) return 0; String filePath = file.getFilePath(); File deleteFile = new File(filePath); if( !deleteFile.exists() ) return 0; boolean deleted = deleteFile.delete(); int result = 0; if( deleted ) { result = fileMapper.delete(no); } return result; } @Override public List<Files> listByParent(Files file) throws Exception { List<Files> fileList = fileMapper.listByParent(file); return fileList; } @Override public int deleteByParent(Files fileInfo) throws Exception { int result = 0; List<Files> fileList = fileMapper.listByParent(fileInfo); for (Files file : fileList) { File deleteFile = new File(file.getFilePath()); if( !deleteFile.exists() ) continue; if( !deleteFile.delete() ) continue; int deleted = fileMapper.delete(file.getNo()); if( deleted > 0 ) result++; } log.info(result + "개의 νŒŒμΌμ„ μ‚­μ œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int download(int fileNo, HttpServletResponse response) throws Exception { // result // 0 : 파일 λ‹€μš΄λ‘œλ“œ 처리 μ‹€νŒ¨ // 1 : 파일 λ‹€μš΄λ‘œλ“œ 성곡 Files file = fileMapper.select(fileNo); if( file == null ) { // BAD_REQUEST : 400, ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ΄ 잘λͺ»λ˜μ—ˆμŒμ„ μ•Œλ €μ£ΌλŠ” μƒνƒœμ½”λ“œ // response.setStatus(response.SC_BAD_REQUEST); return 0; } String filePath = file.getFilePath(); // 파일 경둜 String fileName = file.getFileName(); // 파일 이름 // λ‹€μš΄λ‘œλ“œ 응닡을 μœ„ν•œ 헀더 μ„ΈνŒ… // - ContentType : application/octet-stream // - Content-Disposition : attachment, filename="파일λͺ….ν™•μž₯자" response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // 파일 λ‹€μš΄λ‘œλ“œ // - 파일 μž…λ ₯ File downloadFile = new File(filePath); FileInputStream fis = new FileInputStream(downloadFile); // - 파일 좜λ ₯ ServletOutputStream sos = response.getOutputStream(); // λ‹€μš΄λ‘œλ“œ FileCopyUtils.copy(fis, sos); // byte[] buffer = new byte[1024]; // 1024bytes = 1KB λ‹¨μœ„ 버퍼 // int data; // while( (data = fis.read(buffer)) != -1 ) { // 1KB μ”© νŒŒμΌμž…λ ₯ // sos.write(buffer, 0, data); // 1KB μ”© 파일좜λ ₯ // } fis.close(); sos.close(); return 1; } @Override public int uploadFiles(Files fileInfo, List<MultipartFile> fileList) throws Exception { int result = 0; for (MultipartFile file : fileList) { result += uploadFile( fileInfo, file); } log.info(result + "개 νŒŒμΌμ„ μ—…λ‘œλ“œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int deleteByNoList(List<Integer> deleteFileNoList) throws Exception { int result = 0; if( deleteFileNoList != null && !deleteFileNoList.isEmpty() ) for (Integer deleteFileNo : deleteFileNoList) { if( deleteFileNo == null ) continue; fileMapper.delete(deleteFileNo); log.info(deleteFileNo + "번 파일 μ‚­μ œ"); result++; } return result; } @Override @Transactional public int upload(Files file) throws Exception { int result = uploadFile(file, file.getFile()); if( result > 0 ) result = fileMapper.maxPk(); return result; } public int uploadFile(Files fileInfo, MultipartFile file) throws Exception { int result = 0; if( file.isEmpty() ) return result; // 파일 정보 : μ›λ³ΈνŒŒμΌλͺ…, 파일 μš©λŸ‰, 파일 데이터 String originName = file.getOriginalFilename(); long fileSize = file.getSize(); byte[] fileData = file.getBytes(); // μ—…λ‘œλ“œ 경둜 // 파일λͺ… 쀑볡 방지 방법(μ •μ±…) // - λ‚ μ§œ_파일λͺ….ν™•μž₯자 // - UID_파일λͺ….ν™•μž₯자 // UID_강아지.png String fileName = UUID.randomUUID().toString() + "_" + originName; // c:/upload/UID_강아지.png String filePath = uploadPath + "/" + fileName; // - μ„œλ²„ μΈ‘, 파일 μ‹œμŠ€ν…œμ— 파일 볡사 File uploadFile = new File(uploadPath, fileName); FileCopyUtils.copy(fileData, uploadFile); // 파일 μ—…λ‘œλ“œ // - DB 에 파일 정보 등둝 Files uploadedFile = new Files(); uploadedFile.setParentTable(fileInfo.getParentTable()); uploadedFile.setParentNo(fileInfo.getParentNo()); uploadedFile.setFileName(fileName); uploadedFile.setFilePath(filePath); uploadedFile.setOriginName(originName); uploadedFile.setFileSize(fileSize); uploadedFile.setFileCode(0); result = fileMapper.insert(uploadedFile); return result; } }
Java
볡사

FileController.java

package com.joeun.server.controller; import java.io.File; import java.io.FileInputStream; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.util.FileCopyUtils; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import com.joeun.server.dto.Files; import com.joeun.server.service.FileService; import com.joeun.server.utils.MediaUtil; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @Slf4j @Controller @RequestMapping("/file") public class FileController { @Autowired private FileService fileService; @Autowired private ResourceLoader resourceLoader; /** * 파일 λ‹€μš΄λ‘œλ“œ * @param fileNo * @param response * @throws Exception */ @GetMapping("/{no}") public void fileDownload(@PathVariable("no") int no ,HttpServletResponse response) throws Exception { // 파일 쑰회 Files file = fileService.select(no); // 파일이 μ‘΄μž¬ν•˜μ§€ μ•ŠμœΌλ©΄, if( file == null ) { // 응닡 μƒνƒœμ½”λ“œ : 400, ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ΄ 잘λͺ»λ˜μ—ˆμŒμ„ λ‚˜νƒ€λ‚΄λŠ” μƒνƒœμ½”λ“œ response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } String fileName = file.getFileName(); // 파일 λͺ… String filePath = file.getFilePath(); // 파일 경둜 // 파일 λ‹€μš΄λ‘œλ“œλ₯Ό μœ„ν•œ 헀더 μ„ΈνŒ… // - ContentType : application/octet-straem // - Content-Disposition : attachment; fileanme="파일λͺ….ν™•μž₯자" response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // 파일 μž…λ ₯ File downloadFile = new File(filePath); FileInputStream fis = new FileInputStream(downloadFile); ServletOutputStream sos = response.getOutputStream(); // λ‹€μš΄λ‘œλ“œ FileCopyUtils.copy(fis, sos); } /** * 이미지 썸넀일 * @param no * @param response * @throws Exception */ @GetMapping("/img/{no}") public void showImg(@PathVariable Integer no, HttpServletResponse response) throws Exception { Files file = fileService.select(no); String filePath = (file != null) ? file.getFilePath() : null; Resource resource = resourceLoader.getResource("classpath:static/img/no-image.png"); File imgFile; if (filePath == null || !(imgFile = new File(filePath)).exists()) { // 파일이 μ‘΄μž¬ν•˜μ§€ μ•Šκ±°λ‚˜ 파일 κ²½λ‘œκ°€ null인 경우 imgFile = resource.getFile(); } String ext = filePath.substring(filePath.lastIndexOf(".") + 1); MediaType mType = MediaUtil.getMediaType(ext); if (mType == null) { // 이미지 νƒ€μž…μ΄ 아닐 경우 response.setContentType(MediaType.IMAGE_PNG_VALUE); // 기본적으둜 PNG둜 μ„€μ • imgFile = resource.getFile(); } else { // 이미지 νƒ€μž…μΌ 경우 response.setContentType(mType.toString()); } FileInputStream fis = new FileInputStream(imgFile); ServletOutputStream sos = response.getOutputStream(); FileCopyUtils.copy(fis, sos); } /** * 파일 μ‚­μ œ * @param file * @return * @throws Exception */ @DeleteMapping("/{no}") public ResponseEntity<String> deleteFile(@PathVariable Integer no) throws Exception { log.info("[DELETE] - /file"); int fileNo = no; log.info("fileNo : " + fileNo); if( fileNo == 0 ) return new ResponseEntity<String>("FAIL", HttpStatus.BAD_REQUEST); int result = fileService.delete(fileNo); if( result == 0 ) return new ResponseEntity<String>("FAIL", HttpStatus.OK); return new ResponseEntity<String>("SUCCESS", HttpStatus.OK); } }
Java
볡사

React (Front-End)

React 둜 파일 μ—…λ‘œλ“œ, 쑰회, μ‚­μ œ UI 및 API λ₯Ό κ΅¬ν˜„ν•©λ‹ˆλ‹€.

파일 μ—…λ‘œλ“œ

파일 등둝 UI μΆ”κ°€

BoardInsertForm.jsx

import React, { useState } from 'react' import { Link } from 'react-router-dom' import './BoardInsertForm.css' const BoardInsertForm = ({ onInsert }) => { // state μ„€μ • const [title, setTitle] = useState(''); const [writer, setWriter] = useState(''); const [content, setContent] = useState(''); const [files, setFiles] = useState(null); // βœ… files state μΆ”κ°€ const handleChangeTitle = (e) => { setTitle(e.target.value) } const handleChangeWriter = (e) => { setWriter(e.target.value) } const handleChangeContent = (e) => { setContent(e.target.value) } // βœ… 파일 ν•Έλ“€λŸ¬ μΆ”κ°€ const handleFileChange = (e) => { setFiles(e.target.files); }; const onSubmit = () => { const formData = new FormData(); formData.append('title', title); formData.append('writer', writer); formData.append('content', content); const headers = { headers: { 'Content-Type' : 'multipart/form-data', }, }; if (files) { for (let i = 0; i < files.length; i++) { formData.append(`files[${i}]`, files[i]); } } // onInsert(title, writer, content) // onInsert({title,writer,content,files}, headers) // formData μ‚¬μš© ❌ onInsert(formData, headers) // formData μ‚¬μš© β­• } return ( <div className='container'> <h1 className='title'>κ²Œμ‹œκΈ€ 등둝</h1> <table className='table'> <tbody> <tr> <td>제λͺ©</td> <td> <input type="text" className='form-input' value={title} onChange={handleChangeTitle} /> </td> </tr> <tr> <td>μž‘μ„±μž</td> <td> <input type="text" className='form-input' value={writer} onChange={handleChangeWriter} /> </td> </tr> <tr> <td colSpan={2}>λ‚΄μš©</td> </tr> <tr> <td colSpan={2}> <textarea cols="40" rows="10" className='form-input' value={content} onChange={handleChangeContent} ></textarea> </td> </tr> <tr> <td>파일</td> <td> <input type='file' onChange={handleFileChange} multiple /> </td> </tr> </tbody> </table> <div className='btn-box'> <Link to="/boards" className='btn'>λͺ©λ‘</Link> <button onClick={ onSubmit } className='btn'>등둝</button> </div> </div> ) } export default BoardInsertForm
JavaScript
볡사

파일 μ—…λ‘œλ“œ μš”μ²­ API

files.js

import axios from 'axios'; // μ—…λ‘œλ“œ export const upload = (formData, headers) => axios.post(`/file/upload`, formData, headers)
JavaScript
볡사

︎ 파일 쑰회

파일 λͺ©λ‘ 쑰회 UI 및 API μΆ”κ°€

BoardReadContainer.jsx

import React, { useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; import * as boards from '../apis/boards'; import BoardRead from '../components/BoardRead/BoardRead'; // πŸ‘©β€πŸ’» κ²Œμ‹œκΈ€ 쑰회 const BoardReadContainer = () => { const { no } = useParams() const [board, setBoard] = useState({}); const [fileList, setFileList] = useState([]); const [isLoading, setLoading] = useState(false); const getBoard = async () => { setLoading(true) try { const response = await boards.select(no); const data = response.data; console.log(data); const board = data.board; const fileList = data.fileList; setBoard(board); setFileList(fileList); } catch(e) { console.log(e); } setLoading(false) } useEffect( () => { getBoard() },[]) return (<BoardRead no={no} board={board} fileList={fileList} isLoading={isLoading} />) } export default BoardReadContainer
JavaScript
볡사

BoardRead.jsx

import React, { useEffect } from 'react'; import { Link } from 'react-router-dom'; import * as format from '../../apis/format'; import './BoardRead.css'; const BoardRead = ({ no, board, fileList, isLoading }) => { return ( <div className='container'> <h1 className='title'>κ²Œμ‹œκΈ€ 쑰회</h1> <h3>번호 : {no}</h3> <hr/> { isLoading && <div> <img src="/img/loading.webp" alt="loading" /> </div> } { !isLoading && board && ( <table border={1} className='table'> <tbody> <tr> <td>번호</td> <td> <input type="text" className='form-input' value={board.no} readOnly /> </td> </tr> <tr> <td>λ“±λ‘μΌμž</td> <td> <input type="text" className='form-input' value={format.formatDate( board.regDate )} readOnly /> </td> </tr> <tr> <td>제λͺ©</td> <td> <input type="text" className='form-input' value={board.title} readOnly /> </td> </tr> <tr> <td>μž‘μ„±μž</td> <td> <input type="text" className='form-input' value={board.writer} readOnly /> </td> </tr> <tr> <td colSpan={2}>λ‚΄μš©</td> </tr> <tr> <td colSpan={2}> <textarea cols="40" rows="10" className='form-input' value={board.content} readOnly></textarea> </td> </tr> <tr> <td colSpan={2}>파일</td> </tr> <tr> <td colSpan={2}> { fileList.map( (file) => ( <div className='file-box' key={file.no}> <div className="item"> <img src={`/file/img/${file.no}`} alt={file.fileName} /> <span>{file.originName}({ format.byteToUnit(file.fileSize) })</span> </div> <div className="item"> </div> </div> ))} </td> </tr> </tbody> </table> )} <hr /> <div className="btn-box"> <Link className='btn' to="/boards">λͺ©λ‘</Link> <Link className='btn' to={`/boards/update/${no}`}>μˆ˜μ •</Link> </div> </div> ) } export default BoardRead
JavaScript
볡사

λ‹€μš΄λ‘œλ“œ UI 및 API μΆ”κ°€

BoardRead.jsx

import React, { useEffect } from 'react'; import { Link } from 'react-router-dom'; import * as format from '../../apis/format'; import './BoardRead.css'; const BoardRead = ({ no, board, fileList, isLoading }) => { return ( <div className='container'> <h1 className='title'>κ²Œμ‹œκΈ€ 쑰회</h1> <h3>번호 : {no}</h3> <hr/> { isLoading && <div> <img src="/img/loading.webp" alt="loading" /> </div> } { !isLoading && board && ( <table border={1} className='table'> <tbody> <tr> <td>번호</td> <td> <input type="text" className='form-input' value={board.no} readOnly /> </td> </tr> <tr> <td>λ“±λ‘μΌμž</td> <td> <input type="text" className='form-input' value={format.formatDate( board.regDate )} readOnly /> </td> </tr> <tr> <td>제λͺ©</td> <td> <input type="text" className='form-input' value={board.title} readOnly /> </td> </tr> <tr> <td>μž‘μ„±μž</td> <td> <input type="text" className='form-input' value={board.writer} readOnly /> </td> </tr> <tr> <td colSpan={2}>λ‚΄μš©</td> </tr> <tr> <td colSpan={2}> <textarea cols="40" rows="10" className='form-input' value={board.content} readOnly></textarea> </td> </tr> <tr> <td colSpan={2}>파일</td> </tr> <tr> <td colSpan={2}> { fileList.map( (file) => ( <div className='file-box' key={file.no}> <div className="item"> <img src={`/file/img/${file.no}`} alt={file.fileName} /> <span>{file.originName}({ format.byteToUnit(file.fileSize) })</span> </div> <div className="item"> <button className="btn" onClick={() => handleDownload(file.no, file.originName)}>λ‹€μš΄λ‘œλ“œ</button> </div> </div> ))} </td> </tr> </tbody> </table> )} <hr /> <div className="btn-box"> <Link className='btn' to="/boards">λͺ©λ‘</Link> <Link className='btn' to={`/boards/update/${no}`}>μˆ˜μ •</Link> </div> </div> ) } export default BoardRead
JavaScript
볡사

files.js

JavaScript
볡사

썸넀일 이미지 UI μΆ”κ°€

파일 μ‚­μ œ

μ‚­μ œν•  파일 μ²΄ν¬λ°•μŠ€ UI μΆ”κ°€ 및 μˆ˜μ • μš”μ²­ μ‹œ, μ‚­μ œν•  파일 번호 리슀트 데이터 μΆ”κ°€

κ°œλ³„ 파일 μ‚­μ œ UI 및 API μΆ”κ°€

κ²Œμ‹œνŒ - 파일 μ—…λ‘œλ“œ

Spring Boot (Back-End)

Spring Boot 둜 파일 μ—…λ‘œλ“œ, 쑰회, μ‚­μ œ κΈ°λŠ₯을 κ΅¬ν˜„ν•©λ‹ˆλ‹€.

κ΅¬ν˜„ κΈ°λŠ₯

β€’
파일 μ—…λ‘œλ“œ
β—¦
λ°μ΄ν„°λ² μ΄μŠ€μ— 등둝
β—¦
파일 μ‹œμŠ€ν…œμ— 등둝
β€’
︎ 파일 쑰회
β—¦
파일 λͺ©λ‘ 쑰회
β—¦
λ‹€μš΄λ‘œλ“œ
β—¦
썸넀일 이미지 보기
β€’
파일 μ‚­μ œ
β—¦
κ²Œμ‹œκΈ€μ— μ’…μ†λœ μ—¬λŸ¬ 파일 μ‚­μ œ
β—¦
선택 파일 μ‚­μ œ
β—¦
κ°œλ³„ 파일 μ‚­μ œ

React (Front-End)

React 둜 파일 μ—…λ‘œλ“œ, 쑰회, μ‚­μ œ UI 및 API λ₯Ό κ΅¬ν˜„ν•©λ‹ˆλ‹€.
β€’
파일 μ—…λ‘œλ“œ
β—¦
파일 등둝 UI μΆ”κ°€
β—¦
파일 μ—…λ‘œλ“œ μš”μ²­ API
β€’
︎ 파일 쑰회
β—¦
파일 λͺ©λ‘ 쑰회 UI 및 API μΆ”κ°€
β—¦
λ‹€μš΄λ‘œλ“œ UI 및 API μΆ”κ°€
β—¦
썸넀일 이미지 UI μΆ”κ°€
β€’
파일 μ‚­μ œ
β—¦
κ²Œμ‹œκΈ€μ— μ’…μ†λœ μ—¬λŸ¬ 파일 μ‚­μ œ (κ²Œμ‹œκΈ€ μ‚­μ œ μ‹œ, λ°±μ—”λ“œμ—μ„œ μ²˜λ¦¬ν•¨)
β—¦
μ‚­μ œν•  파일 μ²΄ν¬λ°•μŠ€ UI μΆ”κ°€ 및 μˆ˜μ • μš”μ²­ μ‹œ, μ‚­μ œν•  파일 번호 리슀트 데이터 μΆ”κ°€
β—¦
κ°œλ³„ 파일 μ‚­μ œ UI 및 API μΆ”κ°€

Spring Boot (Back-End)

Spring Boot 둜 파일 μ—…λ‘œλ“œ, 쑰회, μ‚­μ œ κΈ°λŠ₯을 κ΅¬ν˜„ν•©λ‹ˆλ‹€.

파일 μ—…λ‘œλ“œ

λ°μ΄ν„°λ² μ΄μŠ€μ— 등둝

1.
데이터 베이슀 - ν…Œμ΄λΈ” 생성
2.
SQL 쿼리 Mapper μž‘μ„±
3.
DTO
4.
Mapper
5.
Service
6.
Controller

데이터 베이슀 - ν…Œμ΄λΈ” 생성

CREATE TABLE `file` ( `no` int NOT NULL AUTO_INCREMENT, -- 파일 번호 (μžλ™μ¦κ°€) `parent_table` varchar(45) NOT NULL, -- λΆ€λͺ¨ ν…Œμ΄λΈ”λͺ… (예: 'board') `parent_no` int NOT NULL, -- λΆ€λͺ¨ ν…Œμ΄λΈ”μ—μ„œμ˜ 번호 `file_name` text NOT NULL, -- μ €μž₯된 파일λͺ… `origin_name` text, -- 원본 파일λͺ… `file_path` text NOT NULL, -- 파일 경둜 `file_size` int NOT NULL DEFAULT '0', -- 파일 크기 (κΈ°λ³Έκ°’ 0) `reg_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, -- λ“±λ‘μΌμ‹œ `upd_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, -- μˆ˜μ •μΌμ‹œ `file_code` int NOT NULL DEFAULT '0', -- 파일 μ½”λ“œ (κΈ°λ³Έκ°’ 0) PRIMARY KEY (`no`) -- μ£Όν‚€ μ„€μ • ) COMMENT='파일';
SQL
볡사

SQL 쿼리 Mapper μž‘μ„±

β€’
FileMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace="맀퍼 μΈν„°νŽ˜μ΄μŠ€ 경둜" --> <mapper namespace="com.joeun.server.mapper.FileMapper"> <!-- 파일 λͺ©λ‘ --> <select id="list" resultType="Files"> SELECT * FROM file ORDER BY reg_date DESC </select> <!-- 파일 λͺ©λ‘ - λΆ€λͺ¨ ν…Œμ΄λΈ” κΈ°μ€€ --> <!-- * 파일이 μ’…μ†λ˜λŠ” ν…Œμ΄λΈ”μ„ κΈ°μ€€μœΌλ‘œ 파일 λͺ©λ‘μ„ 쑰회 --> <!-- * κ²Œμ‹œκΈ€ 번호 10 πŸ“„ 파일 번호 1 πŸ“„ 파일 번호 2 πŸ“„ 파일 번호 3 --> <select id="listByParent" resultType="Files"> SELECT * FROM file WHERE parent_table = #{parentTable} AND parent_no = #{parentNo} ORDER BY reg_date DESC </select> <!-- 파일 쑰회 --> <select id="select" resultType="Files"> SELECT * FROM file WHERE no = #{no} </select> <!-- 파일 등둝 --> <insert id="insert"> INSERT INTO file( parent_table, parent_no, file_name, origin_name, file_path, file_size, file_code ) VALUES ( #{parentTable}, #{parentNo}, #{fileName}, #{originName}, #{filePath}, #{fileSize}, #{fileCode} ) </insert> <!-- 파일 μˆ˜μ • --> <update id="update"> UPDATE file SET parent_table = #{parentTable} ,parent_no = #{parentNo} ,file_name = #{fileName} ,origin_name = #{originName} ,file_path = #{filePath} ,file_size = #{fileSize} ,file_code = #{fileCode} WHERE no = #{no} </update> <!-- 파일 μ‚­μ œ --> <delete id="delete"> DELETE FROM file WHERE no = #{no} </delete> <!-- 파일 λͺ©λ‘ μ‚­μ œ - λΆ€λͺ¨ ν…Œμ΄λΈ” κΈ°μ€€ 파일 λͺ©λ‘ μ‚­μ œ --> <delete id="deleteByParent"> DELETE FROM file WHERE parent_table = #{parentTable} AND parent_no = #{parentNo} </delete> <!-- κ²Œμ‹œκΈ€ 번호 μ΅œλŒ“κ°’ --> <select id="maxPk" resultType="int"> SELECT MAX(no) FROM file </select> </mapper>
SQL
볡사

DTO

β€’
Files.java
package com.joeun.server.dto; import java.util.Date; import lombok.Data; @Data public class Files { private int no; private String parentTable; private int parentNo; private String fileName; private String originName; private String filePath; private long fileSize; private Date regDate; private Date updDate; private int fileCode; }
Java
볡사

Mapper

β€’
FileMapper.java
package com.joeun.server.mapper; import java.util.List; import org.apache.ibatis.annotations.Mapper; import com.joeun.server.dto.Files; @Mapper public interface FileMapper { // 파일 λͺ©λ‘ public List<Files> list() throws Exception; // 파일 쑰회 public Files select(int no) throws Exception; // 파일 등둝 public int insert(Files file) throws Exception; // 파일 μˆ˜μ • public int update(Files file) throws Exception; // 파일 μ‚­μ œ public int delete(int no) throws Exception; // 파일 번호(κΈ°λ³Έν‚€) μ΅œλŒ“κ°’ public int maxPk() throws Exception; // 파일 λͺ©λ‘ - λΆ€λͺ¨ κΈ°μ€€ public List<Files> listByParent(Files file) throws Exception; // 파일 μ‚­μ œ - λΆ€λͺ¨ κΈ°μ€€ public int deleteByParent(Files file) throws Exception; }
Java
볡사

Service

β€’
FileService.java
β€’
FileServiceImpl.java
β€’
BoardServiceImpl.java
β€’
FileService.java
package com.joeun.server.service; import java.util.List; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import jakarta.servlet.http.HttpServletResponse; public interface FileService { // 파일 λͺ©λ‘ public List<Files> list() throws Exception; // 파일 쑰회 public Files select(int no) throws Exception; // 파일 등둝 public int insert(Files file) throws Exception; // 파일 μˆ˜μ • public int update(Files file) throws Exception; // 파일 μ—…λ‘œλ“œ public int uploadFiles(Files file, List<MultipartFile> fileList) throws Exception; // 파일 μ—…λ‘œλ“œ public int upload(Files file) throws Exception; }
Java
볡사
β€’
FileServiceImpl.java
package com.joeun.server.service; import java.io.File; import java.io.FileInputStream; import java.util.List; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.FileCopyUtils; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import com.joeun.server.mapper.FileMapper; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class FileServiceImpl implements FileService { @Autowired private FileMapper fileMapper; @Value("${upload.path}") // application.properties 에 μ„€μ •ν•œ μ—…λ‘œλ“œ 경둜 속성λͺ… private String uploadPath; // μ—…λ‘œλ“œ 경둜 @Override public List<Files> list() throws Exception { List<Files> fileList = fileMapper.list(); return fileList; } @Override public Files select(int no) throws Exception { Files file = fileMapper.select(no); return file; } @Override public int insert(Files file) throws Exception { int result = fileMapper.insert(file); return result; } @Override public int update(Files file) throws Exception { int result = fileMapper.update(file); return result; } @Override public int uploadFiles(Files fileInfo, List<MultipartFile> fileList) throws Exception { int result = 0; for (MultipartFile file : fileList) { result += uploadFile( fileInfo, file ); } log.info(result + "개 νŒŒμΌμ„ μ—…λ‘œλ“œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override @Transactional public int upload(Files file) throws Exception { int result = uploadFile(file, file.getFile()); if( result > 0 ) result = fileMapper.maxPk(); return result; } public int uploadFile(Files fileInfo, MultipartFile file) throws Exception { int result = 0; if( file.isEmpty() ) return result; // 파일 정보 : μ›λ³ΈνŒŒμΌλͺ…, 파일 μš©λŸ‰, 파일 데이터 String originName = file.getOriginalFilename(); long fileSize = file.getSize(); // μ—…λ‘œλ“œ 경둜 // 파일λͺ… 쀑볡 방지 방법(μ •μ±…) // - λ‚ μ§œ_파일λͺ….ν™•μž₯자 // - UID_파일λͺ….ν™•μž₯자 // UID_강아지.png String fileName = UUID.randomUUID().toString() + "_" + originName; // c:/upload/UID_강아지.png String filePath = uploadPath + "/" + fileName; // - DB 에 파일 정보 등둝 Files uploadedFile = new Files(); uploadedFile.setParentTable(fileInfo.getParentTable()); uploadedFile.setParentNo(fileInfo.getParentNo()); uploadedFile.setFileName(fileName); uploadedFile.setFilePath(filePath); uploadedFile.setOriginName(originName); uploadedFile.setFileSize(fileSize); uploadedFile.setFileCode(0); // DB 에 데이터 등둝 result = fileMapper.insert(uploadedFile); return result; } }
Java
볡사
β€’
BoardServiceImpl.java
package com.joeun.server.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Board; import com.joeun.server.dto.Files; import com.joeun.server.mapper.BoardMapper; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class BoardServiceImpl implements BoardService { @Autowired private BoardMapper boardMapper; @Autowired private FileService fileService; @Override public List<Board> list() throws Exception { List<Board> boardList = boardMapper.list(); return boardList; } @Override public Board select(int no) throws Exception { Board board = boardMapper.select(no); return board; } @Override @Transactional public int insert(Board board) throws Exception { int result = boardMapper.insert(board); // μƒˆλ‘œ μƒμ„±λœ λ°μ΄ν„°μ˜ pk κ°€μ Έμ˜΄ int parentNo = boardMapper.maxPk(); board.setNo(parentNo); // βœ…(New) 파일 μ—…λ‘œλ“œ result += uploadFiles(board); return result; } @Override public int update(Board board) throws Exception { int result = boardMapper.update(board); // 파일 μ—…λ‘œλ“œ result += uploadFiles(board); return result; } @Override public int delete(int no) throws Exception { int result = boardMapper.delete(no); return result; } @Override public int updateViews(int count, int no) throws Exception { int result = boardMapper.updateViews(count, no); return result; } // βœ…(New) 파일 μ—…λ‘œλ“œ public int uploadFiles(Board board) throws Exception { String parentTable = "board"; int parentNo = board.getNo(); int result = 0; List<MultipartFile> fileList = board.getFiles(); if( fileList != null && !fileList.isEmpty() ) { Files fileInfo = new Files(); fileInfo.setParentTable(parentTable); fileInfo.setParentNo(parentNo); result = fileService.uploadFiles(fileInfo, fileList); } return result; } }
Java
볡사

Controller

β€’
FileController.java
package com.joeun.server.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import lombok.extern.slf4j.Slf4j; @Slf4j @Controller @RequestMapping("/file") public class FileController { }
Java
볡사

파일 μ‹œμŠ€ν…œμ— 등둝

1.
DTO
2.
Service

DTO

β€’
Files.java
package com.joeun.server.dto; import java.util.Date; import org.springframework.web.multipart.MultipartFile; import lombok.Data; @Data public class Files { private int no; private String parentTable; private int parentNo; private String fileName; private String originName; private String filePath; private long fileSize; private Date regDate; private Date updDate; private int fileCode; MultipartFile file; // βœ… μΆ”κ°€ }
Java
볡사

Service

β€’
FileServiceImpl.java
μΆ”κ°€ν•  μ½”λ“œ
// βœ…(New) νŒŒμΌμ—…λ‘œλ“œ // - μ„œλ²„ μΈ‘, 파일 μ‹œμŠ€ν…œμ— 파일 볡사 File uploadFile = new File(uploadPath, fileName); FileCopyUtils.copy(fileData, uploadFile); // 파일 μ—…λ‘œλ“œ
Java
볡사
package com.joeun.server.service; import java.io.File; import java.io.FileInputStream; import java.util.List; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.FileCopyUtils; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import com.joeun.server.mapper.FileMapper; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class FileServiceImpl implements FileService { @Autowired private FileMapper fileMapper; @Value("${upload.path}") // application.properties 에 μ„€μ •ν•œ μ—…λ‘œλ“œ 경둜 속성λͺ… private String uploadPath; // μ—…λ‘œλ“œ 경둜 @Override public List<Files> list() throws Exception { List<Files> fileList = fileMapper.list(); return fileList; } @Override public Files select(int no) throws Exception { Files file = fileMapper.select(no); return file; } @Override public int insert(Files file) throws Exception { int result = fileMapper.insert(file); return result; } @Override public int update(Files file) throws Exception { int result = fileMapper.update(file); return result; @Override public List<Files> listByParent(Files file) throws Exception { List<Files> fileList = fileMapper.listByParent(file); return fileList; } @Override public int deleteByParent(Files fileInfo) throws Exception { int result = 0; List<Files> fileList = fileMapper.listByParent(fileInfo); for (Files file : fileList) { File deleteFile = new File(file.getFilePath()); if( !deleteFile.exists() ) continue; if( !deleteFile.delete() ) continue; int deleted = fileMapper.delete(file.getNo()); if( deleted > 0 ) result++; } log.info(result + "개의 νŒŒμΌμ„ μ‚­μ œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int uploadFiles(Files fileInfo, List<MultipartFile> fileList) throws Exception { int result = 0; for (MultipartFile file : fileList) { result += uploadFile( fileInfo, file); } log.info(result + "개 νŒŒμΌμ„ μ—…λ‘œλ“œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int deleteByNoList(List<Integer> deleteFileNoList) throws Exception { int result = 0; if( deleteFileNoList != null && !deleteFileNoList.isEmpty() ) for (Integer deleteFileNo : deleteFileNoList) { if( deleteFileNo == null ) continue; fileMapper.delete(deleteFileNo); log.info(deleteFileNo + "번 파일 μ‚­μ œ"); result++; } return result; } @Override @Transactional public int upload(Files file) throws Exception { int result = uploadFile(file, file.getFile()); if( result > 0 ) result = fileMapper.maxPk(); return result; } public int uploadFile(Files fileInfo, MultipartFile file) throws Exception { int result = 0; if( file.isEmpty() ) return result; // 파일 정보 : μ›λ³ΈνŒŒμΌλͺ…, 파일 μš©λŸ‰, 파일 데이터 String originName = file.getOriginalFilename(); long fileSize = file.getSize(); byte[] fileData = file.getBytes(); // μ—…λ‘œλ“œ 경둜 // 파일λͺ… 쀑볡 방지 방법(μ •μ±…) // - λ‚ μ§œ_파일λͺ….ν™•μž₯자 // - UID_파일λͺ….ν™•μž₯자 // UID_강아지.png String fileName = UUID.randomUUID().toString() + "_" + originName; // c:/upload/UID_강아지.png String filePath = uploadPath + "/" + fileName; // βœ…(New) νŒŒμΌμ—…λ‘œλ“œ // - μ„œλ²„ μΈ‘, 파일 μ‹œμŠ€ν…œμ— 파일 볡사 File uploadFile = new File(uploadPath, fileName); FileCopyUtils.copy(fileData, uploadFile); // 파일 μ—…λ‘œλ“œ // - DB 에 파일 정보 등둝 Files uploadedFile = new Files(); uploadedFile.setParentTable(fileInfo.getParentTable()); uploadedFile.setParentNo(fileInfo.getParentNo()); uploadedFile.setFileName(fileName); uploadedFile.setFilePath(filePath); uploadedFile.setOriginName(originName); uploadedFile.setFileSize(fileSize); uploadedFile.setFileCode(0); result = fileMapper.insert(uploadedFile); return result; } }
Java
볡사

︎ 파일 쑰회

파일 λͺ©λ‘ 쑰회

β€’
FileService.java
β€’
FileServiceImpl.java
β€’
BoardServiceImpl.java
β€’
BoardController.java

FileService.java

package com.joeun.server.service; import java.util.List; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import jakarta.servlet.http.HttpServletResponse; public interface FileService { // 파일 λͺ©λ‘ public List<Files> list() throws Exception; // 파일 쑰회 public Files select(int no) throws Exception; // 파일 등둝 public int insert(Files file) throws Exception; // 파일 μˆ˜μ • public int update(Files file) throws Exception; // 파일 μ—…λ‘œλ“œ public int uploadFiles(Files file, List<MultipartFile> fileList) throws Exception; // 파일 μ—…λ‘œλ“œ public int upload(Files file) throws Exception; // βœ…(New) 파일 λͺ©λ‘ - λΆ€λͺ¨ κΈ°μ€€ public List<Files> listByParent(Files file) throws Exception; }
Java
볡사

FileServiceImpl.java

package com.joeun.server.service; import java.io.File; import java.io.FileInputStream; import java.util.List; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.FileCopyUtils; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import com.joeun.server.mapper.FileMapper; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class FileServiceImpl implements FileService { @Autowired private FileMapper fileMapper; @Value("${upload.path}") // application.properties 에 μ„€μ •ν•œ μ—…λ‘œλ“œ 경둜 속성λͺ… private String uploadPath; // μ—…λ‘œλ“œ 경둜 @Override public List<Files> list() throws Exception { List<Files> fileList = fileMapper.list(); return fileList; } @Override public Files select(int no) throws Exception { Files file = fileMapper.select(no); return file; } @Override public int insert(Files file) throws Exception { int result = fileMapper.insert(file); return result; } @Override public int update(Files file) throws Exception { int result = fileMapper.update(file); return result; } @Override public List<Files> listByParent(Files file) throws Exception { List<Files> fileList = fileMapper.listByParent(file); return fileList; } @Override public int deleteByParent(Files fileInfo) throws Exception { int result = 0; List<Files> fileList = fileMapper.listByParent(fileInfo); for (Files file : fileList) { File deleteFile = new File(file.getFilePath()); if( !deleteFile.exists() ) continue; if( !deleteFile.delete() ) continue; int deleted = fileMapper.delete(file.getNo()); if( deleted > 0 ) result++; } log.info(result + "개의 νŒŒμΌμ„ μ‚­μ œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int uploadFiles(Files fileInfo, List<MultipartFile> fileList) throws Exception { int result = 0; for (MultipartFile file : fileList) { result += uploadFile( fileInfo, file); } log.info(result + "개 νŒŒμΌμ„ μ—…λ‘œλ“œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int deleteByNoList(List<Integer> deleteFileNoList) throws Exception { int result = 0; if( deleteFileNoList != null && !deleteFileNoList.isEmpty() ) for (Integer deleteFileNo : deleteFileNoList) { if( deleteFileNo == null ) continue; fileMapper.delete(deleteFileNo); log.info(deleteFileNo + "번 파일 μ‚­μ œ"); result++; } return result; } @Override @Transactional public int upload(Files file) throws Exception { int result = uploadFile(file, file.getFile()); if( result > 0 ) result = fileMapper.maxPk(); return result; } public int uploadFile(Files fileInfo, MultipartFile file) throws Exception { int result = 0; if( file.isEmpty() ) return result; // 파일 정보 : μ›λ³ΈνŒŒμΌλͺ…, 파일 μš©λŸ‰, 파일 데이터 String originName = file.getOriginalFilename(); long fileSize = file.getSize(); byte[] fileData = file.getBytes(); // μ—…λ‘œλ“œ 경둜 // 파일λͺ… 쀑볡 방지 방법(μ •μ±…) // - λ‚ μ§œ_파일λͺ….ν™•μž₯자 // - UID_파일λͺ….ν™•μž₯자 // UID_강아지.png String fileName = UUID.randomUUID().toString() + "_" + originName; // c:/upload/UID_강아지.png String filePath = uploadPath + "/" + fileName; // - μ„œλ²„ μΈ‘, 파일 μ‹œμŠ€ν…œμ— 파일 볡사 File uploadFile = new File(uploadPath, fileName); FileCopyUtils.copy(fileData, uploadFile); // 파일 μ—…λ‘œλ“œ // - DB 에 파일 정보 등둝 Files uploadedFile = new Files(); uploadedFile.setParentTable(fileInfo.getParentTable()); uploadedFile.setParentNo(fileInfo.getParentNo()); uploadedFile.setFileName(fileName); uploadedFile.setFilePath(filePath); uploadedFile.setOriginName(originName); uploadedFile.setFileSize(fileSize); uploadedFile.setFileCode(0); result = fileMapper.insert(uploadedFile); return result; } // βœ… (New) - 파일 λͺ©λ‘ 쑰회 @Override public List<Files> listByParent(Files file) throws Exception { List<Files> fileList = fileMapper.listByParent(file); return fileList; } }
Java
볡사

BoardController.java

μΆ”κ°€ν•  μ½”λ“œ
@GetMapping("/{no}") public ResponseEntity<?> getOne(@PathVariable Integer no, Files files) { log.info("[GET] - /boards/" + no + " - κ²Œμ‹œκΈ€ 쑰회"); try { Board board = boardService.select(no); files.setParentTable("board"); files.setParentNo(no); List<Files> fileList = fileService.listByParent(files); // 파일 정보 Map<String, Object> response = new HashMap<>(); response.put("board", board); response.put("fileList", fileList); if( board == null ) { board = new Board(); board.setTitle("데이터 μ—†μŒ"); } return new ResponseEntity<>(response, HttpStatus.OK); } catch (Exception e) { log.error(null, e); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } }
Java
볡사
package com.joeun.server.controller; import java.util.HashMap; import java.util.List; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Board; import com.joeun.server.dto.Files; import com.joeun.server.service.BoardService; import com.joeun.server.service.FileService; import lombok.extern.slf4j.Slf4j; @Slf4j @RestController @RequestMapping("/boards") public class BoardController { @Autowired private BoardService boardService; @Autowired private FileService fileService; // πŸ‘©β€πŸ’» CRUD λ©”μ†Œλ“œ μžλ™ 생성 : sp-crud // πŸ‘©β€πŸ’» μžλ™ import : alt + shift + O // πŸ‘©β€πŸ’» ν•œ 쀄 μ‚­μ œ : ctrl + shift + K @GetMapping() public ResponseEntity<?> getAll() { log.info("[GET] - /boards - κ²Œμ‹œκΈ€ λͺ©λ‘"); try { List<Board> boardList = boardService.list(); if( boardList == null ) log.info("쑰회된 κ²Œμ‹œκΈ€ μ—†μŒ"); else log.info("κ²Œμ‹œκΈ€ 수 : " + boardList.size()); return new ResponseEntity<>(boardList, HttpStatus.OK); } catch (Exception e) { log.error(null, e); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } } @GetMapping("/{no}") public ResponseEntity<?> getOne(@PathVariable Integer no, Files files) { log.info("[GET] - /boards/" + no + " - κ²Œμ‹œκΈ€ 쑰회"); try { Board board = boardService.select(no); files.setParentTable("board"); files.setParentNo(no); List<Files> fileList = fileService.listByParent(files); // 파일 정보 Map<String, Object> response = new HashMap<>(); response.put("board", board); response.put("fileList", fileList); if( board == null ) { board = new Board(); board.setTitle("데이터 μ—†μŒ"); } return new ResponseEntity<>(response, HttpStatus.OK); } catch (Exception e) { log.error(null, e); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } } @PostMapping() // public ResponseEntity<?> create(@RequestBody Board board) { // Content-Type : application/json public ResponseEntity<?> create(Board board) { // Content-Type : multipart/form-data log.info("[POST] - /boards - κ²Œμ‹œκΈ€ 등둝"); log.info("board : " + board.toString()); List<MultipartFile> files = board.getFiles(); if( files != null ) for (MultipartFile file : files) { log.info("file : " + file.getOriginalFilename()); } try { int result = boardService.insert(board); if( result > 0 ) return new ResponseEntity<>("κ²Œμ‹œκΈ€ 등둝 μ™„λ£Œ", HttpStatus.CREATED); // 201 else return new ResponseEntity<>("κ²Œμ‹œκΈ€ 등둝 μ‹€νŒ¨", HttpStatus.OK); } catch (Exception e) { log.error(null, e); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } } @PutMapping() // public ResponseEntity<?> update(@RequestBody Board board) { // Content-Type : application/json public ResponseEntity<?> update(Board board) { // Content-Type : multipart/form-data log.info("[PUT] - /boards - κ²Œμ‹œκΈ€ μˆ˜μ •"); try { int result = boardService.update(board); log.info("μˆ˜μ • : " + board); if( result > 0 ) return new ResponseEntity<>("κ²Œμ‹œκΈ€ μˆ˜μ • μ™„λ£Œ", HttpStatus.OK); else return new ResponseEntity<>("κ²Œμ‹œκΈ€ μˆ˜μ • μ‹€νŒ¨", HttpStatus.OK); } catch (Exception e) { log.error(null, e); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } } @DeleteMapping("/{no}") public ResponseEntity<?> destroy(@PathVariable Integer no) { log.info("[DELETE] - /boards/" + no + " - κ²Œμ‹œκΈ€ μ‚­μ œ"); try { int result = boardService.delete(no); if( result > 0 ) return new ResponseEntity<>("κ²Œμ‹œκΈ€ μ‚­μ œ μ™„λ£Œ", HttpStatus.OK); else return new ResponseEntity<>("κ²Œμ‹œκΈ€ μ‚­μ œ μ‹€νŒ¨", HttpStatus.OK); } catch (Exception e) { log.error(null, e); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } } }
Java
볡사

λ‹€μš΄λ‘œλ“œ

β€’
FileService.java
β€’
FileServiceImpl.java
β€’
FileController.java

FileService.java

package com.joeun.server.service; import java.util.List; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import jakarta.servlet.http.HttpServletResponse; public interface FileService { // 파일 λͺ©λ‘ public List<Files> list() throws Exception; // 파일 쑰회 public Files select(int no) throws Exception; // 파일 등둝 public int insert(Files file) throws Exception; // 파일 μˆ˜μ • public int update(Files file) throws Exception; // 파일 μ—…λ‘œλ“œ public int uploadFiles(Files file, List<MultipartFile> fileList) throws Exception; // 파일 μ—…λ‘œλ“œ public int upload(Files file) throws Exception; // 파일 λͺ©λ‘ - λΆ€λͺ¨ κΈ°μ€€ public List<Files> listByParent(Files file) throws Exception; // βœ…(New) - 파일 λ‹€μš΄λ‘œλ“œ public int download(int no, HttpServletResponse response) throws Exception; }
Java
볡사

FileServiceImpl.java

package com.joeun.server.service; import java.io.File; import java.io.FileInputStream; import java.util.List; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.FileCopyUtils; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import com.joeun.server.mapper.FileMapper; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class FileServiceImpl implements FileService { @Autowired private FileMapper fileMapper; @Value("${upload.path}") // application.properties 에 μ„€μ •ν•œ μ—…λ‘œλ“œ 경둜 속성λͺ… private String uploadPath; // μ—…λ‘œλ“œ 경둜 @Override public List<Files> list() throws Exception { List<Files> fileList = fileMapper.list(); return fileList; } @Override public Files select(int no) throws Exception { Files file = fileMapper.select(no); return file; } @Override public int insert(Files file) throws Exception { int result = fileMapper.insert(file); return result; } @Override public int update(Files file) throws Exception { int result = fileMapper.update(file); return result; } @Override public List<Files> listByParent(Files file) throws Exception { List<Files> fileList = fileMapper.listByParent(file); return fileList; } @Override public int deleteByParent(Files fileInfo) throws Exception { int result = 0; List<Files> fileList = fileMapper.listByParent(fileInfo); for (Files file : fileList) { File deleteFile = new File(file.getFilePath()); if( !deleteFile.exists() ) continue; if( !deleteFile.delete() ) continue; int deleted = fileMapper.delete(file.getNo()); if( deleted > 0 ) result++; } log.info(result + "개의 νŒŒμΌμ„ μ‚­μ œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int uploadFiles(Files fileInfo, List<MultipartFile> fileList) throws Exception { int result = 0; for (MultipartFile file : fileList) { result += uploadFile( fileInfo, file); } log.info(result + "개 νŒŒμΌμ„ μ—…λ‘œλ“œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int deleteByNoList(List<Integer> deleteFileNoList) throws Exception { int result = 0; if( deleteFileNoList != null && !deleteFileNoList.isEmpty() ) for (Integer deleteFileNo : deleteFileNoList) { if( deleteFileNo == null ) continue; fileMapper.delete(deleteFileNo); log.info(deleteFileNo + "번 파일 μ‚­μ œ"); result++; } return result; } @Override @Transactional public int upload(Files file) throws Exception { int result = uploadFile(file, file.getFile()); if( result > 0 ) result = fileMapper.maxPk(); return result; } public int uploadFile(Files fileInfo, MultipartFile file) throws Exception { int result = 0; if( file.isEmpty() ) return result; // 파일 정보 : μ›λ³ΈνŒŒμΌλͺ…, 파일 μš©λŸ‰, 파일 데이터 String originName = file.getOriginalFilename(); long fileSize = file.getSize(); byte[] fileData = file.getBytes(); // μ—…λ‘œλ“œ 경둜 // 파일λͺ… 쀑볡 방지 방법(μ •μ±…) // - λ‚ μ§œ_파일λͺ….ν™•μž₯자 // - UID_파일λͺ….ν™•μž₯자 // UID_강아지.png String fileName = UUID.randomUUID().toString() + "_" + originName; // c:/upload/UID_강아지.png String filePath = uploadPath + "/" + fileName; // - μ„œλ²„ μΈ‘, 파일 μ‹œμŠ€ν…œμ— 파일 볡사 File uploadFile = new File(uploadPath, fileName); FileCopyUtils.copy(fileData, uploadFile); // 파일 μ—…λ‘œλ“œ // - DB 에 파일 정보 등둝 Files uploadedFile = new Files(); uploadedFile.setParentTable(fileInfo.getParentTable()); uploadedFile.setParentNo(fileInfo.getParentNo()); uploadedFile.setFileName(fileName); uploadedFile.setFilePath(filePath); uploadedFile.setOriginName(originName); uploadedFile.setFileSize(fileSize); uploadedFile.setFileCode(0); result = fileMapper.insert(uploadedFile); return result; } // 파일 λͺ©λ‘ 쑰회 @Override public List<Files> listByParent(Files file) throws Exception { List<Files> fileList = fileMapper.listByParent(file); return fileList; } @Override public int download(int fileNo, HttpServletResponse response) throws Exception { // result // 0 : 파일 λ‹€μš΄λ‘œλ“œ 처리 μ‹€νŒ¨ // 1 : 파일 λ‹€μš΄λ‘œλ“œ 성곡 Files file = fileMapper.select(fileNo); if( file == null ) { // BAD_REQUEST : 400, ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ΄ 잘λͺ»λ˜μ—ˆμŒμ„ μ•Œλ €μ£ΌλŠ” μƒνƒœμ½”λ“œ // response.setStatus(response.SC_BAD_REQUEST); return 0; } String filePath = file.getFilePath(); // 파일 경둜 String fileName = file.getFileName(); // 파일 이름 // λ‹€μš΄λ‘œλ“œ 응닡을 μœ„ν•œ 헀더 μ„ΈνŒ… // - ContentType : application/octet-stream // - Content-Disposition : attachment, filename="파일λͺ….ν™•μž₯자" response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // 파일 λ‹€μš΄λ‘œλ“œ // - 파일 μž…λ ₯ File downloadFile = new File(filePath); FileInputStream fis = new FileInputStream(downloadFile); // - 파일 좜λ ₯ ServletOutputStream sos = response.getOutputStream(); // λ‹€μš΄λ‘œλ“œ FileCopyUtils.copy(fis, sos); fis.close(); sos.close(); return 1; } }
Java
볡사

FileController.java

package com.joeun.server.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import lombok.extern.slf4j.Slf4j; @Slf4j @Controller @RequestMapping("/file") public class FileController { @Autowired private FileService fileService; /** * 파일 λ‹€μš΄λ‘œλ“œ * @param fileNo * @param response * @throws Exception */ @GetMapping("/{no}") public void fileDownload(@PathVariable("no") int no ,HttpServletResponse response) throws Exception { // 파일 쑰회 Files file = fileService.select(no); // 파일이 μ‘΄μž¬ν•˜μ§€ μ•ŠμœΌλ©΄, if( file == null ) { // 응닡 μƒνƒœμ½”λ“œ : 400, ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ΄ 잘λͺ»λ˜μ—ˆμŒμ„ λ‚˜νƒ€λ‚΄λŠ” μƒνƒœμ½”λ“œ response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } String fileName = file.getFileName(); // 파일 λͺ… String filePath = file.getFilePath(); // 파일 경둜 // 파일 λ‹€μš΄λ‘œλ“œλ₯Ό μœ„ν•œ 헀더 μ„ΈνŒ… // - ContentType : application/octet-straem // - Content-Disposition : attachment; fileanme="파일λͺ….ν™•μž₯자" response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // 파일 μž…λ ₯ File downloadFile = new File(filePath); FileInputStream fis = new FileInputStream(downloadFile); ServletOutputStream sos = response.getOutputStream(); // λ‹€μš΄λ‘œλ“œ FileCopyUtils.copy(fis, sos); } }
Java
볡사

썸넀일 이미지 보기

β€’
FileController.java

FileController.java

package com.joeun.server.controller; import java.io.File; import java.io.FileInputStream; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.util.FileCopyUtils; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import com.joeun.server.dto.Files; import com.joeun.server.service.FileService; import com.joeun.server.utils.MediaUtil; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @Slf4j @Controller @RequestMapping("/file") public class FileController { @Autowired private FileService fileService; @Autowired private ResourceLoader resourceLoader; /** * 파일 λ‹€μš΄λ‘œλ“œ * @param fileNo * @param response * @throws Exception */ @GetMapping("/{no}") public void fileDownload(@PathVariable("no") int no ,HttpServletResponse response) throws Exception { // 파일 쑰회 Files file = fileService.select(no); // 파일이 μ‘΄μž¬ν•˜μ§€ μ•ŠμœΌλ©΄, if( file == null ) { // 응닡 μƒνƒœμ½”λ“œ : 400, ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ΄ 잘λͺ»λ˜μ—ˆμŒμ„ λ‚˜νƒ€λ‚΄λŠ” μƒνƒœμ½”λ“œ response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } String fileName = file.getFileName(); // 파일 λͺ… String filePath = file.getFilePath(); // 파일 경둜 // 파일 λ‹€μš΄λ‘œλ“œλ₯Ό μœ„ν•œ 헀더 μ„ΈνŒ… // - ContentType : application/octet-straem // - Content-Disposition : attachment; fileanme="파일λͺ….ν™•μž₯자" response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // 파일 μž…λ ₯ File downloadFile = new File(filePath); FileInputStream fis = new FileInputStream(downloadFile); ServletOutputStream sos = response.getOutputStream(); // λ‹€μš΄λ‘œλ“œ FileCopyUtils.copy(fis, sos); } /** * 이미지 썸넀일 * @param no * @param response * @throws Exception */ @GetMapping("/img/{no}") public void showImg(@PathVariable Integer no, HttpServletResponse response) throws Exception { Files file = fileService.select(no); String filePath = (file != null) ? file.getFilePath() : null; Resource resource = resourceLoader.getResource("classpath:static/img/no-image.png"); File imgFile; if (filePath == null || !(imgFile = new File(filePath)).exists()) { // 파일이 μ‘΄μž¬ν•˜μ§€ μ•Šκ±°λ‚˜ 파일 κ²½λ‘œκ°€ null인 경우 imgFile = resource.getFile(); } String ext = filePath.substring(filePath.lastIndexOf(".") + 1); MediaType mType = MediaUtil.getMediaType(ext); if (mType == null) { // 이미지 νƒ€μž…μ΄ 아닐 경우 response.setContentType(MediaType.IMAGE_PNG_VALUE); // 기본적으둜 PNG둜 μ„€μ • imgFile = resource.getFile(); } else { // 이미지 νƒ€μž…μΌ 경우 response.setContentType(mType.toString()); } FileInputStream fis = new FileInputStream(imgFile); ServletOutputStream sos = response.getOutputStream(); FileCopyUtils.copy(fis, sos); } }
Java
볡사

파일 μ‚­μ œ

κ²Œμ‹œκΈ€μ— μ’…μ†λœ μ—¬λŸ¬ 파일 μ‚­μ œ

β€’
FileService.java
β€’
FileServiceImpl.java
β€’
BoardServiceImpl.java
β€’
FileService.java
package com.joeun.server.service; import java.util.List; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import jakarta.servlet.http.HttpServletResponse; public interface FileService { // 파일 λͺ©λ‘ public List<Files> list() throws Exception; // 파일 쑰회 public Files select(int no) throws Exception; // 파일 등둝 public int insert(Files file) throwsdeleteByParent Exception; // 파일 μˆ˜μ • public int update(Files file) throws Exception; // 파일 μ‚­μ œ public int delete(int no) throws Exception; // 파일 μ—…λ‘œλ“œ public int uploadFiles(Files file, List<MultipartFile> fileList) throws Exception; // 파일 μ—…λ‘œλ“œ public int upload(Files file) throws Exception; // 파일 λͺ©λ‘ - λΆ€λͺ¨ κΈ°μ€€ public List<Files> listByParent(Files file) throws Exception; // 파일 λ‹€μš΄λ‘œλ“œ public int download(int no, HttpServletResponse response) throws Exception; // βœ…(New) 파일 μ‚­μ œ - λΆ€λͺ¨ κΈ°μ€€ public int deleteByParent(Files fileInfo) throws Exception; }
Java
볡사
β€’
FileServiceImpl.java
μΆ”κ°€ν•  μ½”λ“œ
@Override public int deleteByParent(Files fileInfo) throws Exception { int result = 0; List<Files> fileList = fileMapper.listByParent(fileInfo); for (Files file : fileList) { File deleteFile = new File(file.getFilePath()); if( !deleteFile.exists() ) continue; if( !deleteFile.delete() ) continue; int deleted = fileMapper.delete(file.getNo()); if( deleted > 0 ) result++; } log.info(result + "개의 νŒŒμΌμ„ μ‚­μ œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; }
Java
볡사
package com.joeun.server.service; import java.io.File; import java.io.FileInputStream; import java.util.List; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.FileCopyUtils; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import com.joeun.server.mapper.FileMapper; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class FileServiceImpl implements FileService { @Autowired private FileMapper fileMapper; @Value("${upload.path}") // application.properties 에 μ„€μ •ν•œ μ—…λ‘œλ“œ 경둜 속성λͺ… private String uploadPath; // μ—…λ‘œλ“œ 경둜 @Override public List<Files> list() throws Exception { List<Files> fileList = fileMapper.list(); return fileList; } @Override public Files select(int no) throws Exception { Files file = fileMapper.select(no); return file; } @Override public int insert(Files file) throws Exception { int result = fileMapper.insert(file); return result; } @Override public int update(Files file) throws Exception { int result = fileMapper.update(file); return result; } @Override public List<Files> listByParent(Files file) throws Exception { List<Files> fileList = fileMapper.listByParent(file); return fileList; } @Override public int deleteByParent(Files fileInfo) throws Exception { int result = 0; List<Files> fileList = fileMapper.listByParent(fileInfo); for (Files file : fileList) { File deleteFile = new File(file.getFilePath()); if( !deleteFile.exists() ) continue; if( !deleteFile.delete() ) continue; int deleted = fileMapper.delete(file.getNo()); if( deleted > 0 ) result++; } log.info(result + "개의 νŒŒμΌμ„ μ‚­μ œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int uploadFiles(Files fileInfo, List<MultipartFile> fileList) throws Exception { int result = 0; for (MultipartFile file : fileList) { result += uploadFile( fileInfo, file); } log.info(result + "개 νŒŒμΌμ„ μ—…λ‘œλ“œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int deleteByNoList(List<Integer> deleteFileNoList) throws Exception { int result = 0; if( deleteFileNoList != null && !deleteFileNoList.isEmpty() ) for (Integer deleteFileNo : deleteFileNoList) { if( deleteFileNo == null ) continue; fileMapper.delete(deleteFileNo); log.info(deleteFileNo + "번 파일 μ‚­μ œ"); result++; } return result; } @Override @Transactional public int upload(Files file) throws Exception { int result = uploadFile(file, file.getFile()); if( result > 0 ) result = fileMapper.maxPk(); return result; } public int uploadFile(Files fileInfo, MultipartFile file) throws Exception { int result = 0; if( file.isEmpty() ) return result; // 파일 정보 : μ›λ³ΈνŒŒμΌλͺ…, 파일 μš©λŸ‰, 파일 데이터 String originName = file.getOriginalFilename(); long fileSize = file.getSize(); byte[] fileData = file.getBytes(); // μ—…λ‘œλ“œ 경둜 // 파일λͺ… 쀑볡 방지 방법(μ •μ±…) // - λ‚ μ§œ_파일λͺ….ν™•μž₯자 // - UID_파일λͺ….ν™•μž₯자 // UID_강아지.png String fileName = UUID.randomUUID().toString() + "_" + originName; // c:/upload/UID_강아지.png String filePath = uploadPath + "/" + fileName; // - μ„œλ²„ μΈ‘, 파일 μ‹œμŠ€ν…œμ— 파일 볡사 File uploadFile = new File(uploadPath, fileName); FileCopyUtils.copy(fileData, uploadFile); // 파일 μ—…λ‘œλ“œ // - DB 에 파일 정보 등둝 Files uploadedFile = new Files(); uploadedFile.setParentTable(fileInfo.getParentTable()); uploadedFile.setParentNo(fileInfo.getParentNo()); uploadedFile.setFileName(fileName); uploadedFile.setFilePath(filePath); uploadedFile.setOriginName(originName); uploadedFile.setFileSize(fileSize); uploadedFile.setFileCode(0); result = fileMapper.insert(uploadedFile); return result; } // 파일 λͺ©λ‘ 쑰회 @Override public List<Files> listByParent(Files file) throws Exception { List<Files> fileList = fileMapper.listByParent(file); return fileList; } @Override public int download(int fileNo, HttpServletResponse response) throws Exception { // result // 0 : 파일 λ‹€μš΄λ‘œλ“œ 처리 μ‹€νŒ¨ // 1 : 파일 λ‹€μš΄λ‘œλ“œ 성곡 Files file = fileMapper.select(fileNo); if( file == null ) { // BAD_REQUEST : 400, ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ΄ 잘λͺ»λ˜μ—ˆμŒμ„ μ•Œλ €μ£ΌλŠ” μƒνƒœμ½”λ“œ // response.setStatus(response.SC_BAD_REQUEST); return 0; } String filePath = file.getFilePath(); // 파일 경둜 String fileName = file.getFileName(); // 파일 이름 // λ‹€μš΄λ‘œλ“œ 응닡을 μœ„ν•œ 헀더 μ„ΈνŒ… // - ContentType : application/octet-stream // - Content-Disposition : attachment, filename="파일λͺ….ν™•μž₯자" response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // 파일 λ‹€μš΄λ‘œλ“œ // - 파일 μž…λ ₯ File downloadFile = new File(filePath); FileInputStream fis = new FileInputStream(downloadFile); // - 파일 좜λ ₯ ServletOutputStream sos = response.getOutputStream(); // λ‹€μš΄λ‘œλ“œ FileCopyUtils.copy(fis, sos); fis.close(); sos.close(); return 1; } @Override public int deleteByParent(Files fileInfo) throws Exception { int result = 0; List<Files> fileList = fileMapper.listByParent(fileInfo); for (Files file : fileList) { File deleteFile = new File(file.getFilePath()); if( !deleteFile.exists() ) continue; if( !deleteFile.delete() ) continue; int deleted = fileMapper.delete(file.getNo()); if( deleted > 0 ) result++; } log.info(result + "개의 νŒŒμΌμ„ μ‚­μ œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } }
Java
볡사
β€’
BoardServiceImpl.java
μΆ”κ°€ν•  μ½”λ“œ
@Override @Transactional public int delete(int no) throws Exception { int result = boardMapper.delete(no); String parentTable = "board"; int parentNo = no; if( result > 0 ) { Files fileInfo = new Files(); fileInfo.setParentTable(parentTable); fileInfo.setParentNo(parentNo); int fileResult = fileService.deleteByParent(fileInfo); result = fileResult; } return result; }
Java
볡사
package com.joeun.server.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Board; import com.joeun.server.dto.Files; import com.joeun.server.mapper.BoardMapper; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class BoardServiceImpl implements BoardService { @Autowired private BoardMapper boardMapper; @Autowired private FileService fileService; @Override public List<Board> list() throws Exception { List<Board> boardList = boardMapper.list(); return boardList; } @Override public Board select(int no) throws Exception { Board board = boardMapper.select(no); return board; } @Override @Transactional public int insert(Board board) throws Exception { int result = boardMapper.insert(board); // μƒˆλ‘œ μƒμ„±λœ λ°μ΄ν„°μ˜ pk κ°€μ Έμ˜΄ int parentNo = boardMapper.maxPk(); board.setNo(parentNo); result += uploadFiles(board); return result; } @Override public int update(Board board) throws Exception { int result = boardMapper.update(board); // 파일 μ—…λ‘œλ“œ result += uploadFiles(board); return result; } @Override public int updateViews(int count, int no) throws Exception { int result = boardMapper.updateViews(count, no); return result; } public int uploadFiles(Board board) throws Exception { String parentTable = "board"; int parentNo = board.getNo(); int result = 0; List<MultipartFile> fileList = board.getFiles(); if( fileList != null && !fileList.isEmpty() ) { Files fileInfo = new Files(); fileInfo.setParentTable(parentTable); fileInfo.setParentNo(parentNo); result = fileService.uploadFiles(fileInfo, fileList); } return result; } @Override @Transactional public int delete(int no) throws Exception { int result = boardMapper.delete(no); String parentTable = "board"; int parentNo = no; // βœ…(New) - κ²Œμ‹œκΈ€μ— μ’…μ†λœ 파일 μ‚­μ œ if( result > 0 ) { Files fileInfo = new Files(); fileInfo.setParentTable(parentTable); fileInfo.setParentNo(parentNo); int fileResult = fileService.deleteByParent(fileInfo); result = fileResult; } return result; } }
Java
볡사

선택 파일 μ‚­μ œ

β€’
FileService.java
β€’
FileServiceImpl.java
β€’
BoardServiceImpl.java
β€’
FileService.java
package com.joeun.server.service; import java.util.List; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import jakarta.servlet.http.HttpServletResponse; public interface FileService { // 파일 λͺ©λ‘ public List<Files> list() throws Exception; // 파일 쑰회 public Files select(int no) throws Exception; // 파일 등둝 public int insert(Files file) throws Exception; // 파일 μˆ˜μ • public int update(Files file) throws Exception; // 파일 μ‚­μ œ public int delete(int no) throws Exception; // 파일 μ—…λ‘œλ“œ public int uploadFiles(Files file, List<MultipartFile> fileList) throws Exception; // 파일 μ—…λ‘œλ“œ public int upload(Files file) throws Exception; // 파일 λͺ©λ‘ - λΆ€λͺ¨ κΈ°μ€€ public List<Files> listByParent(Files file) throws Exception; // 파일 λ‹€μš΄λ‘œλ“œ public int download(int no, HttpServletResponse response) throws Exception; // 파일 μ‚­μ œ - λΆ€λͺ¨ κΈ°μ€€ public int deleteByParent(Files fileInfo) throws Exception; // 파일 μ‚­μ œ - noList κΈ°μ€€ public int deleteByNoList(List<Integer> noList) throws Exception; }
Java
볡사
β€’
FileServiceImpl.java
μΆ”κ°€ν•  μ½”λ“œ
@Override public int deleteByNoList(List<Integer> deleteFileNoList) throws Exception { int result = 0; if( deleteFileNoList != null && !deleteFileNoList.isEmpty() ) for (Integer deleteFileNo : deleteFileNoList) { if( deleteFileNo == null ) continue; fileMapper.delete(deleteFileNo); log.info(deleteFileNo + "번 파일 μ‚­μ œ"); result++; } return result; }
Java
볡사
package com.joeun.server.service; import java.io.File; import java.io.FileInputStream; import java.util.List; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.FileCopyUtils; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import com.joeun.server.mapper.FileMapper; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class FileServiceImpl implements FileService { @Autowired private FileMapper fileMapper; @Value("${upload.path}") // application.properties 에 μ„€μ •ν•œ μ—…λ‘œλ“œ 경둜 속성λͺ… private String uploadPath; // μ—…λ‘œλ“œ 경둜 @Override public List<Files> list() throws Exception { List<Files> fileList = fileMapper.list(); return fileList; } @Override public Files select(int no) throws Exception { Files file = fileMapper.select(no); return file; } @Override public int insert(Files file) throws Exception { int result = fileMapper.insert(file); return result; } @Override public int update(Files file) throws Exception { int result = fileMapper.update(file); return result; } @Override public List<Files> listByParent(Files file) throws Exception { List<Files> fileList = fileMapper.listByParent(file); return fileList; } @Override public int deleteByParent(Files fileInfo) throws Exception { int result = 0; List<Files> fileList = fileMapper.listByParent(fileInfo); for (Files file : fileList) { File deleteFile = new File(file.getFilePath()); if( !deleteFile.exists() ) continue; if( !deleteFile.delete() ) continue; int deleted = fileMapper.delete(file.getNo()); if( deleted > 0 ) result++; } log.info(result + "개의 νŒŒμΌμ„ μ‚­μ œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int uploadFiles(Files fileInfo, List<MultipartFile> fileList) throws Exception { int result = 0; for (MultipartFile file : fileList) { result += uploadFile( fileInfo, file); } log.info(result + "개 νŒŒμΌμ„ μ—…λ‘œλ“œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int deleteByNoList(List<Integer> deleteFileNoList) throws Exception { int result = 0; if( deleteFileNoList != null && !deleteFileNoList.isEmpty() ) for (Integer deleteFileNo : deleteFileNoList) { if( deleteFileNo == null ) continue; fileMapper.delete(deleteFileNo); log.info(deleteFileNo + "번 파일 μ‚­μ œ"); result++; } return result; } @Override @Transactional public int upload(Files file) throws Exception { int result = uploadFile(file, file.getFile()); if( result > 0 ) result = fileMapper.maxPk(); return result; } public int uploadFile(Files fileInfo, MultipartFile file) throws Exception { int result = 0; if( file.isEmpty() ) return result; // 파일 정보 : μ›λ³ΈνŒŒμΌλͺ…, 파일 μš©λŸ‰, 파일 데이터 String originName = file.getOriginalFilename(); long fileSize = file.getSize(); byte[] fileData = file.getBytes(); // μ—…λ‘œλ“œ 경둜 // 파일λͺ… 쀑볡 방지 방법(μ •μ±…) // - λ‚ μ§œ_파일λͺ….ν™•μž₯자 // - UID_파일λͺ….ν™•μž₯자 // UID_강아지.png String fileName = UUID.randomUUID().toString() + "_" + originName; // c:/upload/UID_강아지.png String filePath = uploadPath + "/" + fileName; // - μ„œλ²„ μΈ‘, 파일 μ‹œμŠ€ν…œμ— 파일 볡사 File uploadFile = new File(uploadPath, fileName); FileCopyUtils.copy(fileData, uploadFile); // 파일 μ—…λ‘œλ“œ // - DB 에 파일 정보 등둝 Files uploadedFile = new Files(); uploadedFile.setParentTable(fileInfo.getParentTable()); uploadedFile.setParentNo(fileInfo.getParentNo()); uploadedFile.setFileName(fileName); uploadedFile.setFilePath(filePath); uploadedFile.setOriginName(originName); uploadedFile.setFileSize(fileSize); uploadedFile.setFileCode(0); result = fileMapper.insert(uploadedFile); return result; } // 파일 λͺ©λ‘ 쑰회 @Override public List<Files> listByParent(Files file) throws Exception { List<Files> fileList = fileMapper.listByParent(file); return fileList; } @Override public int download(int fileNo, HttpServletResponse response) throws Exception { // result // 0 : 파일 λ‹€μš΄λ‘œλ“œ 처리 μ‹€νŒ¨ // 1 : 파일 λ‹€μš΄λ‘œλ“œ 성곡 Files file = fileMapper.select(fileNo); if( file == null ) { // BAD_REQUEST : 400, ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ΄ 잘λͺ»λ˜μ—ˆμŒμ„ μ•Œλ €μ£ΌλŠ” μƒνƒœμ½”λ“œ // response.setStatus(response.SC_BAD_REQUEST); return 0; } String filePath = file.getFilePath(); // 파일 경둜 String fileName = file.getFileName(); // 파일 이름 // λ‹€μš΄λ‘œλ“œ 응닡을 μœ„ν•œ 헀더 μ„ΈνŒ… // - ContentType : application/octet-stream // - Content-Disposition : attachment, filename="파일λͺ….ν™•μž₯자" response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // 파일 λ‹€μš΄λ‘œλ“œ // - 파일 μž…λ ₯ File downloadFile = new File(filePath); FileInputStream fis = new FileInputStream(downloadFile); // - 파일 좜λ ₯ ServletOutputStream sos = response.getOutputStream(); // λ‹€μš΄λ‘œλ“œ FileCopyUtils.copy(fis, sos); fis.close(); sos.close(); return 1; } @Override public int deleteByParent(Files fileInfo) throws Exception { int result = 0; List<Files> fileList = fileMapper.listByParent(fileInfo); for (Files file : fileList) { File deleteFile = new File(file.getFilePath()); if( !deleteFile.exists() ) continue; if( !deleteFile.delete() ) continue; int deleted = fileMapper.delete(file.getNo()); if( deleted > 0 ) result++; } log.info(result + "개의 νŒŒμΌμ„ μ‚­μ œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int deleteByNoList(List<Integer> deleteFileNoList) throws Exception { int result = 0; if( deleteFileNoList != null && !deleteFileNoList.isEmpty() ) for (Integer deleteFileNo : deleteFileNoList) { if( deleteFileNo == null ) continue; fileMapper.delete(deleteFileNo); log.info(deleteFileNo + "번 파일 μ‚­μ œ"); result++; } return result; } }
Java
볡사
β€’
BoardServiceImpl.java
μΆ”κ°€ν•  μ½”λ“œ
@Override public int update(Board board) throws Exception { int result = boardMapper.update(board); // 파일 μ—…λ‘œλ“œ result += uploadFiles(board); // κ°œλ³„ 파일 μ‚­μ œ List<Integer> deleteFileNoList = board.getDeleteFileNoList(); result += fileService.deleteByNoList(deleteFileNoList); return result; }
Java
볡사
package com.joeun.server.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Board; import com.joeun.server.dto.Files; import com.joeun.server.mapper.BoardMapper; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class BoardServiceImpl implements BoardService { @Autowired private BoardMapper boardMapper; @Autowired private FileService fileService; @Override public List<Board> list() throws Exception { List<Board> boardList = boardMapper.list(); return boardList; } @Override public Board select(int no) throws Exception { Board board = boardMapper.select(no); return board; } @Override @Transactional public int insert(Board board) throws Exception { int result = boardMapper.insert(board); // μƒˆλ‘œ μƒμ„±λœ λ°μ΄ν„°μ˜ pk κ°€μ Έμ˜΄ int parentNo = boardMapper.maxPk(); board.setNo(parentNo); result += uploadFiles(board); return result; } @Override public int update(Board board) throws Exception { int result = boardMapper.update(board); // 파일 μ—…λ‘œλ“œ result += uploadFiles(board); // βœ…(New) κ°œλ³„ 파일 μ‚­μ œ List<Integer> deleteFileNoList = board.getDeleteFileNoList(); result += fileService.deleteByNoList(deleteFileNoList); return result; } @Override public int updateViews(int count, int no) throws Exception { int result = boardMapper.updateViews(count, no); return result; } public int uploadFiles(Board board) throws Exception { String parentTable = "board"; int parentNo = board.getNo(); int result = 0; List<MultipartFile> fileList = board.getFiles(); if( fileList != null && !fileList.isEmpty() ) { Files fileInfo = new Files(); fileInfo.setParentTable(parentTable); fileInfo.setParentNo(parentNo); result = fileService.uploadFiles(fileInfo, fileList); } return result; } @Override @Transactional public int delete(int no) throws Exception { int result = boardMapper.delete(no); String parentTable = "board"; int parentNo = no; if( result > 0 ) { Files fileInfo = new Files(); fileInfo.setParentTable(parentTable); fileInfo.setParentNo(parentNo); int fileResult = fileService.deleteByParent(fileInfo); result = fileResult; } return result; } }
Java
볡사

κ°œλ³„ 파일 μ‚­μ œ

β€’
FileService.java
β€’
FileServiceImpl.java
β€’
FileController.java

FileService.java

// 파일 μ‚­μ œ public int delete(int no) throws Exception;
Java
볡사
package com.joeun.server.service; import java.util.List; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import jakarta.servlet.http.HttpServletResponse; public interface FileService { // 파일 λͺ©λ‘ public List<Files> list() throws Exception; // 파일 쑰회 public Files select(int no) throws Exception; // 파일 등둝 public int insert(Files file) throws Exception; // 파일 μˆ˜μ • public int update(Files file) throws Exception; // 파일 μ‚­μ œ public int delete(int no) throws Exception; // 파일 μ—…λ‘œλ“œ public int uploadFiles(Files file, List<MultipartFile> fileList) throws Exception; // 파일 μ—…λ‘œλ“œ public int upload(Files file) throws Exception; // 파일 λͺ©λ‘ - λΆ€λͺ¨ κΈ°μ€€ public List<Files> listByParent(Files file) throws Exception; // 파일 λ‹€μš΄λ‘œλ“œ public int download(int no, HttpServletResponse response) throws Exception; // 파일 μ‚­μ œ - λΆ€λͺ¨ κΈ°μ€€ public int deleteByParent(Files fileInfo) throws Exception; // 파일 μ‚­μ œ - noList κΈ°μ€€ public int deleteByNoList(List<Integer> noList) throws Exception; }
Java
볡사

FileServiceImpl.java

@Override public int delete(int no) throws Exception { Files file = fileMapper.select(no); if( file == null ) return 0; String filePath = file.getFilePath(); File deleteFile = new File(filePath); if( !deleteFile.exists() ) return 0; boolean deleted = deleteFile.delete(); int result = 0; if( deleted ) { result = fileMapper.delete(no); } return result; }
Java
볡사
package com.joeun.server.service; import java.io.File; import java.io.FileInputStream; import java.util.List; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.FileCopyUtils; import org.springframework.web.multipart.MultipartFile; import com.joeun.server.dto.Files; import com.joeun.server.mapper.FileMapper; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @Slf4j @Service public class FileServiceImpl implements FileService { @Autowired private FileMapper fileMapper; @Value("${upload.path}") // application.properties 에 μ„€μ •ν•œ μ—…λ‘œλ“œ 경둜 속성λͺ… private String uploadPath; // μ—…λ‘œλ“œ 경둜 @Override public List<Files> list() throws Exception { List<Files> fileList = fileMapper.list(); return fileList; } @Override public Files select(int no) throws Exception { Files file = fileMapper.select(no); return file; } @Override public int insert(Files file) throws Exception { int result = fileMapper.insert(file); return result; } @Override public int update(Files file) throws Exception { int result = fileMapper.update(file); return result; } @Override public int delete(int no) throws Exception { Files file = fileMapper.select(no); if( file == null ) return 0; String filePath = file.getFilePath(); File deleteFile = new File(filePath); if( !deleteFile.exists() ) return 0; boolean deleted = deleteFile.delete(); int result = 0; if( deleted ) { result = fileMapper.delete(no); } return result; } @Override public List<Files> listByParent(Files file) throws Exception { List<Files> fileList = fileMapper.listByParent(file); return fileList; } @Override public int deleteByParent(Files fileInfo) throws Exception { int result = 0; List<Files> fileList = fileMapper.listByParent(fileInfo); for (Files file : fileList) { File deleteFile = new File(file.getFilePath()); if( !deleteFile.exists() ) continue; if( !deleteFile.delete() ) continue; int deleted = fileMapper.delete(file.getNo()); if( deleted > 0 ) result++; } log.info(result + "개의 νŒŒμΌμ„ μ‚­μ œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int download(int fileNo, HttpServletResponse response) throws Exception { // result // 0 : 파일 λ‹€μš΄λ‘œλ“œ 처리 μ‹€νŒ¨ // 1 : 파일 λ‹€μš΄λ‘œλ“œ 성곡 Files file = fileMapper.select(fileNo); if( file == null ) { // BAD_REQUEST : 400, ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ΄ 잘λͺ»λ˜μ—ˆμŒμ„ μ•Œλ €μ£ΌλŠ” μƒνƒœμ½”λ“œ // response.setStatus(response.SC_BAD_REQUEST); return 0; } String filePath = file.getFilePath(); // 파일 경둜 String fileName = file.getFileName(); // 파일 이름 // λ‹€μš΄λ‘œλ“œ 응닡을 μœ„ν•œ 헀더 μ„ΈνŒ… // - ContentType : application/octet-stream // - Content-Disposition : attachment, filename="파일λͺ….ν™•μž₯자" response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // 파일 λ‹€μš΄λ‘œλ“œ // - 파일 μž…λ ₯ File downloadFile = new File(filePath); FileInputStream fis = new FileInputStream(downloadFile); // - 파일 좜λ ₯ ServletOutputStream sos = response.getOutputStream(); // λ‹€μš΄λ‘œλ“œ FileCopyUtils.copy(fis, sos); // byte[] buffer = new byte[1024]; // 1024bytes = 1KB λ‹¨μœ„ 버퍼 // int data; // while( (data = fis.read(buffer)) != -1 ) { // 1KB μ”© νŒŒμΌμž…λ ₯ // sos.write(buffer, 0, data); // 1KB μ”© 파일좜λ ₯ // } fis.close(); sos.close(); return 1; } @Override public int uploadFiles(Files fileInfo, List<MultipartFile> fileList) throws Exception { int result = 0; for (MultipartFile file : fileList) { result += uploadFile( fileInfo, file); } log.info(result + "개 νŒŒμΌμ„ μ—…λ‘œλ“œν•˜μ˜€μŠ΅λ‹ˆλ‹€."); return result; } @Override public int deleteByNoList(List<Integer> deleteFileNoList) throws Exception { int result = 0; if( deleteFileNoList != null && !deleteFileNoList.isEmpty() ) for (Integer deleteFileNo : deleteFileNoList) { if( deleteFileNo == null ) continue; fileMapper.delete(deleteFileNo); log.info(deleteFileNo + "번 파일 μ‚­μ œ"); result++; } return result; } @Override @Transactional public int upload(Files file) throws Exception { int result = uploadFile(file, file.getFile()); if( result > 0 ) result = fileMapper.maxPk(); return result; } public int uploadFile(Files fileInfo, MultipartFile file) throws Exception { int result = 0; if( file.isEmpty() ) return result; // 파일 정보 : μ›λ³ΈνŒŒμΌλͺ…, 파일 μš©λŸ‰, 파일 데이터 String originName = file.getOriginalFilename(); long fileSize = file.getSize(); byte[] fileData = file.getBytes(); // μ—…λ‘œλ“œ 경둜 // 파일λͺ… 쀑볡 방지 방법(μ •μ±…) // - λ‚ μ§œ_파일λͺ….ν™•μž₯자 // - UID_파일λͺ….ν™•μž₯자 // UID_강아지.png String fileName = UUID.randomUUID().toString() + "_" + originName; // c:/upload/UID_강아지.png String filePath = uploadPath + "/" + fileName; // - μ„œλ²„ μΈ‘, 파일 μ‹œμŠ€ν…œμ— 파일 볡사 File uploadFile = new File(uploadPath, fileName); FileCopyUtils.copy(fileData, uploadFile); // 파일 μ—…λ‘œλ“œ // - DB 에 파일 정보 등둝 Files uploadedFile = new Files(); uploadedFile.setParentTable(fileInfo.getParentTable()); uploadedFile.setParentNo(fileInfo.getParentNo()); uploadedFile.setFileName(fileName); uploadedFile.setFilePath(filePath); uploadedFile.setOriginName(originName); uploadedFile.setFileSize(fileSize); uploadedFile.setFileCode(0); result = fileMapper.insert(uploadedFile); return result; } }
Java
볡사

FileController.java

package com.joeun.server.controller; import java.io.File; import java.io.FileInputStream; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.util.FileCopyUtils; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import com.joeun.server.dto.Files; import com.joeun.server.service.FileService; import com.joeun.server.utils.MediaUtil; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @Slf4j @Controller @RequestMapping("/file") public class FileController { @Autowired private FileService fileService; @Autowired private ResourceLoader resourceLoader; /** * 파일 λ‹€μš΄λ‘œλ“œ * @param fileNo * @param response * @throws Exception */ @GetMapping("/{no}") public void fileDownload(@PathVariable("no") int no ,HttpServletResponse response) throws Exception { // 파일 쑰회 Files file = fileService.select(no); // 파일이 μ‘΄μž¬ν•˜μ§€ μ•ŠμœΌλ©΄, if( file == null ) { // 응닡 μƒνƒœμ½”λ“œ : 400, ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ΄ 잘λͺ»λ˜μ—ˆμŒμ„ λ‚˜νƒ€λ‚΄λŠ” μƒνƒœμ½”λ“œ response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } String fileName = file.getFileName(); // 파일 λͺ… String filePath = file.getFilePath(); // 파일 경둜 // 파일 λ‹€μš΄λ‘œλ“œλ₯Ό μœ„ν•œ 헀더 μ„ΈνŒ… // - ContentType : application/octet-straem // - Content-Disposition : attachment; fileanme="파일λͺ….ν™•μž₯자" response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // 파일 μž…λ ₯ File downloadFile = new File(filePath); FileInputStream fis = new FileInputStream(downloadFile); ServletOutputStream sos = response.getOutputStream(); // λ‹€μš΄λ‘œλ“œ FileCopyUtils.copy(fis, sos); } /** * 이미지 썸넀일 * @param no * @param response * @throws Exception */ @GetMapping("/img/{no}") public void showImg(@PathVariable Integer no, HttpServletResponse response) throws Exception { Files file = fileService.select(no); String filePath = (file != null) ? file.getFilePath() : null; Resource resource = resourceLoader.getResource("classpath:static/img/no-image.png"); File imgFile; if (filePath == null || !(imgFile = new File(filePath)).exists()) { // 파일이 μ‘΄μž¬ν•˜μ§€ μ•Šκ±°λ‚˜ 파일 κ²½λ‘œκ°€ null인 경우 imgFile = resource.getFile(); } String ext = filePath.substring(filePath.lastIndexOf(".") + 1); MediaType mType = MediaUtil.getMediaType(ext); if (mType == null) { // 이미지 νƒ€μž…μ΄ 아닐 경우 response.setContentType(MediaType.IMAGE_PNG_VALUE); // 기본적으둜 PNG둜 μ„€μ • imgFile = resource.getFile(); } else { // 이미지 νƒ€μž…μΌ 경우 response.setContentType(mType.toString()); } FileInputStream fis = new FileInputStream(imgFile); ServletOutputStream sos = response.getOutputStream(); FileCopyUtils.copy(fis, sos); } /** * 파일 μ‚­μ œ * @param file * @return * @throws Exception */ @DeleteMapping("/{no}") public ResponseEntity<String> deleteFile(@PathVariable Integer no) throws Exception { log.info("[DELETE] - /file"); int fileNo = no; log.info("fileNo : " + fileNo); if( fileNo == 0 ) return new ResponseEntity<String>("FAIL", HttpStatus.BAD_REQUEST); int result = fileService.delete(fileNo); if( result == 0 ) return new ResponseEntity<String>("FAIL", HttpStatus.OK); return new ResponseEntity<String>("SUCCESS", HttpStatus.OK); } }
Java
볡사

React (Front-End)

React 둜 파일 μ—…λ‘œλ“œ, 쑰회, μ‚­μ œ UI 및 API λ₯Ό κ΅¬ν˜„ν•©λ‹ˆλ‹€.

파일 μ—…λ‘œλ“œ

파일 등둝 UI μΆ”κ°€

BoardInsertForm.jsx

import React, { useState } from 'react' import { Link } from 'react-router-dom' import './BoardInsertForm.css' const BoardInsertForm = ({ onInsert }) => { // state μ„€μ • const [title, setTitle] = useState(''); const [writer, setWriter] = useState(''); const [content, setContent] = useState(''); const [files, setFiles] = useState(null); // βœ… files state μΆ”κ°€ const handleChangeTitle = (e) => { setTitle(e.target.value) } const handleChangeWriter = (e) => { setWriter(e.target.value) } const handleChangeContent = (e) => { setContent(e.target.value) } // βœ… 파일 ν•Έλ“€λŸ¬ μΆ”κ°€ const handleFileChange = (e) => { setFiles(e.target.files); }; const onSubmit = () => { const formData = new FormData(); formData.append('title', title); formData.append('writer', writer); formData.append('content', content); const headers = { headers: { 'Content-Type' : 'multipart/form-data', }, }; if (files) { for (let i = 0; i < files.length; i++) { formData.append(`files[${i}]`, files[i]); } } // onInsert(title, writer, content) // onInsert({title,writer,content,files}, headers) // formData μ‚¬μš© ❌ onInsert(formData, headers) // formData μ‚¬μš© β­• } return ( <div className='container'> <h1 className='title'>κ²Œμ‹œκΈ€ 등둝</h1> <table className='table'> <tbody> <tr> <td>제λͺ©</td> <td> <input type="text" className='form-input' value={title} onChange={handleChangeTitle} /> </td> </tr> <tr> <td>μž‘μ„±μž</td> <td> <input type="text" className='form-input' value={writer} onChange={handleChangeWriter} /> </td> </tr> <tr> <td colSpan={2}>λ‚΄μš©</td> </tr> <tr> <td colSpan={2}> <textarea cols="40" rows="10" className='form-input' value={content} onChange={handleChangeContent} ></textarea> </td> </tr> <tr> <td>파일</td> <td> <input type='file' onChange={handleFileChange} multiple /> </td> </tr> </tbody> </table> <div className='btn-box'> <Link to="/boards" className='btn'>λͺ©λ‘</Link> <button onClick={ onSubmit } className='btn'>등둝</button> </div> </div> ) } export default BoardInsertForm
JavaScript
볡사

파일 μ—…λ‘œλ“œ μš”μ²­ API

files.js

import axios from 'axios'; // μ—…λ‘œλ“œ export const upload = (formData, headers) => axios.post(`/file/upload`, formData, headers)
JavaScript
볡사

︎ 파일 쑰회

파일 λͺ©λ‘ 쑰회 UI 및 API μΆ”κ°€

BoardReadContainer.jsx

import React, { useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; import * as boards from '../apis/boards'; import BoardRead from '../components/BoardRead/BoardRead'; // πŸ‘©β€πŸ’» κ²Œμ‹œκΈ€ 쑰회 const BoardReadContainer = () => { const { no } = useParams() const [board, setBoard] = useState({}); const [fileList, setFileList] = useState([]); const [isLoading, setLoading] = useState(false); const getBoard = async () => { setLoading(true) try { const response = await boards.select(no); const data = response.data; console.log(data); const board = data.board; const fileList = data.fileList; setBoard(board); setFileList(fileList); } catch(e) { console.log(e); } setLoading(false) } useEffect( () => { getBoard() },[]) return (<BoardRead no={no} board={board} fileList={fileList} isLoading={isLoading} />) } export default BoardReadContainer
JavaScript
볡사

BoardRead.jsx

import React, { useEffect } from 'react'; import { Link } from 'react-router-dom'; import * as format from '../../apis/format'; import './BoardRead.css'; const BoardRead = ({ no, board, fileList, isLoading }) => { return ( <div className='container'> <h1 className='title'>κ²Œμ‹œκΈ€ 쑰회</h1> <h3>번호 : {no}</h3> <hr/> { isLoading && <div> <img src="/img/loading.webp" alt="loading" /> </div> } { !isLoading && board && ( <table border={1} className='table'> <tbody> <tr> <td>번호</td> <td> <input type="text" className='form-input' value={board.no} readOnly /> </td> </tr> <tr> <td>λ“±λ‘μΌμž</td> <td> <input type="text" className='form-input' value={format.formatDate( board.regDate )} readOnly /> </td> </tr> <tr> <td>제λͺ©</td> <td> <input type="text" className='form-input' value={board.title} readOnly /> </td> </tr> <tr> <td>μž‘μ„±μž</td> <td> <input type="text" className='form-input' value={board.writer} readOnly /> </td> </tr> <tr> <td colSpan={2}>λ‚΄μš©</td> </tr> <tr> <td colSpan={2}> <textarea cols="40" rows="10" className='form-input' value={board.content} readOnly></textarea> </td> </tr> <tr> <td colSpan={2}>파일</td> </tr> <tr> <td colSpan={2}> { fileList.map( (file) => ( <div className='file-box' key={file.no}> <div className="item"> <span>{file.originName}({ format.byteToUnit(file.fileSize) })</span> </div> <div className="item"> </div> </div> ))} </td> </tr> </tbody> </table> )} <hr /> <div className="btn-box"> <Link className='btn' to="/boards">λͺ©λ‘</Link> <Link className='btn' to={`/boards/update/${no}`}>μˆ˜μ •</Link> </div> </div> ) } export default BoardRead
JavaScript
볡사

λ‹€μš΄λ‘œλ“œ UI 및 API μΆ”κ°€

BoardRead.jsx

<button className="btn" onClick={() => handleDownload(file.no, file.originName)}>λ‹€μš΄λ‘œλ“œ</button>
JavaScript
볡사
import React, { useEffect } from 'react'; import { Link } from 'react-router-dom'; import * as format from '../../apis/format'; import './BoardRead.css'; const BoardRead = ({ no, board, fileList, isLoading }) => { return ( <div className='container'> <h1 className='title'>κ²Œμ‹œκΈ€ 쑰회</h1> <h3>번호 : {no}</h3> <hr/> { isLoading && <div> <img src="/img/loading.webp" alt="loading" /> </div> } { !isLoading && board && ( <table border={1} className='table'> <tbody> <tr> <td>번호</td> <td> <input type="text" className='form-input' value={board.no} readOnly /> </td> </tr> <tr> <td>λ“±λ‘μΌμž</td> <td> <input type="text" className='form-input' value={format.formatDate( board.regDate )} readOnly /> </td> </tr> <tr> <td>제λͺ©</td> <td> <input type="text" className='form-input' value={board.title} readOnly /> </td> </tr> <tr> <td>μž‘μ„±μž</td> <td> <input type="text" className='form-input' value={board.writer} readOnly /> </td> </tr> <tr> <td colSpan={2}>λ‚΄μš©</td> </tr> <tr> <td colSpan={2}> <textarea cols="40" rows="10" className='form-input' value={board.content} readOnly></textarea> </td> </tr> <tr> <td colSpan={2}>파일</td> </tr> <tr> <td colSpan={2}> { fileList.map( (file) => ( <div className='file-box' key={file.no}> <div className="item"> <span>{file.originName}({ format.byteToUnit(file.fileSize) })</span> </div> <div className="item"> <button className="btn" onClick={() => handleDownload(file.no, file.originName)}>λ‹€μš΄λ‘œλ“œ</button> </div> </div> ))} </td> </tr> </tbody> </table> )} <hr /> <div className="btn-box"> <Link className='btn' to="/boards">λͺ©λ‘</Link> <Link className='btn' to={`/boards/update/${no}`}>μˆ˜μ •</Link> </div> </div> ) } export default BoardRead
JavaScript
볡사

files.js

import axios from 'axios'; // μ—…λ‘œλ“œ export const upload = (formData, headers) => axios.post(`/file/upload`, formData, headers) // λ‹€μš΄λ‘œλ“œ export const download = (fileNo) => axios.get(`/file/${fileNo}`, {responseType: 'blob',} )
JavaScript
볡사

썸넀일 이미지 UI μΆ”κ°€

BoardRead.jsx

<img src={`/file/img/${file.no}`} alt={file.fileName} />
JavaScript
볡사
import React, { useEffect } from 'react'; import { Link } from 'react-router-dom'; import * as format from '../../apis/format'; import './BoardRead.css'; const BoardRead = ({ no, board, fileList, isLoading }) => { return ( <div className='container'> <h1 className='title'>κ²Œμ‹œκΈ€ 쑰회</h1> <h3>번호 : {no}</h3> <hr/> { isLoading && <div> <img src="/img/loading.webp" alt="loading" /> </div> } { !isLoading && board && ( <table border={1} className='table'> <tbody> <tr> <td>번호</td> <td> <input type="text" className='form-input' value={board.no} readOnly /> </td> </tr> <tr> <td>λ“±λ‘μΌμž</td> <td> <input type="text" className='form-input' value={format.formatDate( board.regDate )} readOnly /> </td> </tr> <tr> <td>제λͺ©</td> <td> <input type="text" className='form-input' value={board.title} readOnly /> </td> </tr> <tr> <td>μž‘μ„±μž</td> <td> <input type="text" className='form-input' value={board.writer} readOnly /> </td> </tr> <tr> <td colSpan={2}>λ‚΄μš©</td> </tr> <tr> <td colSpan={2}> <textarea cols="40" rows="10" className='form-input' value={board.content} readOnly></textarea> </td> </tr> <tr> <td colSpan={2}>파일</td> </tr> <tr> <td colSpan={2}> { fileList.map( (file) => ( <div className='file-box' key={file.no}> <div className="item"> <img src={`/file/img/${file.no}`} alt={file.fileName} /> <span>{file.originName}({ format.byteToUnit(file.fileSize) })</span> </div> <div className="item"> <button className="btn" onClick={() => handleDownload(file.no, file.originName)}>λ‹€μš΄λ‘œλ“œ</button> </div> </div> ))} </td> </tr> </tbody> </table> )} <hr /> <div className="btn-box"> <Link className='btn' to="/boards">λͺ©λ‘</Link> <Link className='btn' to={`/boards/update/${no}`}>μˆ˜μ •</Link> </div> </div> ) } export default BoardRead
JavaScript
볡사

파일 μ‚­μ œ

μ‚­μ œν•  파일 μ²΄ν¬λ°•μŠ€ UI μΆ”κ°€, 파일 μ‚­μ œ λ²„νŠΌ UI 및 API μΆ”κ°€

import React, { useEffect, useState } from 'react' import { Link } from 'react-router-dom' import * as format from '../../apis/format' const BoardUpdateForm = ({ no, board, fileList, onUpdate, onDelete, isLoading, onDownload, onDeleteFile }) => { // state μ„€μ • const [title, setTitle] = useState('') const [writer, setWriter] = useState('') const [content, setContent] = useState('') const [files, setFiles] = useState(null); // βœ… files state μΆ”κ°€ const [fileNoList, setFileList] = useState([]) // βœ… 파일 선택 μ‚­μ œ - deleteFileNoList const handleChangeTitle = (e) => { setTitle(e.target.value) } const handleChangeWriter = (e) => { setWriter(e.target.value) } const handleChangeContent = (e) => { setContent(e.target.value) } const onSubmit = () => { const formData = new FormData(); formData.append('no', no); formData.append('title', title); formData.append('writer', writer); formData.append('content', content); console.log(`fileNoList --------------------------------------`); console.log(fileNoList); formData.append('deleteFileNoList', fileNoList); const headers = { headers: { 'Content-Type' : 'multipart/form-data', }, }; if (files) { for (let i = 0; i < files.length; i++) { formData.append(`files[${i}]`, files[i]); } } // onUpdate(no, title, writer, content); // onUpdate({no, title, writer, content}, headers); onUpdate(formData, headers); } const handleDelete = () => { const check = window.confirm('μ •λ§λ‘œ μ‚­μ œν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?') if( check ) onDelete(no) } const handleDownload = (fileNo, fileName) => { onDownload(fileNo, fileName) } const handleDeleteFile =(fileNo) => { const check = window.confirm('μ •λ§λ‘œ μ‚­μ œν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?') if( check ) onDeleteFile(fileNo) } // βœ… 파일 ν•Έλ“€λŸ¬ μΆ”κ°€ const handleFileChange = (e) => { setFiles(e.target.files); }; // 파일 번호 체크 const checkFileNo = (no) => { setFileList( [...fileNoList, no] ) } useEffect( () => { if( board ) { setTitle(board.title); setWriter(board.writer) setContent(board.content) } }, [board]) return ( <div className='container'> <h1 className='title'>κ²Œμ‹œκΈ€ μˆ˜μ •</h1> <h3>번호 : {no}</h3> <hr/> { isLoading && <div> <img src="/img/loading.webp" alt="loading" /> </div> } { !isLoading && board && ( <table border={1} className='table'> <tbody> <tr> <td>번호</td> <td> <input type="text" className='form-input' value={board.no} readOnly /> </td> </tr> <tr> <td>λ“±λ‘μΌμž</td> <td> <input type="text" className='form-input' value={ format.formatDate(board.regDate) } readOnly /> </td> </tr> <tr> <td>제λͺ©</td> <td> <input type="text" className='form-input' value={title} onChange={handleChangeTitle} /> </td> </tr> <tr> <td>μž‘μ„±μž</td> <td> <input type="text" className='form-input' value={writer} onChange={handleChangeWriter} /> </td> </tr> <tr> <td colSpan={2}>λ‚΄μš©</td> </tr> <tr> <td colSpan={2}> <textarea cols="40" rows="10" className='form-input' value={content} onChange={handleChangeContent}></textarea> </td> </tr> <tr> <td>파일 μ—…λ‘œλ“œ</td> <td> <input type='file' onChange={handleFileChange} multiple /> </td> </tr> { fileList && fileList.length > 0 && <tr> <td>μ‚­μ œ</td> <td>파일</td> </tr> } { fileList.length == 0 && <tr> <td colSpan={2} align='center'> μ—…λ‘œλ“œλœ 파일이 μ—†μŠ΅λ‹ˆλ‹€. </td> </tr> } { fileList.map( (file) => ( <tr> <td> <input type="checkbox" onChange={ () => checkFileNo(file.no) } /> </td> <td> <div className='file-box' key={file.no}> <div className="item"> <img src={`/file/img/${file.no}`} alt={file.fileName} /> <span>{file.originName}({ format.byteToUnit(file.fileSize) })</span> </div> <div className="item"> <button className="btn" onClick={() => handleDownload(file.no, file.originName)}>λ‹€μš΄λ‘œλ“œ</button> <button className="btn" onClick={() => handleDeleteFile(file.no)}>μ‚­μ œ</button> </div> </div> </td> </tr> ))} </tbody> </table> )} <hr /> <div className="btn-box"> <div className="item"> <Link to="/boards" className='btn'>λͺ©λ‘</Link> </div> <div className="item"> <button className='btn' onClick={ handleDelete }>μ‚­μ œ</button> <button className='btn' tton onClick={ onSubmit }>μˆ˜μ •</button> </div> </div> </div> ) } export default BoardUpdateForm
JavaScript
볡사

Github (μ†ŒμŠ€μ½”λ“œ)