스프링

 

스프링부트 (2.7)  & mybatis 

 

 

 

 

 

 

 

1. pom.xml

		<!-- 썸네일 라이브러리 -->
		<dependency>
		    <groupId>net.coobird</groupId>
		    <artifactId>thumbnailator</artifactId>
		    <version>0.4.17</version>
		</dependency>


		<!-- 파일 타입 체크 라이브러리 -->
		<dependency>
		    <groupId>org.apache.tika</groupId>
		    <artifactId>tika-parsers</artifactId>
		    <version>2.4.1</version>
		    <type>pom</type>
		</dependency>

		<dependency>
		    <groupId>org.apache.tika</groupId>
		    <artifactId>tika-core</artifactId>
		    <version>2.4.1</version>
		</dependency>

 

2.TABLE


CREATE TABLE `T_UPLOAD_FILE` (
  `UPLOAD_FILE_SEQ` bigint NOT NULL AUTO_INCREMENT COMMENT '파일테이블 PK', 
  `BOARD_SEQ` bigint null COMMENT '게시판 테이블 PK',
  `BOARD_TYPE` varchar(255) DEFAULT 'NONE' COMMENT '게시판 종류', 
  `PATHNAME` VARCHAR(100) NOT NULL COMMENT '전체경로', 
  `FILENAME` VARCHAR(50) NOT NULL COMMENT '파일명' , 
  `ORIGINAL_FILENAME` VARCHAR(100) NOT NULL COMMENT '원본 파일명' , 
  `SIZE` INT(11) NOT NULL COMMENT '파일크기',
  `CONTENT_TYPE` VARCHAR(50) NOT NULL COMMENT '컨텐츠 종류' , 
  `RESOURCE_PATHNAME` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '리소스 파일경로' , 
  `THUMBNAIL_PATHNAME` VARCHAR(100) NULL COMMENT '썸네일 전체경로',
  `THUMBNAIL_RESOURCE_PATHNAME` VARCHAR(100) NULL DEFAULT '' COMMENT '썸네일 리소스파일경로' ,  
  `REG_DATE` timestamp NOT null DEFAULT CURRENT_TIMESTAMP() COMMENT '등록일',
  PRIMARY KEY (`UPLOAD_FILE_SEQ`) USING BTREE   
)ENGINE=InnoDB  COLLATE='utf8mb4_unicode_ci' COMMENT='파일';


 

3.UploadFile

import java.util.Date;

import kr.net.macaronics.mvc.domain.enums.BoardType;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class UploadFile {
	
	private int uploadFileSeq;  //파일테이블 PK
	private Integer boardSeq;   //게시판 테이블 PK
	private BoardType boardType;  //게시판 종류
	private String pathname; //전체경로
	private String filename;  //파일명
	private String originalFilename;  //원본 파일명
	private int size; //파일크기
	private String contentType;  //컨텐츠 종류
	private String resourcePathname; //리소스 파일경로
	private String thumbnailPathname; //썸네일 전체경로
	private String thumbnailResourcePathname; //썸네일 리소스파일경로
	private Date regDate; //등록일
}

 

 

4.UploadFileDTO

import java.util.Date;

import kr.net.macaronics.mvc.domain.enums.BoardType;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

/** 파일 데이터를 가져올시 파라미터 */
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class UploadFileDTO {

	private int uploadFileSeq;  //파일테이블 PK
	private Integer boardSeq;   //게시판 테이블 PK
	private BoardType boardType;  //게시판 종류
	private String pathname; //전체경로
	private String filename;  //파일명
	private String originalFilename;  //원본 파일명
	private int size; //파일크기
	private String contentType;  //컨텐츠 종류
	private String resourcePathname; //리소스 파일경로
	private String thumbnailPathname; //썸네일 전체경로
	private String thumbnailResourcePathname; //썸네일 리소스파일경로
	private Date regDate; //등록일
	
	
	
}

 

5. UploadFileInsertDTO

import kr.net.macaronics.mvc.domain.enums.BoardTypeInsert;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

/**  등록시 파라미터 */
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class UploadFileInsertDTO {

	
	private int uploadFileSeq;  //파일테이블 PK
	private Integer boardSeq;   //게시판 테이블 PK
	private BoardTypeInsert boardType;  //게시판 종류
	private String pathname; //전체경로
	private String filename;  //파일명
	private String originalFilename;  //원본 파일명
	private int size; //파일크기
	private String contentType;  //컨텐츠 종류
	private String resourcePathname; //리소스 파일경로
	private String thumbnailPathname; //썸네일 전체경로
	private String thumbnailResourcePathname; //썸네일 리소스파일경로
	
	
	@Builder
	public UploadFileInsertDTO (Integer boardSeq ,BoardTypeInsert boardType, String pathname, String filename,
			String originalFilename, int size, String contentType, String resourcePathname, String thumbnailPathname,
			String thumbnailResourcePathname) {
		this.boardSeq = boardSeq;
		this.boardType = boardType;
		this.pathname = pathname;
		this.filename = filename;
		this.originalFilename = originalFilename;
		this.size = size;
		this.contentType = contentType;
		this.resourcePathname = resourcePathname;
		this.thumbnailPathname = thumbnailPathname;
		this.thumbnailResourcePathname = thumbnailResourcePathname;
	}	
}

 

 

 

 

6. 컨트롤러

1)UploadFileApiController

import java.io.File;
import java.io.FileInputStream;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Delete;
import org.springframework.ui.Model;
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 org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import kr.net.macaronics.configuration.GlobalConfig;
import kr.net.macaronics.configuration.exception.BaseException;
import kr.net.macaronics.configuration.http.BaseResponse;
import kr.net.macaronics.configuration.http.BaseResponseCode;
import kr.net.macaronics.mvc.domain.dto.UploadFileDTO;
import kr.net.macaronics.mvc.domain.dto.UploadFileInsertDTO;
import kr.net.macaronics.mvc.domain.enums.ThumbnailType;
import kr.net.macaronics.mvc.service.UploadFileService;
import kr.net.macaronics.utils.DownloadView;
import kr.net.macaronics.utils.pagination2.MysqlPageMaker;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;

@Slf4j
@RestController
@RequestMapping("/api/file")
@Api(tags="파일 업로드 API")
@RequiredArgsConstructor
public class UploadFileApiController {

		
	private final GlobalConfig config;
	
