ํŒŒ์ผ๊ณผ ํด๋”๊ฐ€ ๋“ค์–ด์žˆ๋Š” ํด๋” DIR ๊ตฌ์กฐ

[zipDirDownLoad]

๋Œ€์ƒ ํด๋”๋ฅผ ์••์ถ•ํ•ด zip ํŒŒ์ผ๋กœ ์ƒ์„ฑ(compressDir, compressFile)ํ›„, ์ƒ์„ฑํ•œ zip ํŒŒ์ผ์„ response์— ๋‹ด์•„์„œ client์— ๋ณด๋‚ธ๋‹ค.

package com.study.controller;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.io.File;

   /**
    * @param targetDirPath ์••์ถ•ํ•  ํด๋” ๊ฒฝ๋กœ
    * @param outputPath ์ถœ๋ ฅํŒŒ์ผ ๊ฒฝ๋กœ
    * @param outputFileName ์ถœ๋ ฅํŒŒ์ผ๋ช…
    * @description ํด๋” ์••์ถ• ๋ฉ”์†Œ๋“œ
    */
    @SuppressWarnings("resource")
    protected void zipDirDownLoad(HttpServletResponse response, String targetDirPath, String resultPath, String resultFileName) throws Exception {
        // ํŒŒ์ผ๋ช…์— .zip์ด ์—†๋Š” ๊ฒฝ์šฐ, .zip ์„ ๋ถ™์—ฌ์ค€๋‹ค.
        int pos = resultFileName.lastIndexOf(".") == -1 ? resultFileName.length() : resultFileName.lastIndexOf(".");
        if (!resultZipName.substring(pos).equalsIgnoreCase(".zip")) {
            resultFileName += ".zip";
            }
        
        // ์••์ถ• ๋Œ€์ƒ ํŒŒ์ผ ์กด์žฌ ์—ฌ๋ถ€์ฒดํฌ
        File targetDir = new File(targetDirPath);
        if (!targetDir.exists()) {
            response.getWriter().println("<script>alert('File Not Found');history.back();</script>");
            logger.error("TargetDir does not exist.");
            }

        // ํŒŒ์ผ์ถœ๋ ฅ ์ŠคํŠธ๋ฆผ
        FileOutputStream fos = null;
        // ์••์ถ•ํŒŒ์ผ์ถœ๋ ฅ ์ŠคํŠธ๋ฆผ
        ZipOutputStream zos = null;
        try {
            fos = new FileOutputStream(new File(resultPath + resultFileName));  // ํŒŒ์ผ ๊ฐ์ฒด(new File(resultPath + resultFileName))์— ์“ธ ํŒŒ์ผ์ถœ๋ ฅ ์ŠคํŠธ๋ฆผ
            zos = new ZipOutputStream(fos); // zip output stream
            
            // ๋””๋ ‰ํ† ๋ฆฌ ๊ฒ€์ƒ‰๋ฅผ ํ†ตํ•œ ํ•˜์œ„ ํŒŒ์ผ๊ณผ ํด๋” ๊ฒ€์ƒ‰ ๋ฐ ์••์ถ•
            compressDir(targetDir, targetDir.getPath(), zos);
            
            } finally {
                if (zos != null) zos.close();
                if (fos != null) fos.close();
                }
        
        // ์••์ถ• ํŒŒ์ผ์„ response๋กœ ๋ณด๋‚ด๊ธฐ
        File resultZipFile = new File(resultPath + resultFileName);
        FileInputStream fis = new FileInputStream(resultZipFile);
        
        // ํŒŒ์ผ๋ช… ์ธ์ฝ”๋”ฉ ์„ค์ •
        String userAgent = request.getHeader("User-Agent");
        if(userAgent.contains("Edge") || userAgent.contains("MSIE") || userAgent.contains("Trident")) {
            resultZipName = URLEncoder.encode(resultZipName, "UTF-8").replace("\\+", "%20");
        } else if(userAgent.contains("Chrome") || userAgent.contains("Opera") || userAgent.contains("Firefox")) {
            resultZipName = new String(resultZipName.getBytes("UTF-8"), "ISO-8859-1");
        }
        
        byte[] byteFile = new byte[(int) resultZipFile.length()];
        fis.read(byteFile);    // ์ฃผ์–ด์ง„ ๋ฐฐ์—ด byteFile๋งŒํผ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์„œ byteFile์— ์ €์žฅํ•˜๊ณ  ์ฝ์€ ๋ฐ”์ดํŠธ ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜
        
        response.setContentType("application/zip");
        response.setContentLength(byteFile.length);
        
        response.setHeader("Content-Disposition",  "attachment; filename=\""+resultFileName+"\"");
        response.getOutputStream().write(byteFile); // ์ฃผ์–ด์ง„ ๋ฐฐ์—ด byteFile์— ์ €์žฅ๋œ ๋ชจ๋“  ๋‚ด์šฉ์„ ์ถœ๋ ฅ์†Œ์Šค์— ์“ด๋‹ค.
        
        if(fis != null) fis.close();
        if(resultZipFile.exists()) resultZipFile.delete();
    }

 

