package yourpackage.util;
//importes
public class VideoFileRangeUtil {
public static void renderResponse(FacesContext ctx, String docUNID, String filename) throws IOException, ReadContextException{
System.out.println("get File range data");
Document fileDocument = null;
EmbeddedObject attachment = null;
try{
System.out.println("Retrieve external context etc..");
DominoExternalContext extcon = (DominoExternalContext) FacesContext.getCurrentInstance().getExternalContext();
HttpServletRequest request = (HttpServletRequest) extcon.getRequest();
HttpServletResponse response= (HttpServletResponse) extcon.getResponse();
System.out.println("Retrieve document from content item: "+docUNID);
ReadContext item = factory.getReadContext(docUNID);
fileDocument = item.getDocument();
System.out.println("Retrieve file attachment");
attachment = getFileAttachment(ctx, filename,fileDocument);
if(attachment == null){
response.sendError(404);
return;
}
System.out.println("Retrieve file information");
long length = attachment.getFileSize();
long lastModified = fileDocument.getLastModified().toJavaDate().getTime();
String eTag = filename + "_" + length + "_" + lastModified;
String ifNoneMatch = request.getHeader("If-None-Match");
if (ifNoneMatch != null && VideoFileRangeUtilHelpers.matches(ifNoneMatch, eTag)) {
response.setHeader("ETag", eTag); // Required in 304.
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
System.out.println("Check last modified");
// If-Modified-Since header should be greater than LastModified. If so, then return 304.
// This header is ignored if any If-None-Match header is specified.
long ifModifiedSince = request.getDateHeader("If-Modified-Since");
if (ifNoneMatch == null && ifModifiedSince != -1 && ifModifiedSince + 1000 > lastModified) {
response.setHeader("ETag", eTag); // Required in 304.
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
return;
}
System.out.println("Check if-match headers");
String ifMatch = request.getHeader("If-Match");
if (ifMatch != null && !VideoFileRangeUtilHelpers.matches(ifMatch, eTag)) {
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
return;
}
System.out.println("Check If-Unmodified-Since headers");
long ifUnmodifiedSince = request.getDateHeader("If-Unmodified-Since");
if (ifUnmodifiedSince != -1 && ifUnmodifiedSince + 1000 <= lastModified) {
response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
return;
}
System.out.println("Create byte ranges");
ByteRange full = new ByteRange(0, length - 1, length);
List ranges = new ArrayList<ByteRange>();
System.out.println("Get range headers");
// Validate and process Range and If-Range headers.
String range = request.getHeader("Range");
if (range != null) {
// Range header should match format "bytes=n-n,n-n,n-n...". If not, then return 416.
if (!range.matches("^bytes=\\d*-\\d*(,\\d*-\\d*)*$")) {
response.setHeader("Content-Range", "bytes */" + length); // Required in 416.
response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return;
}
// If-Range header should either match ETag or be greater then LastModified. If not,
// then return full file.
String ifRange = request.getHeader("If-Range");
if (ifRange != null && !ifRange.equals(eTag)) {
try {
long ifRangeTime = request.getDateHeader("If-Range"); // Throws IAE if invalid.
if (ifRangeTime != -1 && ifRangeTime + 1000 < lastModified) {
ranges.add(full);
}
} catch (IllegalArgumentException ignore) {
ranges.add(full);
}
}
System.out.println("Proces part of range");
// If any valid If-Range header, then process each part of byte range.
if (ranges.isEmpty()) {
for (String part : range.substring(6).split(",")) {
// Assuming a file with length of 100, the following examples returns bytes at:
// 50-80 (50 to 80), 40- (40 to length=100), -20 (length-20=80 to length=100).
long start = VideoFileRangeUtilHelpers.sublong(part, 0, part.indexOf("-"));
long end = VideoFileRangeUtilHelpers.sublong(part, part.indexOf("-") + 1, part.length());
if (start == -1) {
start = length - end;
end = length - 1;
} else if (end == -1 || end > length - 1) {
end = length - 1;
}
// Check if Range is syntactically valid. If not, then return 416.
if (start > end) {
response.setHeader("Content-Range", "bytes */" + length); // Required in 416.
response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
return;
}
// Add range.
ranges.add(new ByteRange(start, end, length));
}
}
}
String contentType = "video/x-m4v";
boolean acceptsGzip = false;
String disposition = "inline";
// If content type is unknown, then set the default value.
// For all content types, see: http://www.w3schools.com/media/media_mimeref.asp
// To add new content types, add new mime-mapping entry in web.xml.
if (contentType == null) {
contentType = "application/octet-stream";
}
// If content type is text, then determine whether GZIP content encoding is supported by
// the browser and expand content type with the one and right character encoding.
if (contentType.startsWith("text")) {
String acceptEncoding = request.getHeader("Accept-Encoding");
acceptsGzip = acceptEncoding != null && VideoFileRangeUtilHelpers.accepts(acceptEncoding, "gzip");
contentType += ";charset=UTF-8";
}
// Else, expect for images, determine content disposition. If content type is supported by
// the browser, then set to inline, else attachment which will pop a 'save as' dialogue.
else if (!contentType.startsWith("image")) {
String accept = request.getHeader("Accept");
disposition = accept != null && VideoFileRangeUtilHelpers.accepts(accept, contentType) ? "inline" : "attachment";
}
// Initialize response.
response.reset();
response.setBufferSize(VideoFileRangeUtilHelpers.DEFAULT_BUFFER_SIZE);
response.setHeader("Content-Disposition", disposition + ";filename=\"" + filename + "\"");
response.setHeader("Accept-Ranges", "bytes");
response.setHeader("ETag", eTag);
response.setDateHeader("Last-Modified", lastModified);
response.setDateHeader("Expires", System.currentTimeMillis() + VideoFileRangeUtilHelpers.DEFAULT_EXPIRE_TIME);
// Send requested file (part(s)) to client ------------------------------------------------
// Prepare streams.
InputStream input = attachment.getInputStream();
OutputStream output = null;
try {
// Open streams.
output = response.getOutputStream();
InputStream str;
if (ranges.isEmpty() || ranges.get(0) == full) {
// Return full file.
ByteRange r = full;
response.setContentType(contentType);
response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total);
if (acceptsGzip) {
// The browser accepts GZIP, so GZIP the content.
response.setHeader("Content-Encoding", "gzip");
output = new GZIPOutputStream(output, VideoFileRangeUtilHelpers.DEFAULT_BUFFER_SIZE);
} else {
// Content length is not directly predictable in case of GZIP.
// So only add it if there is no means of GZIP, else browser will hang.
response.setHeader("Content-Length", String.valueOf(r.length));
}
// Copy full range.
VideoFileRangeUtilHelpers.copy(input, output, r.start, r.length);
} else if (ranges.size() == 1) {
// Return single part of file.
ByteRange r = (ByteRange)ranges.get(0);
response.setContentType(contentType);
response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total);
response.setHeader("Content-Length", String.valueOf(r.length));
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206.
// Copy single part range.
VideoFileRangeUtilHelpers.copy(input, output, r.start, r.length);
} else {
// Return multiple parts of file.
response.setContentType("multipart/byteranges; boundary=" + VideoFileRangeUtilHelpers.MULTIPART_BOUNDARY);
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206.
// Cast back to ServletOutputStream to get the easy println methods.
//ServletOutputStream sos = (ServletOutputStream) output;
// Copy multi part range.
// for (ByteRange r : ranges) {
// // Add multipart boundary and header fields for every range.
// sos.println();
// sos.println("--" + VideoFileRangeUtilHelpers.MULTIPART_BOUNDARY);
// sos.println("Content-Type: " + contentType);
// sos.println("Content-Range: bytes " + r.start + "-" + r.end + "/" + r.total);
//
// // Copy single part range of multi part range.
// VideoFileRangeUtilHelpers.copy(input, output, r.start, r.length);
// }
//
// // End with multipart boundary.
// sos.println();
// sos.println("--" + VideoFileRangeUtilHelpers.MULTIPART_BOUNDARY + "--");
}
} finally {
// Gently close streams.
VideoFileRangeUtilHelpers.close(output);
VideoFileRangeUtilHelpers.close(input);
}
}catch(NotesException e){
e.printStackTrace();
}finally{
try{
attachment.recycle();
fileDocument.recycle();
}catch(NotesException e){
e.printStackTrace();
}
}
}
/* Given the filename and the document datasource retrieve the correct embembeddedObject. */
private static EmbeddedObject getFileAttachment(FacesContext ctx,
String filename, Document fileDocument) throws NotesException {
System.out.println("Retrieve file "+filename);
if(fileDocument == null){
return null;
}
System.out.println("Get attachment by filename");
EmbeddedObject attachment = fileDocument.getAttachment(filename);
System.out.println(attachment.getName());
if(attachment == null){
System.out.println("Return nothing");
return null;
}
return attachment;
}
}