	private final UploadFileService uploadFileService;
	
	
	@PostMapping("/save")
	@ApiOperation(value="테스트-파일업로드", notes = "파일업로드 샘플1")
	public BaseResponse<Boolean> save(@RequestParam("uploadFile") MultipartFile multipartFile) throws Exception {
		if(multipartFile==null || multipartFile.isEmpty()) {
			throw new BaseException(BaseResponseCode.DATA_IS_NULL.name());
		}
		
		//날짜폴더를 추가
		String currentDate=new SimpleDateFormat("yyyyMMdd").format(Calendar.getInstance().getTime());
		log.debug("config : {} ", config);		
		String uploadFilePath=config.getUploadFilePath()+currentDate+"/";
		log.info("uploadFilePath  :  {} " , uploadFilePath);
				
		
		String prefix=multipartFile.getOriginalFilename().substring(multipartFile.getOriginalFilename().lastIndexOf(".")+1, multipartFile.getOriginalFilename().length());
		String filename=UUID.randomUUID().toString()+"."+prefix;
		File folder =new File(uploadFilePath);		
		if(!folder.isDirectory()) {
			folder.mkdirs();
		}

		String pathname=uploadFilePath +filename;
		String resourcePathname =config.getUploadResourcePath()+currentDate+"/"+filename;
		File dest=new File(pathname);
				
		log.info("dest  :  {}", dest);		
		multipartFile.transferTo(dest);
		
		//파일업로드 된 후 DB에 저장
		UploadFileInsertDTO uploadFileDTO=UploadFileInsertDTO.builder()
		.contentType(multipartFile.getContentType()) 	//컨텐츠 종류
		.originalFilename(multipartFile.getOriginalFilename()) //원본파일명
		.filename(filename) //저장파일명
		.pathname(pathname) //실제파일 저장경로
		.size((int)multipartFile.getSize()) //파일크기
		.resourcePathname(resourcePathname) 	//static resource 접근 경로
		.build();
		
		uploadFileService.save(uploadFileDTO);		
		return new BaseResponse<Boolean>(true);
	}
	
	
	/**
	 * 업로드된 파일 썸네일 만들기 공통 throws Exception
	 * http://localhost:8080/api/file/thumbnail/make/1/WEB_MAIN
	 * */
	@GetMapping("/thumbnail/make/{uploadFileSeq}/{thumbnailType}")
	@ApiOperation(value="테스트-업로드된 파일 썸네일 만들기", notes = "파일업로드 샘플2")
	public BaseResponse<String> thumb(@PathVariable int uploadFileSeq,
			@PathVariable ThumbnailType thumbnailType
			) throws Exception{
		
		UploadFileDTO uploadFileDTO=uploadFileService.get(uploadFileSeq);		
		String pathname=uploadFileDTO.getPathname();		
				
		String thumbnailPathname=uploadFileDTO.getPathname().replace(".", thumbnailType.width()+"_"+thumbnailType.height()+".");
		
		File thumbanilFile=new File(thumbnailPathname);
		if(!thumbanilFile.isFile()) {
			Thumbnails.of(new File(pathname))
				.size(thumbnailType.width(), thumbnailType.height())
				.toFile(new File(thumbnailPathname));			
		}	
		
		return new BaseResponse<String>(uploadFileDTO.getResourcePathname().replace(".", thumbnailType.width()+"_"+thumbnailType.height()+"."));
	}
	
	
	/**thumbnailator 라이브러리를 통한 업로드된 썸네일 이미지 출력
	 * http://localhost:8080/api/file/thumbnail/output/1/WIDTH_200
	 * */
	@GetMapping("/thumbnail/output/{uploadFileSeq}/{thumbnailType}")
	@ApiOperation(value="테스트-thumbnailator 라이브러리를 통한 업로드된 썸네일 이미지 출력", notes = "파일업로드 샘플3")
	public void thumbnailOutput(@PathVariable int uploadFileSeq,
			@PathVariable ThumbnailType thumbnailType,
			HttpServletResponse response
			) throws Exception{
		
		UploadFileDTO uploadFileDTO=uploadFileService.get(uploadFileSeq);		
		String pathname=uploadFileDTO.getPathname();		
				
		String thumbanilPathname=uploadFileDTO.getPathname().replace(".", thumbnailType.width()+"_"+thumbnailType.height()+".");
		
		File thumbanilFile=new File(thumbanilPathname);
		if(!thumbanilFile.isFile()) {
			Thumbnails.of(new File(pathname))
				.size(thumbnailType.width(), thumbnailType.height())
				.toFile(new File(thumbanilPathname));			
		}	
		
		
		response.setContentType(uploadFileDTO.getContentType());
		FileCopyUtils.copy(new FileInputStream(thumbanilFile), response.getOutputStream());		
	}
	
	
	/*****************************************************************************************************************/
	/*****************************************************************************************************************/
	/*****************************************************************************************************************/
	
	
	
	/**	 파일업로드 - 이미지일 경우 썸네일등록 */
	@PostMapping("/fileSave")
	@ApiOperation(value="파일 등록 - 이미지일 경우 썸네일등록", notes = "서비스에서 작업 -  MimeType  파일 위조 변조 체크")
	public BaseResponse<UploadFileDTO> fileSave(@RequestParam("uploadFile") MultipartFile multipartFile) throws Exception {	
		int  uploadFileSeq=uploadFileService.fileSave(multipartFile, null);
		return new BaseResponse<UploadFileDTO>(uploadFileService.get(uploadFileSeq));
	}
	
	
	/**	멀티파일업로드 - 이미지일 경우 썸네일등록 */
	@PostMapping("/multiFileSave")
	@ApiOperation(value="멀티파일 등록 - 이미지일 경우 썸네일등록", notes = "멀티파일 등록")
	public  BaseResponse<List<UploadFileDTO>> multiFileSave(@RequestParam("uploadFile") List<MultipartFile> multipartFile) throws Exception {		
		List<UploadFileDTO> uploadFileDTOList=uploadFileService.multiFileSave(multipartFile, null);		
		return new  BaseResponse<List<UploadFileDTO>>(uploadFileDTOList);
	}
	
	
	
	/** 전체 파일목록 불러오기 
	 * http://localhost:8080/api/file/fileList
	 * */
	@GetMapping({"/fileList"})
	@ApiOperation(value="전체 파일목록 불러오기", notes="전체 파일목록 불러오기 ")
	public BaseResponse<List<UploadFileDTO>> fileList(
			@ApiParam MysqlPageMaker pageMaker
			) throws Exception{
		
		 Map<String, Object> map=new HashMap<>();		
		 map.put("pageMaker", pageMaker);
		 map.put("boardSeq", "");  
		 pageMaker.setTotalCount(uploadFileService.getTotalCount(map));
	    
	    return new BaseResponse<List<UploadFileDTO>>(uploadFileService.fileList(map), pageMaker);
	}

	
	/** 게시판 pk 파일목록 불러오기
	 * http://localhost:8080/api/file/boarddPkFileList/1
	 *  */
	@GetMapping({"/boarddPkFileList/{boardSeq}"})
	@ApiOperation(value="게시판 번호에 따른 파일 목록 불러오기", notes="boardSeq 파일목록 불러오기 " )
	public BaseResponse<List<UploadFileDTO>> boarddPkFileList(
			@ApiParam MysqlPageMaker pageMaker,
			@ApiParam @PathVariable int boardSeq
			) throws Exception{
		Map<String, Object> map=new HashMap<>();		
		map.put("pageMaker", pageMaker);
		map.put("boardSeq", boardSeq);     		
		
		int totalCount =uploadFileService.getTotalCount(map);
	    pageMaker.setTotalCount(totalCount);
	     		
	    return new BaseResponse<List<UploadFileDTO>>(uploadFileService.bordPkFileList(map), pageMaker);
	}

	
	
