Computer >> 컴퓨터 >  >> 프로그래밍 >> PHP

PHP에서 500MB 이상 대용량 파일을 업로드하는 2가지 방법

PHP에서 500MB 이상 대용량 파일 업로드하기

PHP에서 대용량 파일을 업로드하는 방법은 크게 두 가지가 있습니다. 하나는 서버의 업로드 제한 설정 자체를 늘리는 방식이고, 다른 하나는 파일을 작은 조각으로 나누어 전송한 뒤 다시 하나로 합치는 청크(chunk) 업로드 방식입니다.

  • php.ini 파일에서 upload_max_filesize 제한을 변경하는 방법
  • 파일을 여러 개의 작은 조각으로 분할해 업로드하고, 업로드가 완료되면 조각들을 다시 조립하는 청크 업로드 방식

1. php.ini 파일 수정

php.ini 파일에서 아래 항목을 찾아 값을 수정하거나 추가합니다.

upload_max_filesize = 50M
post_max_size = 50M
max_input_time = 300
max_execution_time = 300

각 항목의 의미는 다음과 같습니다.

  • upload_max_filesize : 업로드할 수 있는 파일의 최대 크기
  • post_max_size : POST 요청으로 전송 가능한 데이터의 최대 크기(upload_max_filesize와 같거나 더 크게 설정해야 함)
  • max_input_time : 입력 데이터를 파싱하는 데 허용되는 최대 시간(초)
  • max_execution_time : 스크립트가 실행될 수 있는 최대 시간(초)

다만 이 방법은 서버 전체 설정을 변경하는 것이므로, 같은 서버에서 운영 중인 다른 프로젝트에도 영향을 줄 수 있다는 점을 감안해 신중하게 적용하는 것이 좋습니다.

2. .htaccess 파일 수정

php.ini 파일에 직접 접근하기 어려운 호스팅 환경이라면, 웹 루트 디렉터리의 .htaccess 파일에 아래 설정을 추가하는 방법도 있습니다.

php_value upload_max_filesize 50M
php_value post_max_size 50M
php_value max_input_time 300
php_value max_execution_time 300

3. 코드 내에서 ini_set()으로 설정 변경

업로드를 처리하는 PHP 스크립트 상단에서 ini_set() 함수를 호출하면, 해당 스크립트에 한해서만 업로드 관련 제한을 일시적으로 조정할 수 있습니다.

<?php
    // 업로드 제한 변경
    ini_set('upload_max_filesize', '50M');
    ini_set('post_max_size', '50M');
    ini_set('max_input_time', 300);
    ini_set('max_execution_time', 300);
    // 저장 위치(목적지 폴더) 지정
    $source = $_FILES["file-upload"]["tmp_name"];
    $destination = $_FILES["file-upload"]["name"];
    // 업로드된 파일을 목적지로 이동
    move_uploaded_file($source, $destination);
?>

4. 청크(Chunk) 단위 업로드

이 방식은 대용량 파일을 여러 개의 작은 조각으로 나눈 뒤 순차적으로 전송하고, 서버에서 이 조각들을 하나의 파일로 병합하는 방법입니다. 클라이언트 쪽에서는 'Plupload'와 같은 자바스크립트 라이브러리를 활용하면 비교적 간단하게 구현할 수 있습니다. 아래는 서버 측(PHP) 처리 코드 예제입니다.

<?php
    // 응답 처리 함수
    function verbose($ok=1,$info=""){
        // 업로드 실패 시 400 에러 반환
        if ($ok==0) { http_response_code(400); }
        die(json_encode(["ok"=>$ok, "info"=>$info]));
    }
    // 유효하지 않은 업로드
    if (empty($_FILES) || $_FILES['file']['error']) {
        verbose(0, "Failed to move uploaded file.");
    }
    // 업로드 경로 지정
    $filePath = __DIR__ . DIRECTORY_SEPARATOR . "uploads";
    if (!file_exists($filePath)) {
        if (!mkdir($filePath, 0777, true)) {
            verbose(0, "Failed to create $filePath");
        }
    }
    $fileName = isset($_REQUEST["name"]) ? $_REQUEST["name"] : $_FILES["file"]["name"];
    $filePath = $filePath . DIRECTORY_SEPARATOR . $fileName;
    // 청크 처리
    $chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0;
    $chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 0;
    $out = @fopen("{$filePath}.part", $chunk == 0 ? "wb" : "ab");
    if ($out) {
        $in = @fopen($_FILES['file']['tmp_name'], "rb");
        if ($in) {
            while ($buff = fread($in, 4096)) { fwrite($out, $buff); }
        } else {
            verbose(0, "Failed to open input stream");
        }
        @fclose($in);
        @fclose($out);
        @unlink($_FILES['file']['tmp_name']);
    } else {
        verbose(0, "Failed to open output stream");
    }
    // 모든 청크가 업로드되었는지 확인 후 임시 파일을 최종 파일로 변경
    if (!$chunks || $chunk == $chunks - 1) {
        rename("{$filePath}.part", $filePath);
    }
    verbose(1, "Upload OK");
?>

마무리

위와 같이 청크 업로드 방식을 적용하면 500MB가 넘는 대용량 파일도 시간 초과나 메모리 부족 없이 성공적으로 업로드할 수 있습니다. 실제 운영 환경에서는 nginx의 client_max_body_size, Apache의 LimitRequestBody처럼 웹 서버 차원의 요청 크기 제한도 함께 확인하고 조정해야 하며, 업로드되는 파일명 검증과 저장 경로 보안 처리도 반드시 병행하는 것이 안전합니다.