Partial_Content

codakcodak·2023년 12월 3일
0

wannago

목록 보기
3/5
post-thumbnail

문제점

  • 동영상을 제공시 Resource를 사용하여 제공하면 버퍼되지 않은 재생시간에 대해서는 재생이 불가능하다.

    위와 같이 빨간 화살표 영역은 버퍼되지 않았기 때문에 클릭을 해도 재생이 불가
  • 특정 사용자가 얼마나 많은 데이터를 소비하는지에 대한 관찰이 어렵다.
    유튜브 같은 경우 프리미엄 구독자가 아닌경우 광고를 내보내는데 이는 사용자가 특정 데이터만큼 영상을 재생시키면 광고를 내보내는 방식으로 짰을 것이다.

해결방안

  • 특정 시작 지점부터 특정 길이만큼 소스를 잘라 보낼 수 있는 ResourceRegion 적용
  • 특정 길이(chunk size)는 2MB로 적용

구현

1. service

@Override
	public ResponseEntity<ResourceRegion> sendMediaVideo(int mediaNo,String userId,HttpHeaders headers) throws Exception{
		MediaDto media=mediaMapper.selectByMediaNo(mediaNo);
		if(media==null) {
			throw new MediaException(MediaErrorCode.NotFoundMedia.getCode(),MediaErrorCode.NotFoundMedia.getDescription());
		}
		if(!"video".equals(media.getMediaType())){
			throw new MediaException(MediaErrorCode.NotCorrectType.getCode(),MediaErrorCode.NotCorrectType.getDescription());
		}
		
		String filePath=uploadPath+ File.separator+media.getSavePath();

		if(!Files.exists(Paths.get(filePath))) {
			throw new FileException(FileErrorCode.NotFoundFile.getCode(),FileErrorCode.NotFoundFile.getDescription());
		}
		
		Resource resource = new FileSystemResource(filePath);
		ResourceRegion region=getVideoRegion(resource,headers);
		
        return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT)
                .contentType(MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM))
                .header("Accept-Ranges", "bytes")
                .eTag(media.getMediaOriginFile())
                .body(region);
	}

2. ResourceRegion

private ResourceRegion getVideoRegion(Resource resource,HttpHeaders headers) throws Exception{
        long contentLength = resource.contentLength();

        ResourceRegion region;

        try {
       		//헤더로부터 모든 인자 추출하기
        	HttpRange httpRange = headers.getRange().stream().findFirst().get();
            long start = httpRange.getRangeStart(contentLength);
            long end = httpRange.getRangeEnd(contentLength);
            //최대 chunksize만큼 길이 설정
            long rangeLength = Long.min(this.chunkSize, end -start + 1);
			//start부터 rangeLength만큼 전송
            region = new ResourceRegion(resource, start, rangeLength);
        } catch (Exception e) {
        	//중간에 에러가 난다면 처음부터 끝까지의 데이터 전송
            long rangeLength = Long.min(this.chunkSize, contentLength);
            region = new ResourceRegion(resource, 0, rangeLength);
        }
        return region;
	}

3.결과

chunk_size(2.1MB)씩 부분적으로 가져오고 있다.

profile
숲을 보는 코더

0개의 댓글