Skip to content

Instantly share code, notes, and snippets.

@ayaysir
Last active August 17, 2020 05:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ayaysir/dea411fa31e7d638f1e637e8fcc131f1 to your computer and use it in GitHub Desktop.
Save ayaysir/dea411fa31e7d638f1e637e8fcc131f1 to your computer and use it in GitHub Desktop.
Safari <audio> 태그의 구간 탐색 문제 ("라이브 방송") 해결 코드
package com.example.awsboard.web;
import com.example.awsboard.config.auth.LoginUser;
import com.example.awsboard.config.auth.dto.SessionUser;
import com.example.awsboard.service.posts.MidiService;
import com.example.awsboard.util.TimidityRunner;
import com.example.awsboard.web.dto.midi.MidiPublicResponseDTO;
import com.example.awsboard.web.dto.midi.MidiRequestDTO;
import com.example.awsboard.web.dto.midi.MidiResponseDTO;
import lombok.RequiredArgsConstructor;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.support.ServletContextResource;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.swing.filechooser.FileSystemView;
import java.io.*;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.util.*;
@RequiredArgsConstructor
@RestController
public class MidiApiController {
private final MidiService midiService;
private final String DEFAULT_URI = "/api/v1/midi";
// .......... 생략 .......... //
@GetMapping(DEFAULT_URI + "/mp3/{id}")
@ResponseStatus(HttpStatus.OK)
public void mp3Play(ModelAndView modelAndView, @PathVariable Long id,
HttpServletRequest request,
HttpServletResponse response) throws IOException {
// mp3 파일 경로 지정
String rootPath = FileSystemView.getFileSystemView().getHomeDirectory().toString();
String basePath = rootPath + "/" + "app/midi";
MidiResponseDTO mp3 = midiService.findById(id);
String mp3Path = basePath + mp3.getOriginalMp3Path();
// File 객체 생성
File initFile = new File(mp3Path);
// ***** RANGE 추출 ***** //
Long startRange = 0l;
Long endRange = 0l;
Boolean isPartialRequest = false;
try {
if(request.getHeader("range") != null) {
String rangeStr = request.getHeader("range");
System.out.println(rangeStr);
String[] range = rangeStr
.replace("bytes=", "").split("-");
startRange = range[0] != null ? Long.parseLong(range[0]) : 0l;
if(range[1] != null) {
endRange = Long.parseLong(range[1]);
isPartialRequest = true;
}
System.out.println(">>>>> range >>>>> " + range[0] + " : " + range[1]);
}
} catch(NullPointerException | ArrayIndexOutOfBoundsException e) {
System.err.println(e);
}
// ****************** //
// 파일 다운로드 이름 생성
String downloadName = id + "-" + mp3.getCustomTitle() + ".mp3";
String browser = request.getHeader("User-Agent");
// 파일 인코딩
if(browser.contains("MSIE") || browser.contains("Trident") || browser.contains("Chrome")){
// 브라우저 확인 파일명 encode
downloadName = URLEncoder.encode(downloadName, "UTF-8").replaceAll("\\+", "%20");
} else {
downloadName = new String(downloadName.getBytes("UTF-8"), "ISO-8859-1");
}
// 리스폰스 헤더 설정
response.setHeader("Content-Disposition", "filename=\"" + downloadName +"\"");
response.setContentType("audio/mpeg");
response.setHeader("Accept-Ranges", "bytes"); // 크롬 구간문제 해결용
response.setHeader("Content-Transfer-Encoding", "binary;");
// 부분 범위 리퀘스트인지, 전체 범위 리퀘스트인지에 따라 Content-Range 값을 다르게
if(isPartialRequest) {
response.setHeader("Content-Range", "bytes " + startRange + "-"
+ endRange + "/" + initFile.length());
} else {
response.setHeader("Content-Length", initFile.length() + "");
response.setHeader("Content-Range", "bytes 0-"
+ initFile.length() + "/" + initFile.length());
startRange = 0l;
endRange = initFile.length();
}
// 랜덤 액세스 파일을 이용해 mp3 파일을 범위로 읽기
try(RandomAccessFile randomAccessFile = new RandomAccessFile(initFile, "r");
ServletOutputStream sos = response.getOutputStream(); ){
Integer bufferSize = 1024, data = 0;
byte[] b = new byte[bufferSize];
Long count = startRange;
Long requestSize = endRange - startRange + 1;
// startRange에서 출발
randomAccessFile.seek(startRange);
while(true) {
// 버퍼 사이즈 (1024) 보다 범위가 작으면
if(requestSize <= 2) {
// Range byte 0-1은 아래 의미가 아님.
// data = randomAccessFile.read(b, 0, requestSize.intValue());
// sos.write(b, 0, data);
// ** write 없이 바로 flush ** //
sos.flush();
break;
}
// 나머지는 일반적으로 진행
data = randomAccessFile.read(b, 0, b.length);
// count가 endRange 이상이면 요청 범위를 넘어선 것이므로 종료
if(count <= endRange) {
sos.write(b, 0, data);
count += bufferSize;
randomAccessFile.seek(count);
} else {
break;
}
}
sos.flush();
}
}
// .......... 생략 .......... //
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment