개발 꿀팁/PHP

php 파일 다운로드 클래스 중단점 지원

Jammie 2022. 8. 17. 14:05
반응형

php는 중단점 속행을 지원하며, 주로 HTTP 프로토콜의 header HTTP_RANGE에 의해 구현됩니다.

HTTP 중단점 속행 원리
HTTP 헤더 Range, Content-Range( )
HTTP 헤더 중 일반적으로 중단점 다운로드 시에만 Range 및 Content-Range 엔티티 헤더를 사용합니다.
Range 사용자 요청 헤더에서 첫 번째 바이트의 위치와 마지막 바이트의 위치를 지정합니다. (Range: 200)-300)
Content-Range는 응답 헤더에 사용됩니다.

전체 파일 다운로드 요청:
GET/test.rar HTTP/1.1
Connection: close
Host: 116.1.219.219.
Range: bytes=0-801 // 전체 파일 다운로드 요청은 bytes=0- 또는 사용하지 않음이 부분

일반 정상 응답
HTTP/1.1 200 OK
Content-Length:801
Content-Type: application/octet-stream
Content-Range: bytes 0-800/801 //801:파일 총크기

FileDownload.class.php

<?php
/**php 다운로드 클래스, 중단점 속행 지원
*   Date:   2013-06-30
*   Author: fdipzone
*   Ver:    1.0
*
*   Func:
*   download: 파일 다운로드
*   setSpeed: 다운로드 속도 설정
*   getRange: header에서 Range
*/
 
class FileDownload{ // class start
 
    private $_speed = 512;   // 다운로드 속도
 
 
    /** 다운로드
    * @param String  $file   다운로드할 파일 경로
    * @param String  $name   파일 이름, 비어 있으면 다운로드한 파일 이름과 같습니다
    * @param boolean $reload 중단점 속행 설정 여부
    */
    public function download($file, $name='', $reload=false){
        if(file_exists($file)){
            if($name==''){
                $name = basename($file);
            }
 
            $fp = fopen($file, 'rb');
            $file_size = filesize($file);
            $ranges = $this->getRange($file_size);
 
            header('cache-control:public');
            header('content-type:application/octet-stream');
            header('content-disposition:attachment; filename='.$name);
 
            if($reload && $ranges!=null){ // 속전 사용
                header('HTTP/1.1 206 Partial Content');
                header('Accept-Ranges:bytes');
                
                // 잔장
                header(sprintf('content-length:%u',$ranges['end']-$ranges['start']));
                
                //레인지 정보
                header(sprintf('content-range:bytes %s-%s/%s', $ranges['start'], $ranges['end'], $file_size));
                
                // fp 포인터가 중단점 위치로 이동
                fseek($fp, sprintf('%u', $ranges['start']));
            }else{
                header('HTTP/1.1 200 OK');
                header('content-length:'.$file_size);
            }
 
            while(!feof($fp)){
                echo fread($fp, round($this->_speed*1024,0));
                ob_flush();
                //sleep(1); // 테스트에 사용, 다운로드 속도 감소
            }
 
            ($fp!=null) && fclose($fp);
 
        }else{
            return '';
        }
    }
 
 
    /** 다운로드 속도 설정
    * @param int $speed
    */
    public function setSpeed($speed){
        if(is_numeric($speed) && $speed>16 && $speed<4096){
            $this->_speed = $speed;
        }
    }
 
 
    /** 헤더 레인지 정보 가져오기
    * @param  int   $file_size 파일 크기
    * @return Array
    */
    private function getRange($file_size){
        if(isset($_SERVER['HTTP_RANGE']) && !empty($_SERVER['HTTP_RANGE'])){
            $range = $_SERVER['HTTP_RANGE'];
            $range = preg_replace('/[\s|,].*/', '', $range);
            $range = explode('-', substr($range, 6));
            if(count($range)<2){
                $range[1] = $file_size;
            }
            $range = array_combine(array('start','end'), $range);
            if(empty($range['start'])){
                $range['start'] = 0;
            }
            if(empty($range['end'])){
                $range['end'] = $file_size;
            }
            return $range;
        }
        return null;
    }
 
} // class end
 
?>

demo

<?php
 
require('FileDownload.class.php');
$file = 'book.zip';
$name = time().'.zip';
$obj = new FileDownload();
$flag = $obj->download($file, $name);
//$flag = $obj->download($file, $name, true); //중단점 속전
 
if(!$flag){
    echo 'file not exists';
}
 
?>

중단점 속행 테스트 방법:
리눅스 wg 사용et 명령 다운로드 테스트하기, wget-c-O file http://xxx

1.먼저 중단점 속행하기
$flag = $obj->download ($file, $name)

fdipzone@ubuntu:~/Downloads$ wget -O test.rar http://demo.fdipzone.com/demo.php
--2013-06-30 16:52:44--  http://demo.fdipzone.com/demo.php
호스트 구문 분석 중 demo.fdipzone.com... 127.0.0.1
연결 중 demo.fdipzone.com|127.0.0.1|:80... 연결됨。
HTTP 요청, 응답 대기 중... 200 OK
길이: 10445120 (10.0M) [application/octet-stream]
저장 중: “test.rar”

30% [============================>                                                                     ] 3,146,580    513K/s  시기를 가늠하다 14s
^C
fdipzone@ubuntu:~/Downloads$ wget -c -O test.rar http://demo.fdipzone.com/demo.php
--2013-06-30 16:52:57--  http://demo.fdipzone.com/demo.php
호스트 구문 분석 중 demo.fdipzone.com... 127.0.0.1
연결 중 demo.fdipzone.com|127.0.0.1|:80... 연결됨。
HTTP 요청, 응답 대기 중... 200 OK
길이: 10445120 (10.0M) [application/octet-stream]
저장 중: “test.rar”

30% [============================>                                                                     ] 3,146,580    515K/s  시기를 가늠하다 14s
^C

볼 수 있습니다. wget-c는 간헐적으로 전송할 수 없습니다

2.브레이크 포인트 속행 열기
$flag = $obj->download ($file, $name, true)

fdipzone@ubuntu:~/Downloads$ wget -O test.rar http://demo.fdipzone.com/demo.php
--2013-06-30 16:53:19--  http://demo.fdipzone.com/demo.php
호스트 구문 분석 중 demo.fdipzone.com... 127.0.0.1
연결됨 demo.fdipzone.com|127.0.0.1|:80... 연결됨v。
HTTP 요청, 응답 대기 중... 200 OK
길이: 10445120 (10.0M) [application/octet-stream]
저장 중: “test.rar”

20% [==================>                                                                               ] 2,097,720    516K/s  시기를 가늠하다 16s
^C
fdipzone@ubuntu:~/Downloads$ wget -c -O test.rar http://demo.fdipzone.com/demo.php
--2013-06-30 16:53:31--  http://demo.fdipzone.com/demo.php
호스트 구문 분석 중 demo.fdipzone.com... 127.0.0.1
연결 중 demo.fdipzone.com|127.0.0.1|:80... 연결됨。
HTTP 요청, 응답 대기 중... 206 Partial Content
길이: 10445121 (10.0M),7822971 (7.5M) 바이트 잉여 [application/octet-stream]
저장 중: “test.rar”

100%[++++++++++++++++++++++++=========================================================================>] 10,445,121   543K/s   시기를 가늠하다 14s   

2013-06-30 16:53:45 (543 KB/s) - 저장됨 “test.rar” [10445121/10445121])

중단점 위치(%20)부터 다운로드를 시작하는 것을 볼 수 있습니다。
반응형