	/** 게시판 boardType 별 파일목록 불러오기 *  */
	@GetMapping({"/boardTypeFileList/{boardType}"})
	@ApiOperation(value="게시판 타입별 파일 목록 불러오기", notes="boardType 별 파일목록 불러오기 ")
	public BaseResponse<List<UploadFileDTO>> boardTypeFileList(
			@ApiParam MysqlPageMaker pageMaker,
			@ApiParam @PathVariable String boardType
			) throws Exception{
		Map<String, Object> map=new HashMap<>();		
		map.put("pageMaker", pageMaker);
		map.put("boardSeq", "");
		map.put("searchType", boardType);
		
		log.info("map  {} ", map.toString());
		
		int totalCount =uploadFileService.getTotalCount(map);
	    pageMaker.setTotalCount(totalCount);	     		
	    return new BaseResponse<List<UploadFileDTO>>(uploadFileService.bordPkFileList(map), pageMaker);
	}

	
	


	/**	 파일정보가져오기  */
	@GetMapping("/getFileInfo/{uploadFileSeq}")
	@ApiOperation(value="파일 정보가져오기", notes = "파일 다운로드")
	public BaseResponse<UploadFileDTO> getFileInfo(@PathVariable int uploadFileSeq , Model model) throws Exception {			
			UploadFileDTO uploadFileDTO=uploadFileService.get(uploadFileSeq);	
			if(uploadFileDTO==null) {
				throw new BaseException("해당 파일이 존재하지 않습니다.");
			}						
		return new BaseResponse<UploadFileDTO>(uploadFileDTO);			
	}
	
	
	
	/**	 파일 다운로드  */	
	@GetMapping(value = "/fileDownload/{uploadFileSeq}")
	@ApiOperation(value="파일 다운로드", notes = "파일 다운로드")
	public DownloadView fileDownload(@PathVariable int uploadFileSeq , Model model) throws Exception {			
			UploadFileDTO uploadFileDTO=uploadFileService.get(uploadFileSeq);	
			if(uploadFileDTO==null) {
				throw new BaseException("해당 파일이 존재하지 않습니다.");
			}
			
			File file = new File(uploadFileDTO.getPathname());
			model.addAttribute("downloadFile", file);
			model.addAttribute("orignalName", uploadFileDTO.getOriginalFilename());
			DownloadView downloadView=new DownloadView();
			return downloadView;			
	}
	
	
	
	/**	 파일 삭제
	 * http://localhost:8080/api/file/fileDelate/1
	 *  */	
	@DeleteMapping("/fileDelate/{uploadFileSeq}")
	@ApiOperation(value="파일 삭제", notes = "파일 삭제")
	public BaseResponse<String> fileDelete(@PathVariable int uploadFileSeq) throws Exception{
		uploadFileService.fileDelete(uploadFileSeq);				
		return new BaseResponse<String>("success",  BaseResponseCode.SUCCESS, "삭제 처리되었습니다.");		
	}
	

	
	
}

 

 

2) UploadFileController

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/file")
public class UploadFileController {

	
	
	/** 파일업로드 테스트 페이지 */
	@GetMapping("/fileUpload")
	public String fileUpload() {
		return "file_upload";
	}
	
	
	/** 파일 목록 페이지 */
	@GetMapping("/fileList")
	public String fileList() {
		return "file_list";
	}
	
}

 

 

7.UploadFileService

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.commons.lang3.StringUtils;
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 kr.net.macaronics.configuration.GlobalConfig;
import kr.net.macaronics.configuration.exception.BaseException;
import kr.net.macaronics.configuration.http.BaseResponseCode;
import kr.net.macaronics.mvc.domain.dto.BoardDTO;
import kr.net.macaronics.mvc.domain.dto.BoardInsertDTO;
import kr.net.macaronics.mvc.domain.dto.UploadFileDTO;
import kr.net.macaronics.mvc.domain.dto.UploadFileInsertDTO;
import kr.net.macaronics.mvc.domain.enums.BoardTypeInsert;
import kr.net.macaronics.mvc.domain.enums.ThumbnailType;
import kr.net.macaronics.mvc.repository.UploadFileRepository;
import kr.net.macaronics.utils.FileFilter;
import kr.net.macaronics.utils.pagination2.MysqlPageMaker;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;

/** 업로드파일 Service */
@Service
@Slf4j
public class UploadFileService {

	@Autowired
	private UploadFileRepository uploadFileRepository;
	
	@Autowired
	private GlobalConfig config;
	
	/** 등록처리 */
	public int save(UploadFileInsertDTO uploadFileInsertDTO) throws Exception{
		return uploadFileRepository.save(uploadFileInsertDTO);
	}

