mongodb - Load video from inputstream using java and video.js -
i fetch inputstream mongodb , pass video.js play.
it plays fine first time, once video finished , click on 'play' button play again got below exception in java code
java.net.socketexception: software caused connection abort: socket write error
my java code -
@requestmapping(value = "/getvideo/{videoid}" , method = requestmethod.get) public void fetchvideo(@pathvariable(value = "videoid") string videoid, httpservletresponse response, httpservletrequest request) { try{ gridfsdbfile file = filestorageservice.getfilebyid(videoid); file.getinputstream(); response.setheader("content-type", file.getcontenttype()); response.setheader("x-content-type-options", "nosniff"); response.setheader("accept-ranges", "bytes"); response.setcontentlength((int) file.getlength()); file.writeto(response.getoutputstream()); file.getinputstream().close(); } catch(exception e) { e.printstacktrace(); } }
html code -
<video id="galleryvideoid" class="hide-normal video-js vjs-default-skin vjs-big-play-centered" autoplay="autoplay" controls="controls" preload="auto" width="100%" height="100%" data-setup="{}"> <source th:src="@{'/getvideo/' + ${videoid} }" type='video/mp4'/> </video>
i tried search on google , tried few solutions didn't work out.
please guide. thanks.
this same issue faced before , below exact solution of it. try below code :
import org.apache.commons.lang3.stringutils; import org.slf4j.logger; import org.slf4j.loggerfactory; import javax.servlet.servletoutputstream; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; import java.io.*; import java.nio.file.files; import java.nio.file.path; import java.nio.file.paths; import java.nio.file.attribute.filetime; import java.time.localdatetime; import java.time.zoneid; import java.time.zoneoffset; import java.util.arraylist; import java.util.arrays; import java.util.list; /** * see full code here : https://github.com/davinkevin/podcast-server/blob/d927d9b8cb9ea1268af74316cd20b7192ca92da7/src/main/java/lan/dk/podcastserver/utils/multipart/multipartfilesender.java */ public class multipartfilesender { protected final logger logger = loggerfactory.getlogger(this.getclass()); private static final int default_buffer_size = 20480; // ..bytes = 20kb. private static final long default_expire_time = 604800000l; // ..ms = 1 week. private static final string multipart_boundary = "multipart_byteranges"; path filepath; httpservletrequest request; httpservletresponse response; public multipartfilesender() { } public static multipartfilesender frompath(path path) { return new multipartfilesender().setfilepath(path); } public static multipartfilesender fromfile(file file) { return new multipartfilesender().setfilepath(file.topath()); } public static multipartfilesender fromuristring(string uri) { return new multipartfilesender().setfilepath(paths.get(uri)); } //** internal setter **// private multipartfilesender setfilepath(path filepath) { this.filepath = filepath; return this; } public multipartfilesender with(httpservletrequest httprequest) { request = httprequest; return this; } public multipartfilesender with(httpservletresponse httpresponse) { response = httpresponse; return this; } public void serveresource() throws exception { if (response == null || request == null) { return; } if (!files.exists(filepath)) { logger.error("file doesn't exist @ uri : {}", filepath.toabsolutepath().tostring()); response.senderror(httpservletresponse.sc_not_found); return; } long length = files.size(filepath); string filename = filepath.getfilename().tostring(); filetime lastmodifiedobj = files.getlastmodifiedtime(filepath); if (stringutils.isempty(filename) || lastmodifiedobj == null) { response.senderror(httpservletresponse.sc_internal_server_error); return; } long lastmodified = localdatetime.ofinstant(lastmodifiedobj.toinstant(), zoneid.of(zoneoffset.systemdefault().getid())).toepochsecond(zoneoffset.utc); string contenttype = "video/mp4"; // validate request headers caching --------------------------------------------------- // if-none-match header should contain "*" or etag. if so, return 304. string ifnonematch = request.getheader("if-none-match"); if (ifnonematch != null && httputils.matches(ifnonematch, filename)) { response.setheader("etag", filename); // required in 304. response.senderror(httpservletresponse.sc_not_modified); return; } // if-modified-since header should greater lastmodified. if so, return 304. // header ignored if if-none-match header specified. long ifmodifiedsince = request.getdateheader("if-modified-since"); if (ifnonematch == null && ifmodifiedsince != -1 && ifmodifiedsince + 1000 > lastmodified) { response.setheader("etag", filename); // required in 304. response.senderror(httpservletresponse.sc_not_modified); return; } // validate request headers resume ---------------------------------------------------- // if-match header should contain "*" or etag. if not, return 412. string ifmatch = request.getheader("if-match"); if (ifmatch != null && !httputils.matches(ifmatch, filename)) { response.senderror(httpservletresponse.sc_precondition_failed); return; } // if-unmodified-since header should greater lastmodified. if not, return 412. long ifunmodifiedsince = request.getdateheader("if-unmodified-since"); if (ifunmodifiedsince != -1 && ifunmodifiedsince + 1000 <= lastmodified) { response.senderror(httpservletresponse.sc_precondition_failed); return; } // validate , process range ------------------------------------------------------------- // prepare variables. full range represents complete file. range full = new range(0, length - 1, length); list<range> ranges = new arraylist<>(); // validate , process range , 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, 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; } string ifrange = request.getheader("if-range"); if (ifrange != null && !ifrange.equals(filename)) { try { long ifrangetime = request.getdateheader("if-range"); // throws iae if invalid. if (ifrangetime != -1) { ranges.add(full); } } catch (illegalargumentexception ignore) { ranges.add(full); } } // if valid if-range header, process each part of byte range. if (ranges.isempty()) { (string part : range.substring(6).split(",")) { // assuming file length of 100, following examples returns bytes at: // 50-80 (50 80), 40- (40 length=100), -20 (length-20=80 length=100). long start = range.sublong(part, 0, part.indexof("-")); long end = range.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 syntactically valid. if not, 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 range(start, end, length)); } } } // prepare , initialize response -------------------------------------------------------- // content type file name , set content disposition. string disposition = "inline"; // if content type unknown, set default value. // content types, see: http://www.w3schools.com/media/media_mimeref.asp // add new content types, add new mime-mapping entry in web.xml. if (contenttype == null) { contenttype = "application/octet-stream"; } else if (!contenttype.startswith("image")) { // else, expect images, determine content disposition. if content type supported // browser, set inline, else attachment pop 'save as' dialogue. string accept = request.getheader("accept"); disposition = accept != null && httputils.accepts(accept, contenttype) ? "inline" : "attachment"; } logger.debug("content-type : {}", contenttype); // initialize response. response.reset(); response.setbuffersize(default_buffer_size); response.setheader("content-type", contenttype); response.setheader("content-disposition", disposition + ";filename=\"" + filename + "\""); logger.debug("content-disposition : {}", disposition); response.setheader("accept-ranges", "bytes"); response.setheader("etag", filename); response.setdateheader("last-modified", lastmodified); response.setdateheader("expires", system.currenttimemillis() + default_expire_time); // send requested file (part(s)) client ------------------------------------------------ // prepare streams. try (inputstream input = new bufferedinputstream(files.newinputstream(filepath)); outputstream output = response.getoutputstream()) { if (ranges.isempty() || ranges.get(0) == full) { // return full file. logger.info("return full file"); response.setcontenttype(contenttype); response.setheader("content-range", "bytes " + full.start + "-" + full.end + "/" + full.total); response.setheader("content-length", string.valueof(full.length)); range.copy(input, output, length, full.start, full.length); } else if (ranges.size() == 1) { // return single part of file. range r = ranges.get(0); logger.info("return 1 part of file : ({}) ({})", r.start, r.end); 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. range.copy(input, output, length, r.start, r.length); } else { // return multiple parts of file. response.setcontenttype("multipart/byteranges; boundary=" + multipart_boundary); response.setstatus(httpservletresponse.sc_partial_content); // 206. // cast servletoutputstream easy println methods. servletoutputstream sos = (servletoutputstream) output; // copy multi part range. (range r : ranges) { logger.info("return multi part of file : ({}) ({})", r.start, r.end); // add multipart boundary , header fields every range. sos.println(); sos.println("--" + 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. range.copy(input, output, length, r.start, r.length); } // end multipart boundary. sos.println(); sos.println("--" + multipart_boundary + "--"); } } } private static class range { long start; long end; long length; long total; /** * construct byte range. * @param start start of byte range. * @param end end of byte range. * @param total total length of byte source. */ public range(long start, long end, long total) { this.start = start; this.end = end; this.length = end - start + 1; this.total = total; } public static long sublong(string value, int beginindex, int endindex) { string substring = value.substring(beginindex, endindex); return (substring.length() > 0) ? long.parselong(substring) : -1; } private static void copy(inputstream input, outputstream output, long inputsize, long start, long length) throws ioexception { byte[] buffer = new byte[default_buffer_size]; int read; if (inputsize == length) { // write full range. while ((read = input.read(buffer)) > 0) { output.write(buffer, 0, read); output.flush(); } } else { input.skip(start); long toread = length; while ((read = input.read(buffer)) > 0) { if ((toread -= read) > 0) { output.write(buffer, 0, read); output.flush(); } else { output.write(buffer, 0, (int) toread + read); output.flush(); break; } } } } } private static class httputils { /** * returns true if given accept header accepts given value. * @param acceptheader accept header. * @param toaccept value accepted. * @return true if given accept header accepts given value. */ public static boolean accepts(string acceptheader, string toaccept) { string[] acceptvalues = acceptheader.split("\\s*(,|;)\\s*"); arrays.sort(acceptvalues); return arrays.binarysearch(acceptvalues, toaccept) > -1 || arrays.binarysearch(acceptvalues, toaccept.replaceall("/.*$", "/*")) > -1 || arrays.binarysearch(acceptvalues, "*/*") > -1; } /** * returns true if given match header matches given value. * @param matchheader match header. * @param tomatch value matched. * @return true if given match header matches given value. */ public static boolean matches(string matchheader, string tomatch) { string[] matchvalues = matchheader.split("\\s*,\\s*"); arrays.sort(matchvalues); return arrays.binarysearch(matchvalues, tomatch) > -1 || arrays.binarysearch(matchvalues, "*") > -1; } } }
try surely compatible case. thank you.
Comments
Post a Comment