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!!