Unable to Stream Videos from Amazon S3 using Spring boot

Viewed 149

I am unable to stream videos in my Spring boot application from Amazon S3. It either downloads the video on page load or it throws some error. I tried the solution from this thread but this too downloads the video. Adding the various approaches I tried:

Approach 1: (I have also tried sending the ResponseEntity<byte[]> as response directly but this too did not work)

@GetMapping(value = "/video")
public Mono<ResponseEntity<byte[]>> getObject(HttpServletRequest request, @RequestHeader(value = "Range", required = false) String httpRangeList) {
    try {

        long rangeStart = 0;
        long rangeEnd;
        byte[] data;

        BasicAWSCredentials credentials = new BasicAWSCredentials(ACCESS_KEY_ID, SECRET_KEY);
        AmazonS3 amazonS3 = AmazonS3ClientBuilder.standard()
                .withRegion(Regions.DEFAULT_REGION)
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .build();

        S3Object object = amazonS3.getObject(BUCKET_NAME, "test.mpg");
        S3ObjectInputStream finalObject = object.getObjectContent();

        Long fileLength = object.getObjectMetadata().getContentLength();

        if (httpRangeList == null) {
            return Mono.just(ResponseEntity.status(HttpStatus.OK)
                    .header(CONTENT_TYPE, object.getObjectMetadata().getContentType())
                    .header(CONTENT_LENGTH, String.valueOf(fileLength))
                    .body(readByteRange(finalObject, rangeStart, finalObject.toString().length() - 1))); // Read the object and convert it as bytes
        }
        String[] ranges = httpRangeList.split("-");
        rangeStart = Long.parseLong(ranges[0].substring(6));
        if (ranges.length > 1) {
            rangeEnd = Long.parseLong(ranges[1]);
        } else {
            rangeEnd = fileLength - 1;
        }
        if (fileLength < rangeEnd) {
            rangeEnd = fileLength - 1;
        }

        data = readByteRange(finalObject, rangeStart, rangeEnd);
        String contentLength = String.valueOf((rangeEnd - rangeStart) + 1);
        return Mono.just(ResponseEntity.status(HttpStatus.PARTIAL_CONTENT)
                .header(CONTENT_TYPE, object.getObjectMetadata().getContentType())
                .header(ACCEPT_RANGES, BYTES)
                .header(CONTENT_LENGTH, contentLength)
                .header(CONTENT_RANGE, BYTES + " " + rangeStart + "-" + rangeEnd + "/" + fileLength)
                .body(data));
    } catch (Exception e) {
        System.err.println("Error "+ e.getMessage());
        return null;
    }

}

public byte[] readByteRange(S3ObjectInputStream finalObject, long start, long end) throws IOException {

    try (InputStream inputStream = finalObject.getDelegateStream();
         ByteArrayOutputStream bufferedOutputStream = new ByteArrayOutputStream()) {
        byte[] data = new byte[1024];
        int nRead;
        while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
            bufferedOutputStream.write(data, 0, nRead);
        }
        bufferedOutputStream.flush();
        byte[] result = new byte[(int) (end - start) + 1];
        System.arraycopy(bufferedOutputStream.toByteArray(), (int) start, result, 0, result.length);
        return result;
    }
}

Approach 2:

@GetMapping(value = "/video", produces = "video/mpeg")
public Mono<ResponseEntity<StreamingResponseBody>> getVideo2Object(HttpServletRequest request) {
    try {

        BasicAWSCredentials credentials = new BasicAWSCredentials(ACCESS_KEY_ID, SECRET_KEY);
        AmazonS3 amazonS3 = AmazonS3ClientBuilder.standard()
                .withRegion(Regions.DEFAULT_REGION)
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .build();

        S3Object object = amazonS3.getObject(BUCKET_NAME, "test.mpg");
        S3ObjectInputStream finalObject = object.getObjectContent();

        final StreamingResponseBody body = outputStream -> {
            int numberOfBytesToWrite = 0;
            byte[] data = new byte[1024];
            while ((numberOfBytesToWrite = finalObject.read(data, 0, data.length)) != -1) {
                outputStream.write(data, 0, numberOfBytesToWrite);
            }
            finalObject.close();
        };
        return Mono.just(ResponseEntity.status(HttpStatus.OK).body(body));
    } catch (Exception e) {
        System.err.println("Error "+ e.getMessage());
        return null;
    }

}

Approach 3:

@GetMapping(value = "/video", produces = "video/mpeg")
public StreamingResponseBody getVideo3Object(HttpServletRequest request) {
    try {

        BasicAWSCredentials credentials = new BasicAWSCredentials(ACCESS_KEY_ID, SECRET_KEY);
        AmazonS3 amazonS3 = AmazonS3ClientBuilder.standard()
                .withRegion(Regions.DEFAULT_REGION)
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .build();

        S3Object object = amazonS3.getObject(BUCKET_NAME, "test.mpg");
        S3ObjectInputStream finalObject = object.getObjectContent();      

        return (os) -> {
            byte[] data = new byte[2048];
            int read = 0;
            while ((read = video.getInputStream().read(data)) > 0) {
                os.write(data, 0, read);
            }
            os.flush();
        };
    } catch (Exception e) {
        System.err.println("Error "+ e.getMessage());
        return null;
    }

}

In Front-end, I am trying to use the or tags to display the Streaming video. Sample FE code:

<video src="video" width="750px" height="500px" controls preload="none">
</video>

<iframe src="video" title="Sample Video Streaming"></iframe>

Any help to this thread is highly appreciated. Thanks in Advance!!

0 Answers
Related