File downloading is the most basic service provided by WEB websites, but do you know how HTTP resumed downloading is implemented?
Background
In the past two days, I have been implementing an online audio and video playback based on HTML5. Since the file is stored in the corporate network disk and HTTP is not reachable, a program is needed to read the file and download the HTTP protocol.It goes without saying that using Java to download files is just a matter of reading the file and writing it to the response through a binary stream. The H5 player call can also be played; however, when I control the progress forward and backward, the problem comes, and it has no effect at all! Is it still called a player if there is no fast-forward player?
Analysis
First of all, when we see that the player cannot obtain the time length of the audio and video files, we naturally think of the Content-Length attribute. In the background, we obtain the file length through file.length() and set it to Content-Length (the code is as follows) ), the length of the audio and video can be displayed in the foreground player, and it can be fast forwarded; however, when I rewind, it is still invalid and an error is reported in the background.
response.addHeader("Content-Length", file.length());
I changed an HTTP file for comparison testing and found that direct HTTP access can fast forward and rewind normally. After careful analysis of the request and response headers of the two, I found the difference, the request parameters There are more attributes as shown in the figure below. The attribute table name needs to be the resource range obtained from the server. By default, it starts from the first byte. Fast forward and fast rewind actually determine the starting point you expect by specifying the Range attribute. starting point.
However, this attribute is in the request header. How does the client know to add this attribute? Continuing to search, I found the Accept-Ranges attribute. The attribute value is bytes, which indicates whether to accept a request to obtain a part of an entity (such as a part of a file). bytes: indicates acceptance, none: indicates not acceptance. There is another attribute in the corresponding response, Content-Range, which indicates which part of the entire object the partial object contained in the response is. The complete response header is as follows:
Solution
Based on the above analysis, we know how to handle it on the server side. First, add Accept-Ranges to the response header.
response.setHeader("Accept-Ranges", "bytes");
Then determine whether the Range attribute exists in the request, that is, whether the specified starting point exists. If it exists, jump directly to the target starting point through the skip of the stream, and finally add the Content-Range attribute table name of the start and end of the current block, complete code As follows:
stream = new FileInputStream(file); if(request.getHeader("Range") != null) //客户端请求的下载的文件块的开始字节 { //从请求中得到开始的字节 //请求的格式是: //Range: bytes=[文件块的开始字节]- String range = StringUtils.substringBetween(request.getHeader("Range"), "bytes=", "-"); long start = Long.parseLong(range); //下载的文件(或块)长度 //响应的格式是: //Content-Length: [文件的总大小] - [客户端请求的下载的文件块的开始字节] response.setHeader("Content-Length", String.valueOf(fileSize - start)); if (start != 0) { //要设置状态 //响应的格式是: //HTTP/1.1 206 Partial Content response.setStatus(javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT);//206 //不是从最开始下载, //响应的格式是: //Content-Range: bytes [文件块的开始字节]-[文件的总大小 - 1]/[文件的总大小] response.setHeader("Content-Range","bytes " + start + "-" + String.valueOf(fileSize - 1) + "/" + String.valueOf(fileSize)); stream.skip(start); } } responseBinaryStream(response, this.getContentType(FilenameUtils.getExtension(fileName)), stream);