๐ INDEX
- FTP ์๋ฒ ์์ ์คํธ๋ฆฌ๋ฐ ๊ฐ์
- ์ค๋ฅ ๋ด์ฉ
- ์ค๋ฅ ๋ถ์
- ์ฝ๋ ๊ฐ์ ๋ฐ ์ค๋ฅ ํด๊ฒฐ
- ๊ฐ๋ฐ ํ๊ฒฝ
FTP ์๋ฒ ์์ ์คํธ๋ฆฌ๋ฐ ๊ฐ์
FTP ์๋ฒ ์์ ์คํธ๋ฆฌ๋ฐ์ด ์ ๋๋ ๊ฒ ๊น์ง ํ
์คํธ๋ฅผ ์๋ฃํ๊ณ ํ๋ก์ ํธ ์งํ ์ค..
getOutputStream() has already been called for this response ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
์ ์ฒด ์ ์ธ FTP ์๋ฒ ์์ฑ ๋ฐ ์ค์ , FTP ์ ์ฐ๊ฒฐํ๋ API ๋ด์ฉ์ ๋ฒจ๋ก๊ทธ์์ ์์ฑ ํด๋์์ผ๋ ์ถํ์ ํฐ์คํ ๋ฆฌ๋ก ์ฎ๊ธธ ์์ ์ด๋ค.. ใ ใ
์ค๋ฅ ๋ด์ฉ
liyo-ftp | 2024-08-14T15:11:06.615+09:00 ERROR 1 --- [service-ftp] [nio-8811-exec-3] [] o.a.c.c.C.[.[.[/].[dispatcherServlet]
: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.IllegalStateException: getOutputStream() has already been called for this response] with root cause
liyo-ftp |
liyo-ftp | java.lang.IllegalStateException: getOutputStream() has already been called for this response
liyo-ftp | at org.apache.catalina.connector.Response.getWriter(Response.java:549)
liyo-ftp | at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:188)
liyo-ftp | at jakarta.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:108)
liyo-ftp | at jakarta.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:108)
liyo-ftp | at jakarta.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:108)
getOutputStream() has already been called for this response
์ด ์๋ฌ ๋ฉ์์ง๋ getOutputStream() ๋ฉ์๋๊ฐ ์ด๋ฏธ ํธ์ถ๋ ํ์ getWriter() ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ค๊ณ ํด์ ๋ฐ์ํ๋ IllegalStateException์ ๋๋ค.
Java ์๋ธ๋ฆฟ์์ HTTP ์๋ต์ ์์ฑํ ๋, ์๋ต์ ๋ฐ๋๋ฅผ ์์ฑํ๋ ๋ ๊ฐ์ง ์ฃผ์ ๋ฐฉ๋ฒ
- getOutputStream(): ๋ฐ์ด๋๋ฆฌ ๋ฐ์ดํฐ๋ฅผ ์์ฑํ ๋ ์ฌ์ฉํฉ๋๋ค.
- getWriter(): ํ ์คํธ ๋ฐ์ดํฐ๋ฅผ ์์ฑํ ๋ ์ฌ์ฉํฉ๋๋ค.
์ด ๋ ๋ฉ์๋๋ ๋์์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, getOutputStream()์ ํธ์ถํ์ฌ ์๋ต ์คํธ๋ฆผ์ ์ด์์ผ๋ฉด ๊ทธ ํ์๋ getWriter()๋ฅผ ํธ์ถํ ์ ์๊ณ , ๊ทธ ๋ฐ๋๋ ๋ง์ฐฌ๊ฐ์ง์ ๋๋ค.
ํด๋น ์๋ฌ ๋ฉ์์ง๋ getOutputStream() ์ด ์ด๋ฏธ ํธ์ถ ๋์ด ์๋๋ฐ, getWriter()๋ฅผ ํธ์ถํ๋ ค ํด์, IllegalStateException ์์ธ๊ฐ ๋ฐ์ํ์ต๋๋ค.
ํ ๋ฒ์ ๋์ ํ์ง๋ง ๋ ๋ฒ์ ์ ๋๋ ์ด์ ...
์ค๋ฅ ๋ถ์
/**
* ๋น๋์ค ์คํธ๋ฆฌ๋ฐ
* @author liyo
* @param vo
* @param resp
* @throws IOException
*/
public void video(FtpVo vo, HttpServletResponse resp) throws IOException {
String fileNm = vo.getFileNm();
FTPClient ftpClient = pool.getFTPClient();
try {
// ftpClient.enterLocalActiveMode(); // ๋ฅ๋
ftpClient.enterLocalPassiveMode(); // ์๋
// ํ์ฅ์๋ณ MIME ํ์
์ค์
String mimeType = getMimeType(fileNm);
resp.setContentType(mimeType);
resp.setHeader("Content-Transfer-Encoding", "binary");
resp.setHeader("Pragma", "no-cache");
resp.setHeader("Expires", "0");
resp.setHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(fileNm, "UTF-8") + ";");
OutputStream out = resp.getOutputStream();
InputStream inputStream = null;
if (vo.isGroup()) {
inputStream = ftpClient.retrieveFileStream(fileNm);
} else {
inputStream = ftpClient.retrieveFileStream(BesysConstants.NOGROUP + BesysConstants.SLASH + fileNm);
}
if (inputStream == null) {
throw new IOException(ftpClient.getReplyString());
}
int len;
byte[] buf = new byte[8192];
while ((len = inputStream.read(buf)) != -1) {
out.write(buf, 0, len);
}
out.flush();
inputStream.close();
out.close();
if (!ftpClient.completePendingCommand()) {
throw new IOException("Could not complete FTP command");
}
} catch (Exception e) {
log.error("Video streaming failed: {}", e);
throw new IOException("Video streaming failed", e);
} finally {
if (ftpClient != null && ftpClient.isConnected()) {
try {
ftpClient.logout();
} catch (IOException ex) {
log.error("Error logging out from FTP server: {}", ex);
}
pool.returnFTPClient(ftpClient);
}
}
}
์ค๋ฅ๊ฐ ๋ฐ์ํ ์ฝ๋๋ ์์ ๊ฐ์ต๋๋ค.
1. out.close() ํธ์ถ ์์
- OutputStream์ ์ง์ ๋ซ๊ธฐ[close()] ์ ์, ์คํธ๋ฆผ์ด ๋ซํ ์ํ์์ ๋ค๋ฅธ ์คํธ๋ฆผ์ ์๋ํ๊ฑฐ๋, ์ด๋ฏธ ์๋ต์ด ๋๋ ํ ์ถ๊ฐ์ ์ธ ์ฒ๋ฆฌ๋ฅผ ์๋ํ๋ ๊ฒฝ์ฐ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
- out.close()๊ฐ inputStream.close() ์ดํ์ ํธ์ถ๋๊ณ , ๊ทธ ํ์๋ ๋ ์ด์ ์๋ต์ ์์ฑ๋์ง ์๋๋ก ํด์ผ ํฉ๋๋ค.
2. ์ค๋ณต๋ ์คํธ๋ฆผ ์ฒ๋ฆฌ
- ๋ฉ์๋๊ฐ ์ ์์ ์ผ๋ก ์คํธ๋ฆผ์ ์ฒ๋ฆฌํ ํ, ๋ค๋ฅธ ๊ณณ์์ ๊ฐ์ ์๋ต์ ๋ํด ๋ ๋ค๋ฅธ ์คํธ๋ฆผ์ ์ด๋ ค๊ณ ์๋ํ๋ฉด ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
- ์ฃผ์ ๊น๊ฒ ํ์ธํด์ผ ํ๋ ๊ฒ์ ์ฝ๋ ์ธ๋ถ์์ ์ด ๋ฉ์๋๊ฐ ํธ์ถ๋ ์ดํ, ๋์ผํ ์๋ต ๊ฐ์ฒด(HttpServletResponse)๋ฅผ ์ฌ์ฉํ๋ ๋ค๋ฅธ ์ฝ๋์์ getOutputStream() ๋๋ getWriter()๋ฅผ ํธ์ถํ๋ ค๊ณ ํ๋์ง ์ฌ๋ถ์ ๋๋ค.
3. ์์ธ์ฒ๋ฆฌ
- ์์ธ๊ฐ ๋ฐ์ํ๋ฉด ์คํธ๋ฆผ์ ๋ซ์ง ์๊ณ , ๊ทธ๋๋ก ๋จ์ ์๋ ๊ฒฝ์ฐ finally ๋ธ๋ก์์ ๋๋ ๋ค๋ฅธ ํ์ ์ฝ๋์์ ๋ ๋ค๋ฅธ ์คํธ๋ฆผ์ ์๋ํ ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
- ์์ธ๊ฐ ๋ฐ์ํ๋๋ผ๋ ์คํธ๋ฆผ์ด ์ ์์ ์ผ๋ก ๋ซํ๋๋ก ์ ๊ฒฝ ์จ์ผ ํฉ๋๋ค.
์ฝ๋ ๊ฐ์ ๋ฐ ์ค๋ฅ ํด๊ฒฐ
/**
* ๋น๋์ค ์คํธ๋ฆฌ๋ฐ
* @author liyo
* @param vo
* @param resp
* @throws IOException
*/
public void video(FtpVo vo, HttpServletResponse resp) throws IOException {
String fileNm = vo.getFileNm();
FTPClient ftpClient = pool.getFTPClient();
OutputStream out = null;
InputStream inputStream = null;
try {
// FTP ๋ชจ๋ ์ค์
ftpClient.enterLocalPassiveMode();
// ํ์ฅ์๋ณ MIME ํ์
์ค์
String mimeType = getMimeType(fileNm);
resp.setContentType(mimeType);
resp.setHeader("Content-Transfer-Encoding", "binary");
resp.setHeader("Pragma", "no-cache");
resp.setHeader("Expires", "0");
resp.setHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(fileNm, "UTF-8") + ";");
// ์๋ต OutputStream๊ณผ FTP InputStream ์ด๊ธฐํ
out = resp.getOutputStream();
inputStream = vo.isGroup() ? ftpClient.retrieveFileStream(fileNm)
: ftpClient.retrieveFileStream(BesysConstants.NOGROUP + BesysConstants.SLASH + fileNm);
if (inputStream == null) {
throw new IOException(ftpClient.getReplyString());
}
// ๋ฐ์ดํฐ ์ ์ก
byte[] buf = new byte[8192];
int len;
while ((len = inputStream.read(buf)) != -1) {
out.write(buf, 0, len);
}
out.flush();
// FTP ๋ช
๋ น ์๋ฃ ํ์ธ
if (!ftpClient.completePendingCommand()) {
throw new IOException("Could not complete FTP command");
}
} catch (Exception e) {
log.error("Video streaming failed: {}", e);
throw new IOException("Video streaming failed", e);
} finally {
// ์คํธ๋ฆผ ์์ ํ๊ฒ ๋ซ๊ธฐ
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException ex) {
log.error("Error closing input stream: {}", ex);
}
}
if (out != null) {
try {
out.close();
} catch (IOException ex) {
log.error("Error closing output stream: {}", ex);
}
}
// FTP ์ฐ๊ฒฐ ํด์ ๋ฐ ๋ฐํ
if (ftpClient != null && ftpClient.isConnected()) {
try {
ftpClient.logout();
} catch (IOException ex) {
log.error("Error logging out from FTP server: {}", ex);
}
pool.returnFTPClient(ftpClient);
}
}
}
์์ ๋ด์ฉ
1. ์คํธ๋ฆผ(InputStream ๋ฐ OutputStream)์ finally ๋ธ๋ก์์ ์์ ํ๊ฒ ๋ซ์ต๋๋ค.
2. FTP ์ฐ๊ฒฐ ํด์ ๋ฐ ๋ฐํ๋ finally ๋ธ๋ก์์ ์ด๋ฃจ์ด์ง๋๋ค.
3. close() ๋ฉ์๋ ํธ์ถ์ ์ ์ ํ ์์น์ ๋ฐฐ์นํ์ฌ, ํ ๋ฒ ์คํธ๋ฆผ์ ๋ซ์ผ๋ฉด ์ดํ์ ๋ ์ด์ ์๋ต์ ์ฒ๋ฆฌํ์ง ์๋๋ก ํฉ๋๋ค.
์์ ์๋ฃ!
์์ ๊ฐ์ด ์์ ํ์ฌ getOutputStream() ๋๋ getWriter()๋ฅผ ๋ฐ๋ณตํด์ ํธ์ถํ๋ ๋ฌธ์ ๋ฅผ ๋ฐฉ์งํ์์ต๋๋ค.
ํฌ์คํธ๋งจ ํ ์คํธ ๊ฒฐ๊ณผ ์ฑ๊ณต์ ์ผ๋ก ์์์ด ์คํธ๋ฆฌ๋ฐ ๋ฉ๋๋ค.
ํด๋น API ๋ฅผ ์น์์ ํธ์ถํด๋ ์๋ํ๋๋ ๋ชจ์ต์ ๋๋ค!
๊ฐ๋ฐ ํ๊ฒฝ
- Backend
- Framework: Spring Boot 3.2.2
- Language: Java
- Gateway: Spring Cloud Gateway
- Database
- DBMS: MS SQL Server
- Container: Docker
- Development Tools
- IDE: Visual Studio Code (VSCode)
'Windows > FTP' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Wondows] FTP ์๋ฒ ๊ตฌ์ถํ๊ธฐ (1) | 2024.09.26 |
---|