파일 다운로드는 WEB 웹사이트에서 제공하는 가장 기본적인 서비스인데, HTTP 재개 다운로드가 어떻게 구현되는지 알고 계시나요?
배경
지난 이틀 동안 HTML5 기반의 온라인 오디오 및 비디오 재생을 구현했습니다. 파일이 회사 네트워크 디스크에 저장되어 있으므로 HTTP에 연결할 수 없습니다. 파일을 읽고 HTTP 프로토콜을 통해 다운로드하려면 프로그램이 필요합니다.말할 필요도 없이 Java를 사용하여 파일을 다운로드하는 것은 파일을 읽고 바이너리 스트림을 통해 응답에 쓰기만 하면 됩니다. H5 플레이어 호출도 재생할 수 있지만 진행 상황을 앞뒤로 제어하면 문제가 발생하고 전혀 효과가 없습니다! 빨리감기 플레이어가 없는데도 플레이어라고 부르나요?
분석
우선 플레이어가 오디오와 비디오 파일의 길이를 알 수 없다는 점을 보면 콘텐츠를 생각하는 것이 당연하다. -Length 속성. 백그라운드에서 file.length()는 파일 길이를 가져와서 Content-Length로 설정합니다(코드는 다음과 같습니다). 오디오 및 비디오의 길이는 포그라운드 플레이어에 표시될 수 있습니다. 빨리 감기; 그러나 되감기하면 여전히 유효하지 않으며 백그라운드에서 오류가 보고됩니다.
response.addHeader("Content-Length", file.length());
비교 테스트를 위해 HTTP 파일을 변경했는데 직접 HTTP 액세스가 정상적으로 빨리 감기 및 되감기가 가능하다는 사실을 발견했습니다. 두 헤더의 요청 및 응답 헤더를 주의 깊게 분석한 결과 요청 매개변수의 차이점을 발견했습니다. 아래 그림과 같이 추가 속성이 있습니다. 속성 테이블 이름은 서버에서 가져온 리소스 범위여야 합니다. 기본적으로 첫 번째 바이트부터 시작하여 실제로 예상하는 시작점을 지정합니다. 범위 속성.
그런데 이 속성은 요청 헤더에 있습니다. 클라이언트는 이 속성을 추가하는지 어떻게 알 수 있나요? 계속 검색하면서 속성 값은 엔터티의 일부(예: 파일의 일부)를 얻기 위한 요청을 수락할지 여부를 나타내는 바이트입니다. bytes: 승인을 나타냅니다. none: 승인되지 않음을 나타냅니다. 이에 대응하여 응답에는 또 다른 속성인 Content-Range가 있는데, 이는 응답에 포함된 부분 객체가 전체 객체 중 어느 부분인지를 나타냅니다. 전체 응답 헤더는 다음과 같습니다.
해결책
위의 분석을 바탕으로 서버 측에서 처리하는 방법을 알고 있습니다. 먼저 추가합니다. 응답 헤더 -Ranges를 수락합니다.
response.setHeader("Accept-Ranges", "bytes");
그런 다음 요청에 Range 속성이 있는지, 즉 지정된 시작점이 존재하는지 확인합니다. 존재하는 경우 스트림 건너뛰기를 통해 대상 시작점으로 직접 점프하고 마지막으로 Content-Range 속성 테이블 이름을 현재 블록의 시작점과 끝부분으로 지정하고 전체 코드는 다음과 같습니다.
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);