BACKEND/JAVA & SPRING

MultipartFile์™€ Ajax๋ฅผ ํ™œ์šฉํ•œ ํŒŒ์ผ ์—…๋กœ๋“œ

์ด-ํ”„ 2024. 8. 1. 15:48

๐Ÿ’ก Issue

  • Type์„ File๋กœ ์„ ํƒ์‹œ, ๋กœ์ปฌ์—์„œ ํŒŒ์ผ์„ ์„œ๋ฒ„๋กœ ์ „์†กํ•˜์—ฌ ์ €์žฅํ•˜๋Š” ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜๋Š” ์ค‘์ด๋‹ค.
  • ํŒŒ์ผ์„ ์„œ๋ฒ„๋กœ ์ „์†กํ•  ๋•Œ, ์ฃผ๋กœ Multipart-File์„ ์ž์ฃผ ์‚ฌ์šฉํ•œ๋‹ค.

 

๐Ÿ’ก Multipart-File์ด๋ž€?

  • ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„๊ฐ„์— ์ „์†ก๋˜๋Š” HTTP ์š”์ฒญ-์‘๋‹ต์—์„œ ์ฃผ๋กœ ํŒŒ์ผ ์—…๋กœ๋“œ์™€ ๊ด€๋ จํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•  ๋•Œ ์‚ฌ์šฉ๋œ๋‹ค.
  • HTTP๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ํ…์ŠคํŠธ ๊ธฐ๋ฐ˜ (JSON)์œผ๋กœ ์š”์ฒญ-์‘๋‹ตํ•˜์ง€๋งŒ, ํŒŒ์ผ๊ณผ ๊ฐ™์€ ์ด์ง„ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•ด์•ผํ•  ๋•Œ๋Š”, ์ด์ง„ ๋ฐ์ดํ„ฐ๋ฅผ ํ…์ŠคํŠธ ํ˜•์‹์œผ๋กœ ์ธ์ฝ”๋”ฉํ•˜๋Š” ๊ณผ์ •์ด ๋น„ํšจ์œจ์ ์ด๋ฉฐ ์ œํ•œ์ ์ด๋‹ค.
  • ๐Ÿ”‘ Multipart๋Š” ์ด๋Ÿฌํ•œ ์ด์ง„๋ฐ์ดํ„ฐ(ํŒŒ์ผ)์„ ์ธ์ฝ”๋”ฉํ•˜์ง€ ์•Š๊ณ , ์›๋ณธ ํ˜•์‹ ๊ทธ๋Œ€๋กœ ์ „์†กํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ด๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด, HTTP Content-Type Header์— ‘multipart/form-data’ ํ˜•์‹์œผ๋กœ ์ „์†กํ•˜๋ฉด ๋œ๋‹ค.

 

๐Ÿ’ก Mutlipart Streaming ๋ฐฉ์‹์˜ ์ €์žฅ๋ฐฉ๋ฒ•

  • ์ „์ฒด ๋ฐ์ดํ„ฐ ์ˆ˜์‹ 
    • ์„œ๋ฒ„ → ํด๋ผ์ด์–ธํŠธ๋กœ ์ „์ฒด ๋ฉ€ํ‹ฐํŒŒํŠธ ๋ฐ์ดํ„ฐ ์ˆ˜์‹ 
  • ๋ฉ”๋ชจ๋ฆฌ/๋””์Šคํฌ์— ์ €์žฅ
    • ์ˆ˜์‹ ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ๋‚˜ ์ž„์‹œ ๋””์Šคํฌ ํŒŒ์ผ์— ์ €์žฅ
  • ๋ถ„์„ ๋ฐ ์ฒ˜๋ฆฌ
    • ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ํŒŒ์‹ฑํ•˜์—ฌ ๊ฐœ๋ณ„ ํŒŒํŠธ๋กœ ๋ถ„๋ฆฌํ•˜๊ณ , ๊ฐ ํŒŒํŠธ๋ฅผ ์ฒ˜๋ฆฌ (multipart์ธ ์ด์œ )

 

๐Ÿ’ก ํ”„๋กœ์ ํŠธ ์ฝ”๋“œ ๋ถ„์„ - AJAX๋กœ ๊ตฌํ˜„ (FORM ํƒœ๊ทธ X)

  • uploadFile (AJAX)
    	function uploadFile() {
    			let targetPathInput = document.getElementById("target_path");
                const fileInput = document.getElementById('file-upload');
                const file = fileInput.files[0];
                const formData = new FormData();
                formData.append('file', file);
    
                fetch('/upload-file', {
                    method: 'POST',
                    body: formData 
                })
                .then(response => response.json())
                .then(data => {
                    if (data.message.includes("์„ฑ๊ณต")) {
                        document.getElementById('file_path').value = data.data;
                        targetPathInput.value = data.data;
                    } else {
                        alert('ํŒŒ์ผ ์—…๋กœ๋“œ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: ' + data.message);
                    }
                })
                .catch(error => {
                    console.error('Error:', error);
                    alert('ํŒŒ์ผ ์—…๋กœ๋“œ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.');
                });
            }
    • fetch์˜ body๋ฅผ formData๋กœ ์„ค์ •์‹œ, ์ž๋™์œผ๋กœ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ multipart/form-data ํ˜•์‹์œผ๋กœ ์ธ์ฝ”๋”ฉํ•˜์—ฌ ์ „์†กํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Service (back)
    	/**
    	 * ์‚ฌ์šฉ์ž์˜ ๋กœ์ปฌ ํŒŒ์ผ์„ ์„œ๋ฒ„์— ์ €์žฅ
    	 * 
    	 * @param file ์‚ฌ์šฉ์ž์˜ ๋กœ์ปฌ ํŒŒ์ผ 
    	 */
    	public String saveFileInServer(MultipartFile file) {
    		
    		String saveFilePath = System.getProperty("user.dir") + TARGET_SAVE_PATH;
    		String fileName = file.getOriginalFilename();
    		String fileNameWithoutExtension = fileName.substring(0, fileName.lastIndexOf("."));
    		String fullPath = saveFilePath + fileName;
    		try {
    			if(isValidDirectoryPath(fullPath)) {
    				throw new DuplicatedFileException(ErrorCode.DUPLICATED_FILE_EXCEPTION);
    			}
    			if(!file.isEmpty() && isFileTar(fileName)) {
    				file.transferTo(new File(fullPath));
    			} else if(!isFileTar(file.getOriginalFilename())) {
    				throw new FailedToSaveLocalFileException(ErrorCode.NOT_TAR_EXTENSION);
    			}
    			return fileNameWithoutExtension;
    		} catch (IllegalStateException | IOException e) {
    			throw new  FailedToSaveLocalFileException(ErrorCode.FAILED_TO_SAVE_LOCAL_FILE);
    		}
    	}
    
    • AJAX๋ฅผ ํ†ตํ•ด ํ˜ธ์ถœ๋  ๋•Œ, ์‚ฌ์šฉ์ž๊ฐ€ ์—…๋กœ๋“œํ•œ MultipartFile ํƒ€์ž…์˜ ํŒŒ์ผ์„ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๊ฐ–๋Š”๋‹ค.
    • ์ €์žฅํ•˜๊ณ ์ž ํ•˜๋Š” ๊ฒฝ๋กœ (fullPath)์— ์˜ˆ์™ธ์ฒ˜๋ฆฌ์— ํ•ด๋‹น๋˜์ง€ ์•Š์„ ๊ฒฝ์šฐ(์˜ณ์€ ์กฐ๊ฑด) transfer๋ฅผ ํ†ตํ•ด ์ €์žฅํ•œ๋‹ค.