	public UploadFileDTO get(int uploadFileSeq) throws Exception {
		return uploadFileRepository.get(uploadFileSeq);		
	}

	
	public int fileSave(MultipartFile multipartFile, BoardInsertDTO boardInsertDTO) throws Exception {
		if(multipartFile==null || multipartFile.isEmpty()) {
			throw new BaseException(BaseResponseCode.DATA_IS_NULL.name());
		}

		Integer boardSeq=null; //게시판 업로드일 파일 경우 - 게시판 테이블 PK 
		BoardTypeInsert boardType=BoardTypeInsert.NONE; //게시판 타입		
		if(boardInsertDTO!=null) {
			boardSeq=boardInsertDTO.getBoardSeq();   
			boardType=boardInsertDTO.getBoardType(); 		
		}
		
		
		//1.업로드 가능한지 파일인지 체크 			
		if(!FileFilter.isPermisionFileMimeType(multipartFile)) {
			throw new BaseException("업로드할 수 없는 파일입니다.");
		};

		
		//2.날짜폴더를 추가 - 이미지가 아닌파일은 files 디렉토리에 저장
		String currentDate=new SimpleDateFormat("yyyyMMdd").format(Calendar.getInstance().getTime());
		String uploadFilePath=config.getUploadFilePath()+"files/"+currentDate+"/";
	
		//3.이미지파일 체크  - 이미지파일은 images 디렉토리에 저장
		boolean isImage=false;
		if(FileFilter.validImgFile(multipartFile)) {
			isImage=true;
			uploadFilePath=config.getUploadFilePath()+"images/"+currentDate+"/";
		}
					
		
		//4.랜덤 파일명 생성 및 디렉토리 생성 
		String prefix=multipartFile.getOriginalFilename().
				substring(multipartFile.getOriginalFilename().lastIndexOf(".")+1, multipartFile.getOriginalFilename().length());
		String filename=UUID.randomUUID().toString()+"."+prefix;
		File folder =new File(uploadFilePath);		
		if(!folder.isDirectory()) {
			folder.mkdirs();
		}

		
		//5.URL 주소에서 보여줄 파일경로
		String pathname=uploadFilePath +filename;
		String resourcePathname =config.getUploadResourcePath()+(isImage?"images/":"files/") +currentDate+"/"+filename;
		File dest=new File(pathname);
		
		
		//6.파일 생성
		log.info("dest  :  {}", dest);		
		multipartFile.transferTo(dest);		
		
		String thumbnailPathname=null; //썸네일 전체경로
		String thumbnailResourcePathname=null; //썸네일 리소스파일경로
		
		//7.업로드 파일일 이미지 일경우 썸네일 생성
		if(isImage){
			thumbnailPathname=pathname.replace(".", "_"+ThumbnailType.WEB_MAIN.width()+"_"+ThumbnailType.WEB_MAIN.height()+".");			
			File thumbanilFile=new File(thumbnailPathname);
			if(!thumbanilFile.isFile()) {
				Thumbnails.of(new File(pathname))
					.size(ThumbnailType.WEB_MAIN.width(), ThumbnailType.WEB_MAIN.height())
					.toFile(new File(thumbnailPathname));	
				
				thumbnailResourcePathname =resourcePathname.replace(".",  "_"+ThumbnailType.WEB_MAIN.width()+"_"+ThumbnailType.WEB_MAIN.height()+".");
			}				
		}
		
		//9.UploadFileInsertDTO 객체에 저장
		UploadFileInsertDTO uploadFileInsertDTO=UploadFileInsertDTO.builder()
		.boardSeq(boardSeq)
		.boardType(boardType)	
		.contentType(multipartFile.getContentType()) 	//컨텐츠 종류
		.originalFilename(multipartFile.getOriginalFilename()) //원본파일명
		.filename(filename) //저장파일명
		.pathname(pathname) //실제파일 저장경로
		.size((int)multipartFile.getSize()) //파일크기
		.resourcePathname(resourcePathname) 	//static resource 접근 경로
		.thumbnailPathname(thumbnailPathname)
		.thumbnailResourcePathname(thumbnailResourcePathname)
		.build();

		
		uploadFileRepository.save(uploadFileInsertDTO);
		
		//9.파일업로드 된 후 DB에 저장후 반화
		return uploadFileInsertDTO.getUploadFileSeq();
	}

	
	
	public int getTotalCount(Map<String, Object> map) throws Exception {
		return uploadFileRepository.getTotalCount(map);
	}

	public List<UploadFileDTO> fileList(Map<String, Object> map) throws Exception {
		return uploadFileRepository.fileList(map);
	}

	public List<UploadFileDTO> bordPkFileList(Map<String, Object> map) throws Exception {
		return uploadFileRepository.bordPkFileList(map);
	}

	
	public void fileDelete(int uploadFileSeq)  throws Exception {
		UploadFileDTO uploadFileDTO=uploadFileRepository.get(uploadFileSeq);	
		if(uploadFileDTO==null) {
			throw new BaseException("해당 파일이 존재하지 않습니다.");
		}
				
		File file=new File(uploadFileDTO.getPathname());
		if(file.exists())file.delete();
		else throw new BaseException("해당 파일이 존재하지 않습니다.");
		
		//썸네일 삭제
		if(!StringUtils.isEmpty(uploadFileDTO.getThumbnailPathname())) {
			File thumbnailFile=new File(uploadFileDTO.getThumbnailPathname());
			if(thumbnailFile.exists()) thumbnailFile.delete();
			else throw new BaseException("해당 파일이 존재하지 않습니다."); 
		}
		
		//DB에서 삭제처리
		uploadFileRepository.fileDelete(uploadFileSeq);
	}

	public List<UploadFileDTO> multiFileSave(List<MultipartFile> multipartFile, BoardInsertDTO boardInsertDTO) throws Exception {
		
		List<Integer> uploadFileSeq=new ArrayList<Integer>();
		for(MultipartFile multiFile : multipartFile) {
			uploadFileSeq.add(fileSave(multiFile, null));
		}
		return	uploadFileRepository.fileSelectList(uploadFileSeq);
		
	}
	
	
	
}

 

 

8.UploadFileRepository

import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Repository;

import kr.net.macaronics.mvc.domain.dto.UploadFileDTO;
import kr.net.macaronics.mvc.domain.dto.UploadFileInsertDTO;

/** 업로드 파일 Repository */
@Repository
public interface UploadFileRepository {

	public int save(UploadFileInsertDTO uploadFileInsertDTO) throws Exception;

	public UploadFileDTO get(int uploadFileSeq) throws Exception;

	public int getTotalCount(Map<String, Object> map) throws Exception;

	public List<UploadFileDTO> fileList(Map<String, Object> map) throws Exception;

	public List<UploadFileDTO> bordPkFileList(Map<String, Object> map) throws Exception;

	public void fileDelete(int uploadFileSeq) throws Exception;

	public List<UploadFileDTO> fileSelectList(List<Integer> uploadFileSeq) throws Exception;
		
	
}

 

9.UploadFile.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">