[ compressDir ]

๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ํŒŒ์ผ ์ธ์ž๋กœ ๋ฐ›์•„์„œ ๋‚ด์šฉ ํŒŒ์ผ๋“ค์˜ ๋””๋ ‰ํ† ๋ฆฌ์—ฌ๋ถ€์— ๋”ฐ๋ผ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ

    /**
     * @param file ํ˜„์žฌ ํŒŒ์ผ
     * @param resultRootPath ๋ฃจํŠธ ๊ฒฝ๋กœ
     * @param zos  ์••์ถ• ์ŠคํŠธ๋ฆผ
     * @description ๋””๋ ‰ํ† ๋ฆฌ ํƒ์ƒ‰ ๋ฐ ๋ถ„๊ธฐ
     */
    private void compressDir(File file, String resultRootPath, ZipOutputStream zos) throws Exception {
        // ์ธ์ž๋กœ ์ฃผ์–ด์ง„ ํŒŒ์ผ์ด ๋””๋ ‰ํ† ๋ฆฌ์ธ์ง€ ํŒŒ์ผ์ธ์ง€์— ๋”ฐ๋ผ ๋ถ„๊ธฐ
        if (file.isDirectory()) {
            // ๋””๋ ‰ํ† ๋ฆฌ์ผ ๊ฒฝ์šฐ ์žฌ๊ท€
            File[] files = file.listFiles();
            for (File f : files) {
                compressDir(f, resultRootPath, zos);
                }
            file.delete();
            } else {    // ํŒŒ์ผ์ผ ๊ฒฝ์šฐ ์••์ถ•์„ ํ•œ๋‹ค.
                compressFile(file, resultRootPath, zos);
                }
	}

 

[ compressFile ]

