PHP 스크립트로 대용량 파일을 다운로드하려면 파일 전체를 한 번에 메모리에 올리지 않고, 일정 크기씩 나누어 읽어 전송하는 '청크(chunk) 단위 처리' 방식을 사용하는 것이 좋습니다. 이를 구현한 코드는 다음과 같습니다.
예제
<?php
function readfile_chunked($filename,$retbytes=true) {
$chunksize = 1*(1024*1024); // 한 번에 읽어들일 청크 크기(바이트)
$buffer = '';
$cnt =0;
$handle = fopen($filename, 'rb');
if ($handle === false) {
return false;
}
while (!feof($handle)) {
$buffer = fread($handle, $chunksize);
echo $buffer;
if ($retbytes) {
$cnt += strlen($buffer);
}
}
$status = fclose($handle);
if ($retbytes && $status) {
return $cnt; // readfile()처럼 전송된 바이트 수를 반환합니다.
}
return $status;
}
?>출력 결과
위 코드를 실행하면 다음과 같은 결과를 얻을 수 있습니다.
대용량 파일이 정상적으로 다운로드됩니다.
코드 설명
사용자가 직접 정의한 'readfile_chunked' 함수는 두 개의 매개변수를 받습니다. 첫 번째는 다운로드할 파일 이름이며, 두 번째는 전송된 바이트 수를 반환할지 여부를 나타내는 값으로 기본값은 'true'입니다. 이 값이 true이면 대용량 파일이 성공적으로 다운로드되었음을 의미합니다.
변수 'chunksize'에는 한 번에 읽어들일 청크당 바이트 수가 선언되어 있습니다. 위 예제에서는 1MB(1024 × 1024 바이트)로 설정되어 있습니다. 'buffer' 변수는 빈 문자열로 초기화되고, 'cnt'는 전송된 총 바이트 수를 세기 위해 0으로 설정됩니다. 이후 파일을 바이너리 읽기 모드('rb')로 열어 'handle' 변수에 할당합니다.
while 루프는 'handle'이 파일 끝(EOF)에 도달할 때까지 반복 실행되며, 지정된 청크 크기만큼 파일 내용을 읽어 들입니다. 읽어들인 내용은 곧바로 화면에 출력되어 클라이언트로 전송됩니다. 만약 두 번째 매개변수인 'retbytes'의 값이 true라면, 버퍼의 길이가 'cnt' 변수에 계속 더해집니다.
루프가 종료되면 파일을 닫고, 'retbytes'가 true이면서 파일 닫기 작업이 성공했을 경우 전송된 총 바이트 수인 'cnt' 값을 반환합니다. 그렇지 않은 경우에는 파일 닫기 작업의 상태를 반환합니다.
참고 사항
이 방식은 PHP의 readfile() 함수처럼 동작하지만, 파일 전체를 메모리에 한꺼번에 로드하지 않기 때문에 수백 MB 이상의 대용량 파일도 메모리 제한(memory_limit)에 걸리지 않고 안정적으로 전송할 수 있다는 장점이 있습니다. 필요에 따라 청크 크기를 조절하여 서버 환경에 맞게 최적화할 수 있습니다.