<mapper namespace="kr.net.macaronics.mvc.repository.UploadFileRepository">
  

	 <insert id="save" parameterType="kr.net.macaronics.mvc.domain.dto.UploadFileInsertDTO" useGeneratedKeys="true" keyProperty="uploadFileSeq">
	 		INSERT INTO T_UPLOAD_FILE
			
			(BOARD_SEQ , BOARD_TYPE ,PATHNAME, FILENAME, ORIGINAL_FILENAME, `SIZE`, CONTENT_TYPE, RESOURCE_PATHNAME , THUMBNAIL_PATHNAME, THUMBNAIL_RESOURCE_PATHNAME)
			
			VALUES(#{boardSeq}, #{boardType}, #{pathname}, #{filename}, #{originalFilename}, 
			
			#{size}, #{contentType}, #{resourcePathname} , #{thumbnailPathname}, #{thumbnailResourcePathname}) 		
	 </insert>


	<select id="get" resultType="kr.net.macaronics.mvc.domain.dto.UploadFileDTO">
			SELECT 
				UPLOAD_FILE_SEQ , BOARD_SEQ, BOARD_TYPE, PATHNAME, FILENAME, ORIGINAL_FILENAME, `SIZE`, CONTENT_TYPE, 
				RESOURCE_PATHNAME ,	THUMBNAIL_PATHNAME, THUMBNAIL_RESOURCE_PATHNAME,	 REG_DATE
			FROM T_UPLOAD_FILE UF	
			WHERE UF.UPLOAD_FILE_SEQ =#{uploadFileSeq}		
	</select>



	<select id="fileList" resultType="kr.net.macaronics.mvc.domain.dto.UploadFileDTO" parameterType="java.util.HashMap">		
		 SELECT 
		 		 UPLOAD_FILE_SEQ, 
				 BOARD_SEQ, 
				 BOARD_TYPE, 
				 PATHNAME,
				 FILENAME, 
				 ORIGINAL_FILENAME, 
				 `SIZE`, 
				 CONTENT_TYPE, 
				 RESOURCE_PATHNAME, 
				 THUMBNAIL_PATHNAME, 
				 THUMBNAIL_RESOURCE_PATHNAME,
				 REG_DATE
		FROM T_UPLOAD_FILE U
		
		<include refid="searchSql"></include>
		
		ORDER BY U.REG_DATE DESC     
		LIMIT #{pageMaker.pageStart} , #{pageMaker.perPageNum}
	</select>


	
	 <sql id="searchSql" >
       <where>
	   	 <if test='boardSeq !=null and !boardSeq.equals("")'>
	   	 	AND U.BOARD_SEQ = #{boardSeq}
	   	 </if> 
	   	 
		  <if test="@org.apache.commons.lang3.StringUtils@isNotEmpty(searchType)">
		  		
		   		<if test="searchType == 'NOTICE'.toString()">
		   			AND  U.BOARD_TYPE ='NOTICE'
		   		</if>
	
		   		<if test="searchType == 'FAQ'.toString()">
		   			AND  U.BOARD_TYPE ='FAQ'
		   		</if>
		   		
		   		<if test="searchType == 'INQUIRY'.toString()">
		   			AND  U.BOARD_TYPE ='INQUIRY'
		   		</if>
		   			   			   			   		
		   	 </if> 
	   	 
	   </where>       
    </sql>
	
	
	<select id="getTotalCount" resultType="int" parameterType="java.util.HashMap">	
		SELECT
		 	 COUNT(U.UPLOAD_FILE_SEQ) AS CNT    	 
		FROM T_UPLOAD_FILE  U
		
		<include refid="searchSql"></include>
	</select>






	<select id="bordPkFileList" resultType="kr.net.macaronics.mvc.domain.dto.UploadFileDTO">		
		 SELECT 
		 		 UPLOAD_FILE_SEQ, 
				 BOARD_SEQ, 
				 BOARD_TYPE, 
				 PATHNAME,
				 FILENAME, 
				 ORIGINAL_FILENAME, 
				 `SIZE`, 
				 CONTENT_TYPE, 
				 RESOURCE_PATHNAME, 
				 THUMBNAIL_PATHNAME, 
				 THUMBNAIL_RESOURCE_PATHNAME,
				 REG_DATE
		FROM T_UPLOAD_FILE U
		
		<include refid="searchSql"></include>
		
		ORDER BY U.REG_DATE DESC     
		LIMIT #{pageMaker.pageStart} , #{pageMaker.perPageNum}
	</select>



	<delete id="fileDelete">
		DELETE FROM T_UPLOAD_FILE WHERE UPLOAD_FILE_SEQ =#{uploadFileSeq}
	</delete>



	<select id="fileSelectList"  resultType="kr.net.macaronics.mvc.domain.dto.UploadFileDTO">
	
		SELECT * FROM T_UPLOAD_FILE
		
		WHERE  UPLOAD_FILE_SEQ IN(		
		  <foreach collection="uploadFileSeq" item="value" separator=",">
   	 			#{value}
   	 	  </foreach>
   	 	 ) 
	</select>

</mapper>

 

 

10. 개발 환경에 따른 파일 출력 설정    GlobalConfig,  WebMvcConfig

 

GlobalConfig

import java.util.Properties;

import javax.annotation.PostConstruct;

import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PropertiesLoaderUtils;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

/**
 * global ~.properties 값을 읽어와	글로벌 변수설정하는 클래스
 */
@Slf4j
@Data
public class GlobalConfig {

	@Autowired
	private ApplicationContext context;
	
	@Autowired
	private ResourceLoader resourceLoader;		
	
	private String uploadFilePath;//파일 저장 경로	
	private String schedulerCronExample1;//스케쥴처리
	private String uploadResourcePath;//웹 브라우저 출력시 업로드파일경로
		
	private boolean local;
	private boolean dev;
	private boolean prod;
	
	
	/**
	 * @PostConstruct는 의존성 주입이 이루어진 후 초기화를 수행하는 메서드이다. 
	 * @PostConstruct가 붙은 메서드는 클래스가 service를 수행하기 전에 발생한다.
	 *  이 메서드는 다른 리소스에서 호출되지 않는다해도 수행된다. 
		
		@PostConstruct의 사용 이유
		1) 생성자가 호출되었을 때, 빈은 초기화되지 않았음(의존성 주입이 이루어지지 않았음) 
		이럴 때 @PostConstruct를 사용하면 의존성 주입이 끝나고 실행됨이 보장되므로 빈의 초기화에 대해서 걱정할 필요가 없다. 
		2) bean 의 생애주기에서 오직 한 번만 수행된다는 것을 보장한다. (어플리케이션이 실행될 때 한번만 실행됨)
		따라서 bean이 여러 번 초기화되는 걸 방지할 수 있다
		여기서는, ApplicationContext, ResourceLoader 가 의존성 주입이 완료되었는지에 대해 염려할 필요가 없다. 
	 */
	@PostConstruct
	public void init(){
		log.info("GlobalConfig-init" );
		String[] activeProfiles =context.getEnvironment().getActiveProfiles();
		String activeProfile="local"; // 기본위치 local
		if(ObjectUtils.isNotEmpty(activeProfiles)) {
			activeProfile=activeProfiles[0];
		}
		String resourcePath=String.format("classpath:globals/global-%s.properties", activeProfile);
		try {
			Resource resource=resourceLoader.getResource(resourcePath);
			Properties properties=PropertiesLoaderUtils.loadProperties(resource);
			this.uploadFilePath=properties.getProperty("uploadFile.path");
			this.schedulerCronExample1 =properties.getProperty("scheduler.cron.example1");
			this.uploadResourcePath=properties.getProperty("uploadFile.resourcePath");
			this.local=activeProfile.equals("local");
			this.dev=activeProfile.equals("dev");
			this.prod=activeProfile.equals("prod");
			
		}catch (Exception e) {
			log.error("e", e);
		}
		
	}
	
	
	

	
}

 

 

WebMvcConfig

 

import java.util.List;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;

import kr.net.macaronics.configuration.handler.BaseHandlerInterceptor;
import kr.net.macaronics.utils.pagination.MySQLPageRequestHandleMethodArgumentResolver;
import lombok.extern.slf4j.Slf4j;

@Configuration
@Slf4j
public class WebMvcConfig implements WebMvcConfigurer {

		
	private static final String WINDOW_FILE ="file:///";
	private static final String LINUX_FILE= "file:";


	
	
/**	 addResourceHandlers 설명)
WebMvcConfigurer interface를 상속받아 addResourceHandlers method를 오버 라이딩하고 리소스 등록 및 핸들러를 관리하는 
객체인 ResourceHandlerRegistry를 통해 리소스의 위치와 리소스와 매칭 될 url을 설정.

addResourceHandler : 리소스와 연결될 URL path를 지정(클라이언트가 파일에 접근하기 위해 요청하는 url)
localhost:8080/imagePath/** 

addResourceLocations: 실제 리소스가 존재하는 외부 경로를 지정합니다.
경로의 마지막은 반드시 " / "로 끝나야 하고, 로컬 디스크 경로일 경우 file:/// 접두어를 꼭 붙인다.
 
이렇게 설정하면 클라이언트로부터 http://호스트 주소:포트/imagePath/testImage.jpg 와 같은 요청이 들어 왔을 때 /home/uploadedImage/testImage.jpg 파일로 연결.
 
 예)
	private String connectPath = "/imagePath/**";
	private String resourcePath = "file:///home/uploadedImage";

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler(connectPath)
                .addResourceLocations(resourcePath);
    }
*/
	
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		//업로드 파일 static resource 접근 경로
		String resourcePattern=config().getUploadResourcePath() +"**";
		log.info("addResourceHandlers  -resourcePattern   {}", resourcePattern);
		
		
		if(config().isLocal()) {
			//로컬(윈도우환경)			
			registry.addResourceHandler(resourcePattern)
			.addResourceLocations(WINDOW_FILE+config().getUploadFilePath());
			log.info("윈도우 환경");
			log.info(" resourcePattern -  {}", resourcePattern);
			log.info(" addResourceLocations -  {}", WINDOW_FILE+config().getUploadFilePath() );
		}if(config().isDev()) {
			//개발환경(윈도우환경)
			log.info("개발환경(윈도우환경)");
			registry.addResourceHandler(resourcePattern)
			.addResourceLocations(WINDOW_FILE+config().getUploadFilePath());
			log.info(" resourcePattern -  {}", resourcePattern);
			log.info(" addResourceLocations -  {}", WINDOW_FILE+config().getUploadFilePath() );
		}else {
			//리눅스 또는 유닉스 환경
			log.info("리눅스 또는 유닉스 환경");
			registry.addResourceHandler(resourcePattern)
			.addResourceLocations(LINUX_FILE+config().getUploadFilePath());
		}				
	}
	
}







 

 

 

