반응형
최근에 카페24에 스프링 프레임워크를 설치하고 운영중에 버그가 한개 발생했었습니다.
그 버그는 동일 서버내에서 영상 재생을 다이렉트로 하면, Stack Overflow가 발생하면서 재생동안 에러로그가 쌓여 카페24에서 할당받은 용량을 catalina.out이 꽉 차면서 용량이 사용할 수 없는 문제였습니다.
확인해보니, 컨트롤러단에서 스트리밍 링크를 직접 연결해주면 해당 문제가 해결되는것을 확인했습니다.
private final String FOLDER_MOVIE = "{폴더경로}";
/**
* @reference : http://aodis.egloos.com/5962812
* @modified : whiteduck
*/
@RequestMapping(value="/stream/{video_name:.+}", method= RequestMethod.GET)
public String stream(@PathVariable("video_name") String video_name, HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException, IOException {
// 확장자 확인 //
String[] filename_seperate = video_name.split("\\.");
String exp;
if(filename_seperate.length <= 1 ) {
// 확장자 에러 //
throw new RuntimeException("Wrong file name. You need to include expand file name");
// response.getOutputStream().write(resultMsg.getBytes(SystemInfo.ENCODING));
// response.getOutputStream().flush();
// return null;
} else {
exp = filename_seperate[1];
}
// Progressbar 에서 특정 위치를 클릭하거나 해서 임의 위치의 내용을 요청할 수 있으므로
// 파일의 임의의 위치에서 읽어오기 위해 RandomAccessFile 클래스를 사용한다.
// 해당 파일이 없을 경우 예외 발생
File file = new File(FOLDER_MOVIE + video_name);
if(!file.exists()) throw new FileNotFoundException();
RandomAccessFile randomFile = new RandomAccessFile(file, "r");
long rangeStart = 0; // 요청 범위의 시작 위치
long rangeEnd = 0; // 요청 범위의 끝 위치
boolean isPart = false; //부분 요청일 경우 true, 전체 요청의 경우 false
// randomFile 을 클로즈 하기 위하여 try~finally 사용
try{
// 동영상 파일 크기
long movieSize = randomFile.length();
// 스트림 요청 범위, request의 헤더에서 range를 읽는다.
String range = request.getHeader("range");
// 브라우저에 따라 range 형식이 다른데, 기본 형식은 "bytes={start}-{end}" 형식이다.
// range가 null이거나, reqStart가 0이고 end가 없을 경우 전체 요청이다.
// 요청 범위를 구한다.
if(range != null) {
// 처리의 편의를 위해 요청 range에 end 값이 없을 경우 넣어줌
if(range.endsWith("-")){
range = range+(movieSize - 1);
}
int idxm = range.trim().indexOf("-"); //"-" 위치
rangeStart = Long.parseLong(range.substring(6,idxm));
rangeEnd = Long.parseLong(range.substring(idxm+1));
if(rangeStart > 0){
isPart = true;
}
}else{
//range가 null인 경우 동영상 전체 크기로 초기값을 넣어줌. 0부터 시작하므로 -1
rangeStart = 0;
rangeEnd = movieSize - 1;
}
// 전송 파일 크기
long partSize = rangeEnd - rangeStart + 1;
// 전송시작
response.reset();
// 전체 요청일 경우 200, 부분 요청일 경우 206을 반환상태 코드로 지정
response.setStatus(isPart ? 206 : 200);
// mime type 지정
response.setContentType("video/mp4");
// 전송 내용을 헤드에 넣어준다. 마지막에 파일 전체 크기를 넣는다.
response.setHeader("Content-Range", "bytes "+rangeStart+"-"+rangeEnd+"/"+movieSize);
response.setHeader("Accept-Ranges", "bytes");
response.setHeader("Content-Length", ""+partSize);
OutputStream out = response.getOutputStream();
// 동영상 파일의 전송시작 위치 지정
randomFile.seek(rangeStart);
// 파일 전송... java io는 1회 전송 byte수가 int로 지정됨
// 동영상 파일의 경우 int형으로는 처리 안되는 크기의 파일이 있으므로
// 8kb로 잘라서 파일의 크기가 크더라도 문제가 되지 않도록 구현
int bufferSize = 8*1024;
byte[] buf = new byte[bufferSize];
do{
int block = partSize > bufferSize ? bufferSize : (int)partSize;
int len = randomFile.read(buf, 0, block);
out.write(buf, 0, len);
partSize -= block;
}while(partSize > 0);
}catch(IOException e){
// 전송 중에 브라우저를 닫거나, 화면을 전환한 경우 종료해야 하므로 전송취소.
// progressBar를 클릭한 경우에는 클릭한 위치값으로 재요청이 들어오므로 전송 취소.
}finally{
randomFile.close();
}
return null;
}
해당 소스는 이글루 whiteduck님의 aodis.egloos.com/5962812 소스를 재구성한 소스입니다.
이렇게 소스를 구현하고 html에서 아래와 같이 호출하게 되면
<video width="640" height="344" controls autoplay="autoplay">
<source src="http://localhost:8080/stream/sample.mp4" type="video/mp4"/>
</video>
FOLDER_MOVIE에서 지정한 폴더 냉에서 sample.mp4파일을 호출해서 재생하게 됩니다.
반응형
'개발 창고 > Web' 카테고리의 다른 글
[Javascript] 모바일과 윈도우 구분짓기, User-Agent (0) | 2020.08.13 |
---|---|
[HTML] Form 자동 Submit 막기 (0) | 2020.08.12 |
[MyBATIS] Primary Key를 Insert하기 위한 SelectKey (0) | 2020.08.05 |
[Spring] 프로그램의 로깅을 위한 기술 Log4j (0) | 2020.08.03 |
[Javascript] 숫자 한글로 표기하기 (2) | 2020.07.26 |