1. 개요
스프링 프레임워크 기반으로 개발을 하면서 파일 다운로드를 할 때
다양한 방법으로 적용할 수 있어서 볼 때마다 새로이 다시 공부하는 느낌이였다.
오늘은 내가 보기 편하게 찾을 수 있도록
Http프로토콜에서 파일 다운로드를 하는 방법을 정리하고자 한다.
※참고로 구현 가능한 기술들의 동향이 달라지기 때문에 변수가 존재하는 점을 참고해야 한다.
2. 내장 기능을 이용한 구현 방법
- 스프링 내장 기능을 이용한 다운로드 방법
스프링 프레임워크에서 HttpHeaders와 ResponseEntity를 이용하여 구현한다.
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
@Controller
public class FileDownloadController {
@GetMapping("/download/{fileName}")
public ResponseEntity<FileSystemResource> downloadFile(@PathVariable String fileName) throws IOException {
//실제 파일의 경로를 읽어서 File객체를 생성한다
File file = new File("your_directory_path/" + fileName);
//응답 헤더 설정을 위한 HttpHeaders객체를 생성한다.
HttpHeaders headers = new HttpHeaders();
//생성된 응답 헤더 객체의 컨텐츠 타입을 바이너리 데이터로 설정한다.
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
/*응답의 Content-Disposition헤더를 설정한다.
headers.setContentDispositionFormData(응답형태, 파일명)
- param1: attachment는 응답을 첨부파일로 처리한다.
- param2: fileName는 첨부파일의 명칭을 직접 지정한다.*/
headers.setContentDispositionFormData("attachment", fileName);
/*파일 다운로드를 위한 ResponseEntity객체를 생성 및 반환한다.
new ResponseEntity<>(다운로드할 파일, 응답 헤더, 응답 상태코드);
- param1: new FileSystemResource(file)는 다운로드할 파일을 읽어온다.
- param2: headers는 설정된 HttpHeaders 응답 헤더
- param3: org.springframework.http.HttpStatus.OK는 응답 상태가 성공적임을 나타내는 200(OK)코드
*/
return new ResponseEntity<>(new FileSystemResource(file), headers, org.springframework.http.HttpStatus.OK);
}
}
스프링 프레임워크에서 StreamingResponseBody를 클라이언트에게 데이터를 스트리밍하도록 구현한다.
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
@RestController
public class FileDownloadController {
@GetMapping("/downloadStreamingResponseBody/{fileName}")
public StreamingResponseBody downloadFileWithStreamingResponseBody(@PathVariable String fileName) throws IOException {
//실제 경로를 읽어서 다운로드할 파일을 나타내는 File객체를 생성한다.
File file = new File("your_directory_path/" + fileName);
//파일을 읽기 위한 FileInputStream객체를 생성한다.
FileInputStream fileInputStream = new FileInputStream(file);
/*파일을 읽고 해당 데이터를 클라이언트로 전송하기위해 사용되는 outputStream객체를 사용한다.
파일을 4096바이트 크기의 버퍼를 사용하여 읽어들이고, outputStream을 통해 클라이언트에게 전송한다.
그리고 파일을 모두 읽은 후에는 fileInputStream을 닫아준다.
*/
return outputStream -> {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = fileInputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
fileInputStream.close();
};
}
}
- 스프링 부트(SPRING BOOT)를 이용한 다운로드 방법
스프링 부트를 이용하면 스프링을 이용한 구현 방법이 더 간결해진다.
@RestController
public class FileDownloadController {
@GetMapping("/downloadSpringBoot/{fileName}")
public ResponseEntity<Resource> downloadFile(@PathVariable String fileName) throws IOException {
/*FileSystemResource를 사용하여 파일을 나타내는 Resource객체를 생성한다.
스프링에서 제공되는 Resource객체는 인터페이스를 구현하는 클래스로 파일시스템의 리소스를 나타낸다 */
Resource resource = new FileSystemResource("your_directory_path/" + fileName);
/*ResponseEntity를 생성하여 클라이언트에게 파일 다운로드 응답을 생성하고 --> 응답 헤더의 Content-Disposition에 따라 파일명과 다운로드방식을 지정하여 클라이언트에게 응답을 전송한다.
.ok()는 HTTP 상태 코드를 OK(200)으로 설정한다.
.header는 응답 헤더의 Content-Disposition을 설정한다
.body(resource)는 응답의 본문(body)에 'resource'를 설정한다.
*/
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
.body(resource);
}
}
- 아파치(Apache)를 이용한 다운로드 방법
아파치는 image, js, html, css등 정적인 리소스 파일만을 서비스하기 위한 웹 서버이다.
src/webapp 경로에 다운로드 파일을 지정시키고 해당 디렉토리 경로를 URL로 실행시키면 다운로드가 된다.
다만, 위의 경우 보안상의 문제가 발생할 수 있다는 점을 참고하자. (이 방법은 아파치를 이용)
그리고 src/webapp/WEB-INF 경로의 경우에는 직접 접근할 수 없고, 무조건 Controller를 거쳐야만 사용자가 접근할 수 있는 경로이다. (이 방법은 톰캣을 이용)
때문에 프로젝트 소스 내에 Temp파일을 다운로드 한다면 직접 다운로드할 수 있는(=보안에 위배되는) webapp에 지정하지 않고 WEB-INF에 지정시키고 Controller를 거쳐서 다운로드를 하는 방법이 바람직하다.
- 톰캣(Tomcat)을 이용한 다운로드 방법
톰캣은 주로 Java 웹 애플리케이션의 서버로 사용되어, 파일 다운로드는 서블릿을 사용하여 구현한다.
@WebServlet("/download")
public class FileDownloadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//먼저 다운로드할 파일의 경로를 지정한다.
String filePath = "your_directory_path/your_file.txt";
//다운로드할 파일을 나타내는 File객체를 생성한다.
File downloadFile = new File(filePath);
//FileInputStream객체를 사용하여 파일을 읽기 위한 스트림을 생성한다.
try (FileInputStream fis = new FileInputStream(downloadFile)) {
/*HTTP응답의 Content-type 헤더를 설정한다.
일반적으로 바이너리 데이터를 나타내는 application/octet-stream를 사용한다.*/
response.setContentType("application/octet-stream");
/*HTTP응답의 Content-Length 헤더를 설정한다.
다운로드할 파일의 크기를 지정한다.*/
response.setContentLength((int) downloadFile.length());
//Content-Disposition헤더를 설정하여 브라우저가 응답을 파일로 처리하도록 지시한다.
response.setHeader("Content-Disposition", "attachment; filename=\"" + downloadFile.getName() + "\"");
//HTTP응답의 출력 스트림을 얻는다.
try (OutputStream os = response.getOutputStream()) {
/*주어진 파일을 HTTP응답으로 전송하여
파일을 읽어서 버퍼에 담고 --> 버퍼의 내용을
출력 스트림(os)을 통해 클라이언트에게 전송하는 과정을 반복하여 파일의 모든 내용을 전송한다*/
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
}
System.out.println("여기까지 실행하는데 문제없으면 다운로드 성공");
}
}
}
- 자바 내장기능을 이용한 다운로드 방법
순수 자바 언어를 이용한 다운로드는 HttpConnection를 사용하여 구현한다.
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
public class FileDownloadExample {
public static void main(String[] args) {
//다운로드할 파일의 URL을 지정한다.
String fileURL = "https://example.com/your_file.txt";
//다운로드할 파일을 저장할 디렉토리를 지정한다.
String saveDir = "your_directory_path/";
try {
//다운로드할 파일의 URL을 URL객체로 변환한다.
URL url = new URL(fileURL);
//URL을 통해 연결을 열고, 연결에 대한 URLConnection객체를 얻는다.
URLConnection connection = url.openConnection();
//연결로부터 입력스트림을 얻어서 BufferedInputStream으로 감싸준다.
BufferedInputStream in = new BufferedInputStream(connection.getInputStream());
//다운로드한 데이터를 저장할 파일의 출력 스트림을 생성한다.
FileOutputStream fileOutputStream = new FileOutputStream(saveDir + "your_file.txt");
//데이터를 1024바이트씩 읽어오고 --> 읽어온 데이터를 파일 출력 스트림을 통해 저장한다.
byte[] dataBuffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
fileOutputStream.write(dataBuffer, 0, bytesRead);
}
System.out.println("여기까지 실행하는데 문제없으면 다운로드 성공");
//파일 출력 스트림과 입력 스트림을 닫는다.
fileOutputStream.close();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- Servlet 내장기능을 이용한 다운로드 방법
순수 자바 웹 애플리케이션을 이용한 다운로드 구현.
@WebServlet("/downloadServlet")
public class FileDownloadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*다운로드할 파일의 경로를 지정한다
경로는 Nas서버나 로컬에 따라서 달라질 수 있다*/
String filePath = "your_directory_path/your_file.txt";
//다운로드할 파일을 나타내는 File객체를 생성한다.
File downloadFile = new File(filePath);
//파일을 읽기 위한 FileInputStream을 생성한다.
try (FileInputStream fis = new FileInputStream(downloadFile);
//생성된 FileInputStream을 BufferedInputStream으로 감싼다.
BufferedInputStream bis = new BufferedInputStream(fis);
//클라이언트로 데이터를 출력하기 위한 OutputStream을 얻기 위해 try-with-resources를 사용한다.
OutputStream os = response.getOutputStream()) {
/*HTTP응답의 Content-Type헤더를 설정한다.
일반적으로 바이너리 데이터를 나타내는 application/octet-stream를 사용한다*/
response.setContentType("application/octet-stream");
//HTTP응답의 Content-Length헤더를 설정하여 다운로드할 파일의 크기를 지정한다.
response.setContentLength((int) downloadFile.length());
//Content-Disposition헤더를 설정하여 브라우저가 응답을 파일로 처리하도록 지시한다.
response.setHeader("Content-Disposition", "attachment; filename=\"" + downloadFile.getName() + "\"");
/*파일을 읽어서 버퍼에 담고 -> 버퍼의 내용을 출력 스트림을 통해 클라이언트에게 전송한다
이 과정을 반복하여 파일의 모든 내용을 클라이언트에게 전송한다.*/
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
System.out.println("여기까지 실행하는데 문제없으면 다운로드 성공");
}
}
}
3. 라이브러리를 이용한 구현 방법
- Apache Commons IO 라이브러리를 이용한 다운로드 방법
Maven 또는 Gradle에서 org.apache.commons:commons-io:2.11.0 라이브러리를 추가가 되어 있는 상태에서 FileUtils를 이용하여 구현한다.
import org.apache.commons.io.FileUtils;
public class FileDownloadUsingCommonsIO {
public static void main(String[] args) {
//다운로드 할 파일의 URL을 지정한다
String fileURL = "https://example.com/your_file.txt";
//다운로드한 파일을 저장할 경로를 지정한다.
String saveDir = "your_directory_path/";
//파일 다운로드 시 예외 처리를 위한 try 블록을 감싼다.
try {
/*Apache Commons IO 라이브러리의 FileUtils.copyURLToFile메서드를 사용한다.
copyURLToFile는 주어진 URL에서 파일을 다운로드하고, 지정된 경로에 저장하는 기능을 제공한다*/
FileUtils.copyURLToFile(new URL(fileURL), new File(saveDir + "your_file.txt"));
System.out.println("여기까지 실행하는데 문제없으면 다운로드 성공");
} catch (IOException e) {
e.printStackTrace();
}
}
}
- OkHttp 라이브러리를 이용한 다운로드 방법
OkHttp는 안드로이드 및 Java에서 HTTP통신을 쉽게 처리할 수 있는 라이브러리이다.
Maven 또는 Gradle에서 com.squareup.okhttp3:okhttp:4.9.0 라이브러리를 추가하여 구현한다.
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class FileDownloadUsingOkHttp {
public static void main(String[] args) {
//다운로드 할 파일의 URL을 지정한다.
String fileURL = "https://example.com/your_file.txt";
//다운로드한 파일을 저장할 경로를 지정한다.
String saveDir = "your_directory_path/";
/*OkHttpClient객체를 생성한다.
이는 Square에서 제공하는 OkHttp라이브러리를 사용하여 HTTP요청을 처리하는데 사용한다*/
OkHttpClient client = new OkHttpClient();
/*OkHttp의 Request.Builder를 사용하여 HTTP요청을 생성한다.
여기서 주어진 URL로 GET요청을 생성한다 */
Request request = new Request.Builder().url(fileURL).build();
/*OkHttp를 사용하여 서버에 HTTP요청을 보내고 -> 응답을 받아오기 위해 try-with-resources블록을 시작한다.
여기서는 execute() 메서드를 통해 동기적으로 요청을 실행하고 응답을 얻습니다*/
try (Response response = client.newCall(request).execute()) {
//응답(response)에서 바디를 얻는다.
ResponseBody body = response.body();
//응답 바디가 null이 아닌 경우에만 코드 블록을 실행합니다.
if (body != null) {
/*응답 바디에서 InputStream을 얻어 파일을 읽는다.
그리고 FileOutputStream을 사용하여 파일을 저장할 try-with-resources 블록을 시작한다*/
try (InputStream inputStream = body.byteStream();
FileOutputStream fileOutputStream = new FileOutputStream(saveDir + "your_file.txt")) {
/*파일을 읽어서 (4096크기의 바이트 단위) 버퍼에 담는다.
그리고 버퍼의 내용을 출력 스트림을 통해 클라이언트에게전송한다.
이 과정을 반복하여 파일의 모든 내용을 클라이언트에게 전송한다. */
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, bytesRead);
}
System.out.println("여기까지 실행하는데 문제없으면 다운로드 성공");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
4. 멀티(압축) 다운로드 방법
- 스프링을 이용한 (여러개를 전송) 다운로드 방법1
서버에서 여러 개의 파일을 클라이언트에게 직접 전송하는 방식
다시 말하자면,
서버에서는 클라이언트에게 다건 파일을 전송하고 -> 브라우저에서 응답헤더에 의해 압축형태로 내려받는 방식이다.
하지만, 상황에 따라 다건을 낱개로 내려받을 수 있다.
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Controller
public class MultiFileDownloadController {
@GetMapping("/multiDownload")
public ResponseEntity<List<FileSystemResource>> multiFileDownload() throws IOException {
/*FileSystemResource 객체를 리스트에 추가하는 부분이다.
다운로드할 여러 파일을 resources 리스트에 추가한다.
여기서는 "file1.txt" 및 "file2.txt" 파일을 추가하고 있다.*/
List<FileSystemResource> resources = new ArrayList<>();
resources.add(new FileSystemResource("your_directory_path/file1.txt"));
resources.add(new FileSystemResource("your_directory_path/file2.txt"));
/*응답 헤더를 설정.
Content-Type을 바이너리 데이터로 지정하고,
Content-Disposition을 설정하여 브라우저가 응답을 "files.zip"으로 처리하도록 지시한다*/
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", "files.zip");
/*ResponseEntity를 사용하여 HTTP 응답을 생성한다.
headers로 설정한 응답 헤더와 다운로드할 파일 목록(resources)을 응답 본문으로 설정한다.
ok() 메서드로 HTTP 상태 코드를 200 (OK)로 설정한다.*/
return ResponseEntity.ok()
.headers(headers)
.body(resources);
}
}
- 스프링을 이용한 (한번에 처리) 다운로드 방법2
여러 파일을 서버에서 압축하여 클라이언트에게 압축 파일 하나를 전송하는 방식.
다시 말하자면,
서버에서는 클라이언트에게 다건 파일을 압축 전송하고 -> 브라우저에서는 압축된 파일을 전달받는 방식.
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@Controller
public class ZipFileDownloadController {
@GetMapping("/zipDownload")
public ResponseEntity<FileSystemResource> zipFileDownload() throws IOException {
List<FileSystemResource> resources = new ArrayList<>();
resources.add(new FileSystemResource("your_directory_path/file1.txt"));
resources.add(new FileSystemResource("your_directory_path/file2.txt"));
/*압축된 ZIP 파일을 생성할 File 객체를 생성한다.
그리고 경로에 해당하는 압축파일이 생성된다.*/
File zipFile = new File("your_directory_path/files.zip");
//ZipOutputStream을 사용하여 ZIP 파일을 생성하는 try-with-resources 블록을 시작에서 압축파일을 생성한다.
try (ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFile))) {
/*resources 리스트에 있는 각 FileSystemResource에 대해 반복한다
resources는 이전 코드에서 생성한 파일 목록*/
for (FileSystemResource resource : resources) {
//현재 처리 중인 FileSystemResource의 정보를 기반으로 ZipEntry를 생성하고 ZipOutputStream에 추가한다
ZipEntry zipEntry = new ZipEntry(resource.getFilename());
zipOutputStream.putNextEntry(zipEntry);
/*현재 처리 중인 FileSystemResource의 내용을 읽어들여 ZIP 파일에 쓰는 작업을 수행한다.
resource.getInputStream()으로부터 데이터를 읽어 들이고, zipOutputStream을 통해 ZIP 파일에 쓰기를 수행한다*/
byte[] data = new byte[1024];
int bytesRead;
while ((bytesRead = resource.getInputStream().read(data)) != -1) {
zipOutputStream.write(data, 0, bytesRead);
}
//현재의 ZipEntry 처리를 마치고, 다음 ZipEntry로 넘어가기 전에 현재 엔트리를 닫습니다.
zipOutputStream.closeEntry();
}
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", "files.zip");
return ResponseEntity.ok()
.headers(headers)
.body(new FileSystemResource(zipFile));
}
}
- 자바를 이용한 (여러개를 전송) 다운로드 방법1
여러 개의 파일을 각각 다운로드하고 저장하는 방식으로, 파일 간의 독립성이 있는 방식이다.
때문에, 압축이 아니라 낱개로 여러 개를 다운로드 받을 수도 있다.
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
public class MultiFileDownload {
public static void main(String[] args) {
//파일들의 URL목록을 나타내는 List를 생성하고 초기화한다.
List<String> fileURLs = Arrays.asList(
"https://example.com/file1.txt",
"https://example.com/file2.txt"
);
//각 파일의 URL에 대해 forEach 메서드를 사용하여 루프를 돌린다.
fileURLs.forEach(url -> {
/*URL을 통해 입력 스트림(InputStream)을 열고 해당 URL에서 파일의 내용을 읽는다.
동시에 출력 스트림(FileOutputStream)을 생성하여 로컬 파일 시스템에 파일을 저장할 경로를 설정한다.
그리고 try-with-resources 블록을 사용하여 자동으로 스트림을 닫는다*/
try (InputStream in = new URL(url).openStream();
FileOutputStream fos = new FileOutputStream("your_directory_path/" + getFileName(url))) {
/*파일의 내용을 읽어들여 1024바이트씩 버퍼에 담고, 버퍼의 내용을 출력 스트림을 통해 로컬 파일에 쓰는 작업을 수행한다.
이 과정을 반복하여 파일의 모든 내용을 로컬에 저장한다.*/
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
}
});
}
private static String getFileName(String url) {
return url.substring(url.lastIndexOf('/') + 1);
}
}
- 자바를 이용한 (한번에 처리) 다운로드 방법2
서버에서 여러 파일을 압축하여 하나의 ZIP파일로 만들어 클라이언트로 저장하는 방식이다.
주로 파일 간의 관계가 있는 경우에 사용된다.
import java.io.*;
import java.util.Arrays;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipFileDownload {
public static void main(String[] args) {
//압축할 파일의 이름과 내용을 담은 리스트를 생성하고 초기화한다.
List<String> fileNames = Arrays.asList("file1.txt", "file2.txt");
List<String> fileContents = Arrays.asList("Content of file 1", "Content of file 2");
/*압축된 ZIP 파일을 생성할 ZipOutputStream을 생성한다.
그리고 try-with-resources 블록을 사용하여 자동으로 스트림을 닫는다.
생성된 ZIP 파일은 작성된 경로의 파일명으로 저장된다 */
try (ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream("your_directory_path/files.zip"))) {
//파일 이름과 내용의 리스트를 순회하면서 각 파일에 대한 압축 작업을 수행한다.
for (int i = 0; i < fileNames.size(); i++) {
//현재 처리 중인 파일에 대한 ZipEntry를 생성하고, ZipOutputStream에 추가한다.
ZipEntry zipEntry = new ZipEntry(fileNames.get(i));
zipOutputStream.putNextEntry(zipEntry);
//현재 처리 중인 파일의 내용을 바이트 배열로 변환하고, ZipOutputStream을 통해 ZIP 파일에 쓰기를 수행한다.
byte[] data = fileContents.get(i).getBytes();
zipOutputStream.write(data, 0, data.length);
//현재의 ZipEntry 처리를 마치고, 다음 ZipEntry로 넘어가기 전에 현재 엔트리를 닫는다.
zipOutputStream.closeEntry();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
'WEB 심화 > Back-End' 카테고리의 다른 글
[Spring] dispatcherServlet이란 (0) | 2024.01.15 |
---|---|
[SPRING] 자주 쓰이는 기능 (어노테이션, 라이브러리) 정리 (0) | 2024.01.09 |
[SPRING] 웹 개발 환경 설정 파일 (0) | 2023.12.12 |
[SPRING] AOP, Filter, Interceptor, DI, IOC 비교 정리 (0) | 2023.12.07 |
[OS] 운영체제의 Shell과 주요 기능 (1) | 2023.11.27 |