11. 다운로드 

DownloadView

 

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.util.FileCopyUtils;
import org.springframework.web.servlet.view.AbstractView;


public class DownloadView extends AbstractView{
	public DownloadView() {
		setContentType("application/download; charset=utf-8");
	}

	@Override
	protected void renderMergedOutputModel(Map<String, Object> model,
			HttpServletRequest request, HttpServletResponse response) throws Exception {

		File file = (File)model.get("downloadFile");
		
		String orignalName = (String)model.get("orignalName");

		response.setContentType(getContentType());

		response.setContentLength((int)file.length());
		
		String fileName = null;
		
		if (orignalName != null && orignalName.trim().length() > 0) {
			fileName = orignalName;
		} else {
			fileName = file.getName();
		}
		
		fileName = URLEncoder.encode(fileName, "utf-8");
		
		fileName = fileName.replaceAll("\\+", "%20");
		response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\";");
		response.setHeader("Content-Transfer-Encoding", "binary");
		
		OutputStream out = response.getOutputStream();

		FileInputStream fis = null;

		try {
			fis = new FileInputStream(file);

			FileCopyUtils.copy(fis, out);

		} finally {
			if(fis != null) {
				try {
					fis.close();
				} catch(IOException e) {
					e.printStackTrace();
				}
			}

		}
		out.flush();
	}
}

 

 

12. Tika를 이용한 MimeType  파일 위조 변조 체크

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import org.apache.tika.Tika;
import org.springframework.web.multipart.MultipartFile;

import lombok.extern.slf4j.Slf4j;

/**업로드 파일체크
 * Tika를 이용한 MimeType  파일 위조 변조 체크
 *  **/
@Slf4j
public class FileFilter {

	private static final Tika tika = new Tika();
    
    
	 /** 이미지 파일체크 */
	public static boolean validImgFile(MultipartFile multipartFile) throws Exception {		
		try {
            List<String> notValidTypeList = Arrays.asList("image/gif", "image/jpeg", "image/png", "image/bmp");
            
            String mimeType = tika.detect(multipartFile.getInputStream());
            return notValidTypeList.stream().anyMatch(notValidType -> notValidType.equalsIgnoreCase(mimeType));
            
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
	}
		

	
	
	 /** 업로드 가능한 파일 MimeType 체크 */
	public static boolean isPermisionFileMimeType(MultipartFile multipartFile) throws Exception {		
		//MIME 타입의 전체 목록 - https://developer.mozilla.org/ko/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
		try {
            List<String> notValidTypeList = Arrays.asList(
            		"image/gif", "image/jpeg", "image/png", "image/bmp",
        			"application/pdf", "video/mp4" ,"application/zip" ,"application/x-zip-compressed" ,"application/x-tika-msoffice" ,     
        			 "video/quicktime"
            		);
            //application/x-tika-msoffice => hwp , msoffice 파일 
            //video/quicktime mp4 파일
            String mimeType = tika.detect(multipartFile.getInputStream());
            log.info("MimeType : " + mimeType);

            boolean isValid = notValidTypeList.stream().anyMatch(notValidType -> notValidType.equalsIgnoreCase(mimeType));

            return isValid;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
	}
}

 

 

 

13. 뷰

파일 등록

1) file_upload.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>파일업로드 테스트</title> 
{{>layout/head}}
<link rel="stylesheet" href="/css/file_upload.css"  >
</head>
<body>


	<div class="row mt-5" id="file-upload-container">
		<div class="col-md-4 offset-md-2 text-center">

			<div class="mb-5 d-flex justify-content-end">
				<a class="btn btn-info text-white" href="fileList">파일목록</a>
			</div>
			<h2>1.파일 업로드 테스트</h2>
			<form action="/file/save" enctype="multipart/form-data" method="post">
				<div class="input-group">
					<input type="file" class="form-control" name="uploadFile" id="uploadFile1" data-view="image_container1" onchange="setThumbnail(event);">
					<button class="btn btn-primary" type="button" id="uploadBtn1">파일업로드</button>
				</div>
			</form>
			<br>
			<div class="imgView" id="image_container1"></div>


			<br>
			<br>
			<br>
			<br>
			<br>