๋ถ„๊ธฐ์ฒ˜๋ฆฌ ์ค‘ ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ์•„๋‹Œ ํŒŒ์ผ์ผ ๊ฒฝ์šฐ ์‹ค์งˆ์ ์œผ๋กœ ์••์ถ• ์ˆ˜ํ–‰

    /**
     * @param file ํ˜„์žฌ ํŒŒ์ผ
     * @param resultRootPath ๋ฃจํŠธ ๊ฒฝ๋กœ
     * @param zos  ์••์ถ• ์ŠคํŠธ๋ฆผ
     * @description ํŒŒ์ผ ์••์ถ• ๋ฉ”์„œ๋“œ
     */
    private void compressFile(File file, String resultRootPath, ZipOutputStream zos) throws Exception {
        FileInputStream fis = null;
        try {
            String zipFileName = file.getPath().replace(resultRootPath + "\\", "");
            // ํŒŒ์ผ์„ ์ฝ์–ด๋“ค์ž„
            fis = new FileInputStream(file);
            // Zip์—”ํŠธ๋ฆฌ ์ƒ์„ฑ
            ZipEntry zipentry = new ZipEntry(zipFileName);  // zipFileName์„ ์ด๋ฆ„์œผ๋กœ ๊ฐ€์ง€๋Š” zipEntry ์ƒ์„ฑ
            // ์ŠคํŠธ๋ฆผ์— ๋ฐ€์–ด๋„ฃ๊ธฐ(์ž๋™ ์˜คํ”ˆ)
            zos.putNextEntry(zipentry);         // zip entry๋ฅผ ์“ฐ๊ณ , ์—”ํŠธ๋ฆฌ ๋ฐ์ดํ„ฐ ์‹œ์ž‘์— stream์„ ์œ„์น˜์‹œํ‚ด
            int length = (int) file.length();   // ํŒŒ์ผ์˜ ๊ธธ์ด
            byte[] buffer = new byte[length];   // ํŒŒ์ผ์˜ ๊ธธ์ด๋งŒํผ์˜ ๋ฒ„ํผ
            fis.read(buffer, 0, length);        // ์ตœ๋Œ€ length๊ฐœ์˜ byte๋ฅผ ์ฝ์–ด์„œ, ๋ฐฐ์—ด buffer์˜ ์ง€์ •๋œ ์œ„์น˜(0)๋ถ€ํ„ฐ ์ €์žฅํ•˜๊ณ  ์ฝ์€ ๋ฐ”์ดํŠธ ์ˆ˜ ๋ฐ˜ํ™˜
            zos.write(buffer, 0, length);       // ์ฃผ์–ด์ง„ ๋ฐฐ์—ด buffer์— ์ €์žฅ๋œ ๋‚ด์šฉ ์ค‘์—์„œ 0๋ฒˆ์งธ ๋ถ€ํ„ฐ length๋งŒํผ๋งŒ์„ ์ฝ์–ด์„œ ์ถœ๋ ฅ์†Œ์Šค์— ์“ด๋‹ค.
            zos.closeEntry();                   // ํ˜„์žฌ zip entry๋ฅผ ๋‹ซ๊ณ  ๋‹ค์Œ ์—”ํŠธ๋ฆฌ ์ž‘์„ฑ์„ ์œ„ํ•ด ์œ„์น˜ํ•จ 
            } finally {
                if (fis != null) fis.close();
                if (file != null && file.exists())file.delete();
                }
	}

๋ฐ˜์‘ํ˜•

[๊ฐœ๋ฐœ ํ™˜๊ฒฝ]

์„œ๋ฒ„: Spring Boot 3.2.4

ํ”„๋ก ํŠธ: Vite + React + TypeScript

 

<MyController.java>

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class MyController {

    @RequestMapping(value = "/gnbmenu")
    public String gnbMenu(@RequestBody Object request) {
        String str = "{\"message\" : \"๋ฉ”๋‰ด ์š”์ฒญ url.\"}";
        return str;
    }

}

 

<MyFront.tsx>

import { useEffect } from "react";

function MyFront() {
  const dataToSend = {
    key1: "value1",
    key2: "value2",
  };
  useEffect(() => {
    fetch("/api/gnbmenu", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(dataToSend),
    }).then((response) => console.log(response.json()));
  }, []);

  return <></>;
export default MyFront;

 

<Server Console>

2024-04-18T20:10:29.643+09:00 ERROR 26416 --- [****] [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.thymeleaf.exceptions.TemplateInputException: Error resolving template [{"message" : "๋ฉ”๋‰ด ์š”์ฒญ url."}], template might not exist or might not be accessible by any of the configured Template Resolvers] with root cause

org.thymeleaf.exceptions.TemplateInputException: Error resolving template [{"message" : "๋ฉ”๋‰ด ์š”์ฒญ url."}], template might not exist or might not be accessible by any of the configured Template Resolvers
	at org.thymeleaf.engine.TemplateManager.resolveTemplate(TemplateManager.java:869) ~[thymeleaf-3.1.2.RELEASE.jar:3.1.2.RELEASE]
	at org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:607) ~[thymeleaf-3.1.2.RELEASE.jar:3.1.2.RELEASE]
	at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1103) ~[thymeleaf-3.1.2.RELEASE.jar:3.1.2.RELEASE]
	at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1077) ~[thymeleaf-3.1.2.RELEASE.jar:3.1.2.RELEASE]
	at org.thymeleaf.spring6.view.ThymeleafView.renderFragment(ThymeleafView.java:372) ~[thymeleaf-spring6-3.1.2.RELEASE.jar:3.1.2.RELEASE]
	at org.thymeleaf.spring6.view.ThymeleafView.render(ThymeleafView.java:192) ~[thymeleaf-spring6-3.1.2.RELEASE.jar:3.1.2.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1431) ~[spring-webmvc-6.1.5.jar:6.1.5]
	at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1167) ~[spring-webmvc-6.1.5.jar:6.1.5]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1106) ~[spring-webmvc-6.1.5.jar:6.1.5]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[spring-webmvc-6.1.5.jar:6.1.5]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-6.1.5.jar:6.1.5]
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914) ~[spring-webmvc-6.1.5.jar:6.1.5]
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590) ~[tomcat-embed-core-10.1.19.jar:6.0]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.1.5.jar:6.1.5]
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[tomcat-embed-core-10.1.19.jar:6.0]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.1.5.jar:6.1.5]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.1.5.jar:6.1.5]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.1.5.jar:6.1.5]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) ~[tomcat-embed-core-10.1.19.jar:10.1.19]
	at java.base/java.lang.Thread.run(Thread.java:842) ~[na:na]

 

<Client console>

MyFront.tsx:36 
 POST http://localhost:5173/api/gnbmenu 500 (Internal Server Error)
(anonymous)	@	MyFront.tsx:36
Show 10 more frames

 

[์˜ˆ์ƒ ๋ฌธ์ œ 1]

๊ฒฝ๋กœ ์„ค์ • ๋ฌธ์ œ?

"/abc"๋กœ ์‹œ์ž‘ํ•˜๋Š” ๊ฒฝ๋กœ์—์„œ "/"์„ ์ œ๊ฑฐํ•˜๊ณ  "abc"ํ˜•ํƒœ๋กœ ์ˆ˜์ • โ˜ž AWS๋‚˜ ๋ฐฐํฌ ํ™˜๊ฒฝ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋กœ ํ•ด๋‹น ๋ฌธ์ œ๊ฐ€ ์•„๋‹˜

 

[๋ฌธ์ œ 2]

Controller Annotaion ์ข…๋ฅ˜์˜ ๋ฌธ์ œ

@Controller๋Š” ๋ฐ˜ํ™˜๊ฐ’์œผ๋กœ view๋ฅผ ์ฐพ์Œ. view๊ฐ€ ์•„๋‹Œ jsonํ˜•ํƒœ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— @RestController๋กœ ์„ค์ • โ˜ž ํ•ด๊ฒฐ

 

๋ฐ˜์‘ํ˜•
package com.study.controller;

import org.apach.commons.io.FileUtils;
import org.springFamework.http.HttpHeaders;
import org.springFamework.http.HttpStatus;
import org.springFamework.http.MediaType;
import org.springFamework.http.ResponseEntity;


@Controller
public class AttachFileController {

	@RequestMapping("/pdfView")
    public ResponseEntity<byte[]> pdfView(HttpServletRequest request, HttpServletResponse) throws Exception{
    	request.setCharacterEncoding("utf-8");
        String fileId = new String(request.getParameter("fileId".getBytes("8859_1", "KSC5601");
        // fileId ์—†๋Š” ๊ฒฝ์šฐ ์˜ˆ์™ธ์ฒ˜๋ฆฌ
        // fileId์— ํ•ด๋‹นํ•˜๋Š” ํŒŒ์ผ ์—†๋Š” ๊ฒฝ์šฐ ์˜ˆ์™ธ์ฒ˜๋ฆฌ
              
        String filePath = ${FILES_ROOT_PATH} + fileId;
        String tempFile = new File(filePath);
        byte[] pdfBytes;
        
        try {
        	pdfBytes = org.apache.commons.io.FileUtils.readFileToByteArray(tempFile);
        } catch (Exception e){
        	e.printStackTrace();
            String error = "<script tye='text/javascript'> alert('File Not Found');</script>";
            byte[] arrorBytes = error.getBytes(StandardCharsets.UTF_8);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorBytes);
            }
            
        HttpHeaders = headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_PDF);
        headers.setContentDispositionFormData("filename", "example.pdf");
        headers.setContentLength(pdfBytes.length);
        //ํŒŒ์ผ์ด ๋‹ค์šด๋กœ๋“œ ๋˜์ง€ ์•Š๊ณ  ๋ธŒ๋ผ์šฐ์ € ๋‚ด์— ๋ณด์ด๋„๋ก ํ•จ
        headers.set("Content-Disposition", "inline; filename=example.pdf"
        
        return new ResponseEntity<>(pdfBytes, headers, HttpStatus.OK);
    }

}
๋ฐ˜์‘ํ˜•

+ Recent posts