Last active
August 17, 2020 05:42
-
-
Save ayaysir/dea411fa31e7d638f1e637e8fcc131f1 to your computer and use it in GitHub Desktop.
Safari <audio> 태그의 구간 탐색 문제 ("라이브 방송") 해결 코드
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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