			<h2>2.파일 업로드 테스트2</h2>
			<form action="/file/fileSave" enctype="multipart/form-data" method="post">
				<div class="input-group">
					<input type="file" class="form-control" name="uploadFile" id="uploadFile2" data-view="image_container2" onchange="setThumbnail(event);">
					<button class="btn btn-primary" type="button" id="uploadBtn2">파일업로드</button>
				</div>
			</form>
			<br>
			<div class="imgView" id="image_container2"></div>


			<br>
			<br>
			<br>
			<br>
			<br>

			<h2>3.멀티 파일 업로드 테스트</h2>
			<form action="/file/fileSave" enctype="multipart/form-data" method="post">
				<div class="input-group">
					<input type="file" class="form-control" name="uploadFile" id="uploadFile3" data-view="image_container3" onchange="setThumbnail(event);" multiple>
					<button class="btn btn-primary" type="button" id="uploadBtn3">파일업로드</button>
				</div>
			</form>
			<br>
			<div class="imgView" id="image_container3"></div>


		</div>
	</div>

	{{>layout/footer}}



	<script src="/js/file_upload.js"></script>


</body>
</html>

 

2) file_upload.js

let fileUpload= {

	init:function(){
		document.querySelector("#uploadBtn1").addEventListener("click", function(event){
			fileUpload.uploadBtn1();	
		});
		
		document.querySelector("#uploadBtn2").addEventListener("click", function(event){
			fileUpload.uploadBtn2();	
		});
		
		document.querySelector("#uploadBtn3").addEventListener("click", function(event){
			fileUpload.uploadBtn3();	
		});
	},
	
	
	//1.파일 업로드 테스트
	uploadBtn1:function(){
		let formData = new FormData();
		const inputFile =document.getElementById("uploadFile1");		
		const files = inputFile.files;
		
		if(inputFile.value==undefined || inputFile.value==""){
			return;
		}
	
		//formdata에 파일 데이터 추가
		for(let i=0; i<files.length; i++){			
			if(!checkFileName(files[i].name, files[i].size)){
				console.log("file error");
				return false;
			}			
			formData.append("uploadFile", files[i]);
		}
		 	
		const params={
			method:"POST",
			body:formData
		} 	
		
		 	
		fetch('/api/file/save', params).then((response)=>response.json())
		.then((res)=>{	
			if(res.resState=="success"){					
					document.querySelector("#uploadFile1").value="";					
					document.querySelector("#image_container1").innerHTML="<textarea>"+JSON.stringify(res, null, 4) + "</textarea>";
					alert("업로드 처리 되었습니다.");
			}
		}).catch((error)=>{
			console.log("error:", error);
		})
		 		  	 		
	},
	
	
	
	//2.파일 업로드 테스트2
	uploadBtn2:function(){
		let formData = new FormData();
		const inputFile=document.getElementById("uploadFile2");
		const files = inputFile.files;
		if(inputFile.value==undefined || inputFile.value==""){
			return;
		}
		
		//formdata에 파일 데이터 추가
		for(let i=0; i<files.length; i++){			
			if(!checkFileName(files[i].name, files[i].size)){
				console.log("file error");
				return false;
			}			
			formData.append("uploadFile", files[i]);
		}
		 	
		 		
 		$.ajax({
 		    type:"POST",
 		    url : '/api/file/fileSave',
 		    processData: false,
 		    contentType: false,
 		    data: formData,
 		    success: function(res){
 		    	if(res.resState=="success"){
					document.querySelector("#uploadFile2").value="";
					document.querySelector("#image_container2").innerHTML="<textarea>"+JSON.stringify(res, null, 4) + "</textarea>";
					alert("업로드 처리 되었습니다.");
				}
 		    },
 		    error: function(err){
 		      console.log("err:", err)
 		    }
 		  }) 		 		
	}
	,
	
	//3.멀티 파일 업로드 테스트
	uploadBtn3:function(){
		let formData = new FormData();
		const inputFile = document.getElementById("uploadFile3");
		const files = inputFile.files;
		if(inputFile.value==undefined || inputFile.value==""){
			return;
		}
		//formdata에 파일 데이터 추가
		for(let i=0; i<files.length; i++){			
			if(!checkFileName(files[i].name, files[i].size)){
				console.log("file error");
				return false;
			}			
			formData.append("uploadFile", files[i]);
		}
		 
		 
		const params={
			method:"POST",
			body:formData
		} 	
			
		fetch('/api/file/multiFileSave', params).then((response)=>response.json())
		.then((res)=>{	
			if(res.resState=="success"){					
					document.querySelector("#uploadFile3").value="";					
					document.querySelector("#image_container3").innerHTML="<textarea>"+JSON.stringify(res, null, 4) + "</textarea>";
					alert("업로드 처리 되었습니다.");
			}
		}).catch((error)=>{
			console.log("error:", error);
		})		 
		 		
 	}

	
}



