Serving video files from Play 2.1

I am trying to create a controller method that serves a video file supported by some database record similar to CMS. My controller method is as follows:

def getVideo(id: Int) = DBAction { request => implicit dbSession => { for { dbFile <- fetchDBFile(id) fsFile <- fetchFilesystemFile(dbFile) rangeOpt <- request.headers.get(RANGE).map(_.replaceAll("bytes=", "").split("-").toList match { case rangeStart :: rangeEnd :: Nil => Some(rangeStart.toLong, rangeEnd.toLong) case rangeStart :: Nil => Some(rangeStart.toLong, fsFile.length()) case _ => None }) (rangeStart, rangeEnd) <- rangeOpt } yield SimpleResult( header = ResponseHeader( status = PARTIAL_CONTENT, headers = Map( CONTENT_TYPE -> MimeTypes.forExtension("mp4").get, ACCEPT_RANGES -> "bytes", DATE -> new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz").format(new Date()), CONTENT_LENGTH -> fsFile.length.toString, CONTENT_RANGE -> s"bytes $rangeStart-$rangeEnd/${fsFile.length}", CONNECTION -> "keep-alive" ) ), body = Enumerator.fromStream(new FileInputStream(fsFile)) ) } getOrElse { NotFound } } 

It is mainly based on two sources for implementing logic to handle a specific request for the range of bytes needed to serve the video.

When using Chrome or Safari on OS X to access this controller method, the developer tools report that the request is canceled - no response was received, be it 200 or 404. I confirmed that SimpleResponse actually returns this controller action for requests that I expect that he will give a good answer, but either Play will not complete the answer, or my browsers will not confirm it. Am I doing something wrong here in response, or am I stumbled upon a mistake in the framework?

My version of the game is 2.1.3.

+4
source share
1 answer

The reasons Chrome cancels requests.

The sources you used are more perfect. I am showing you the code that I noticed:

The response code is not always 206 PARTIAL_CONTENT:

 val responseCode = if (rangeStart != 0 || rangeEnd != fileLength - 1) 206 else 200 

Missing bytes missing:

 val stream = new FileInputStream(file) stream.skip(rangeStart) # range starts defaults to 0 

The range of content can be full or partial:

 val contentLength = if (responseCode == 206) (rangeEnd - rangeStart + 1) else fileLength 

Therefore, you should not send the Content-Range header in each case.

+3
source

Source: https://habr.com/ru/post/1497971/


All Articles