//파일 확장자 체크 및 사이즈 체크
function checkFileName(str, fileSize){		 
	
	//1. 확장자 체크
    const ext =  str.split('.').pop().toLowerCase();
    const ableExts=['bmp' , 'hwp', 'jpg', 'pdf', 'png', 'xls', 'zip', 'pptx', 'xlsx', 'jpeg', 'doc', 'gif' ,'mp4'];
    
    
    if( ableExts.indexOf(ext) ==-1  ) {	 
        //alert(ext);
        alert(ext+' 파일은 업로드 하실 수 없습니다.');
        return false;
    }	 
    //2. 파일명에 특수문자 체크
    const pattern =   /[\{\}\/?,;:|*~`!^\+<>@\#$%&\\\=\'\"]/gi;
    if(pattern.test(str) ){
        //alert("파일명에 허용된 특수문자는 '-', '_', '(', ')', '[', ']', '.' 입니다.");
        alert('파일명에 특수문자를 제거해주세요.');
        return false;
    }
    
	const maxSize = 1024*1024*17; //517MB
	if(fileSize >= maxSize){
		alert("파일 사이즈 초과");
		return false;
	}		
	return true;	
}

//이미지 미리보기
function setThumbnail(event) {
	const dataView=$(event.target).attr("data-view");
	
	document.querySelector("div#"+dataView).innerHTML="";    
    for (let image of event.target.files) {    	
    	// 확장자 체크
        const ext =  image.name.split('.').pop().toLowerCase();
        const ableExt=['jpg','png','jpeg','gif'];
        if(ableExt.indexOf(ext) > -1) {	
        	//console.log("이미지");
        	
       	  const reader = new FileReader();
       		
   	      reader.onload = function(event) {
   	        let img = document.createElement("img");
   	        img.setAttribute("src", event.target.result);
   	     	img.style.width="auto";
   	        img.style.height="100px";
   	        document.querySelector("div#"+dataView).appendChild(img);
   	      };
   	
   	      reader.readAsDataURL(image);    	                
        }	    		    
    }
}



fileUpload.init();




 

파일 목록

1) file_list.html

<!DOCTYPE html>
<html>
<head>
{{>layout/head}}
<title>파일목록</title>
<link rel="stylesheet" href="/css/file_upload.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@48,400,0,0" />
</head>
<body>

	<div class="container">
		<div class="row mt-5">
			<div class="col-md-12  text-center">

				<div class="mb-5 d-flex justify-content-end">
					<a class="btn btn-info text-white" href="fileUpload">파일등록</a>
				</div>


				<table class="table table-striped" id="fileUpload">
					<thead>
						<tr>
							<th colspan="8">
								<div id="displayCount" class="d-flex justify-content-end"></div>
							</th>
						</tr>
					</thead>
					<thead>
						<tr>
							<th>upload_file_seq</th>
							<th>thumbnail_resource_pathname</th>
							<th>board_seq</th>
							<th>board_type</th>
							<th>filename</th>
							<th>original_filename</th>
							<th>reg_date</th>
							<th>삭제</th>
						</tr>
					</thead>
					<tbody>
						<tr>
							<td class="text-center" colspan="8">등록된 데이터가 없습니다.</td>
						</tr>
					</tbody>
				</table>

				<ul id="pagingul">
				</ul>

			</div>
		</div>


		
	</div>

	{{>layout/footer}}

<script src="/js/file_list.js"></script>


</body>
</html>


 

 

2) file_list.js

 

let totalData; //총 데이터 수
let dataPerPage; //한 페이지에 나타낼 글 수
let pageCount; //페이징에 나타낼 페이지 수
let currentPage; //현재 페이지
let fileList = {
	
	init: function() {	
		this.list();	
	},

	list: function(page) {
		if (page == undefined || page == "") {
			page = 1;
		}

		fetch("/api/file/fileList?page=" + page)
		.then((response)=>response.json())
		.then(res=>{
			
			console.log(res.pageMaker);
			totalData = res.pageMaker.totalCount;
			dataPerPage = res.pageMaker.perPageNum;
			pageCount = res.pageMaker.displayPageNum;
			currentPage = res.pageMaker.page;

			if (res.resState == "success") {
				let html = "";
				res.data.forEach(function(item) {
					const regDate = item.regDate.replace('T', ' ').substring(0, 19);

					const ext=item.originalFilename.split('.').pop().toLowerCase();
					const ableExt=['jpg','png','jpeg','gif'];
					let img=`<span class="material-symbols-outlined" style="font-size:90px" >file_Open</span>`;
					if(ableExt.indexOf(ext) > -1) {
						if(item.thumbnailResourcePathname==null){
							img="썸네일 이미지 없음";
						}else{
							img=`<img  class="img-thumbnail rounded max-auto d-inline" src="${item.thumbnailResourcePathname}">`;
						}
						
					}

					html += `<tr>
			         <td>${item.uploadFileSeq}</td>
			         <td class="text-center">${img}</td>
			         <td>${item.boardSeq}</td>
			         <td>${item.boardType != null ? item.boardType.label : ""}</td>
			         <td>${item.filename}</td>
			         <td>${item.originalFilename}</td>                  
			         <td>${regDate}</td> 
			         <th><button class="btn btn-sm  btn-danger fileDeleteBtn" data-id="${item.uploadFileSeq}"  >삭제</button></th>   
			        </tr>  
			        `;
				});


				document.querySelector("tbody").innerHTML = html;
			}

			//페이징 표시 호출
			fileList.paging();
			
			//삭제 이벤트 추가
			let fileDeletes=document.querySelectorAll(".fileDeleteBtn");			
			for(let i=0; i<fileDeletes.length; i++){
				fileDeletes[i].addEventListener("click", function(e){
						fileList.fileDelete(e.target.getAttribute("data-id"));	
				});
			}
			
			
		}).catch(error=>console.log("error  :  {}", error));
		

	},


	//파일 삭제
	fileDelete:function(uploadFileSeq){			
			if(confirm("정말 삭제 하시겠습니까?")){
				console.log("파일 삭제 {} ",uploadFileSeq);	
				
				fetch("/api/file/fileDelate/"+uploadFileSeq,{
					method:"DELETE"		
				})
				.then((response)=>response.json())
				.then((res)=>{	
							
					if(res.resState==="success"){	
						currentPage=1;
						fileList.list();
					}						
				});			
				
			}
	},



	// 페이징 표시 함수 
	 //function(totalData, dataPerPage, pageCount, currentPage)
	paging: function(){
		console.log("currentPage : " + currentPage);

		totalPage = Math.ceil(totalData / dataPerPage); //총 페이지 수

		if (totalPage < pageCount) {
			pageCount = totalPage;
		}

		let pageGroup = Math.ceil(currentPage / pageCount); // 페이지 그룹
		let last = pageGroup * pageCount; //화면에 보여질 마지막 페이지 번호

		if (last > totalPage) {
			last = totalPage;
		}

		let first = last - (pageCount - 1); //화면에 보여질 첫번째 페이지 번호
		let next = last + 1;
		let prev = first - 1;

		let pageHtml = "";

		if (prev > 0) {
			pageHtml += "<li><a href='#' id='prev'> « </a></li>";
		}

		//페이징 번호 표시 
		for (var i = first; i <= last; i++) {

			if (currentPage == i) {
				pageHtml +=
					"<li class='on'><a href='#' id='" + i + "'>" + i + "</a></li>";
			} else {
				pageHtml += "<li><a href='#' id='" + i + "'>" + i + "</a></li>";
			}
		}


		if (last < totalPage) {
			pageHtml += "<li><a href='#' id='next'> » </a></li>";
		}


		document.querySelector("#pagingul").innerHTML = pageHtml;

		let displayCount = "";
		displayCount = "현재 1 - " + totalPage + " (" + currentPage + "페이지) / " + totalData + "건";
		document.querySelector("#displayCount").innerText = displayCount;


		//페이징 번호 클릭 이벤트 
		const paginationClass = document.querySelectorAll("#pagingul li a");
		for (let i = 0; i < paginationClass.length; i++) {
			paginationClass[i].addEventListener("click", function(e) {
				e.preventDefault();

				let $id = this.getAttribute("id")
				selectedPage = this.innerText;

				console.log("선택한 페이지 ", selectedPage);
				if ($id == "next") selectedPage = next;
				if ($id == "prev") selectedPage = prev;
				fileList.list(selectedPage);
			});
		}

	}

}


//파일 삭제
function fileDelete(uploadFileSeq){
	alert(uploadFileSeq);
}

fileList.init();




 

 

 

 

 

소스 :

https://github.com/braverokmc79/sprig_boot_2.7.0_mybatis_board_project/commit/e15c894dfcd4655c224d541e367a34f84ca9644b

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

나중에야 삼수 갑산을 갈지라도 , 결과가 최악에 이르는 한이 있더라도 우선 단행하거나 저질러 놓고 본다는 말.

댓글 ( 4)

댓글 남기기

작성