본문 바로가기
Program/PHP

PHP 청크 업로드 / 파일 분할 업로드 예제

by 한빛가람 2024. 2. 5.
반응형

클라우드플레어 CDN을 사용하는데, 100MB 제한이 걸려서 100MB 이상의 파일을 업로드 하려니까 꽤나 골치였습니다.

이 코드는 보안상, 구조상 문제점이 있어서 각자 사정에 맞게 수정해야하지만, 동작이 잘 확인되어 공유합니다.

1. upload.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>분할 업로드 예제</title>
</head>
<body>
    <form id="uploadForm" action="upload.php" method="post" enctype="multipart/form-data">
        <input type="file" name="file" id="fileInput" />
        <button type="button" onclick="uploadFile()">파일 업로드</button>
        <progress id="progressBar" value="0" max="100"></progress>
    </form>

    <script>
        function uploadFile() {
            var fileInput = document.getElementById('fileInput');
            var file = fileInput.files[0];
            var fileSize = file.size;

            var chunkSize = 1024 * 1024; // 1MB chunks
            var chunks = Math.ceil(fileSize / chunkSize);
            var currentChunk = 0;

            var progressBar = document.getElementById('progressBar');

            function uploadNextChunk() {
                var xhr = new XMLHttpRequest();
                var formData = new FormData();
                formData.append('file', file.slice(currentChunk * chunkSize, (currentChunk + 1) * chunkSize));
                formData.append('chunks', chunks);
                formData.append('currentChunk', currentChunk);

                xhr.open('POST', 'upload.php', true);

                xhr.onload = function () {
                    if (xhr.status == 200) {
                        currentChunk++;

                        var progress = (currentChunk / chunks) * 100;
                        progressBar.value = progress;

                        if (currentChunk < chunks) {
                            uploadNextChunk();
                        } else {
                            alert('업로드 완료!');
                        }
                    } else {
                        alert('업로드 중 오류 발생');
                    }
                };

                xhr.send(formData);
            }

            uploadNextChunk();
        }
    </script>
</body>
</html>

 

2. upload.php

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $chunks = isset($_POST['chunks']) ? intval($_POST['chunks']) : 0;
    $currentChunk = isset($_POST['currentChunk']) ? intval($_POST['currentChunk']) : 0;

    $uploadDir = 'uploads/';
    $fileName = 'uploaded_file';

    $tempFilePath = $uploadDir . $fileName . '_' . $currentChunk;

    move_uploaded_file($_FILES['file']['tmp_name'], $tempFilePath);

    if ($currentChunk == $chunks - 1) {
        // All chunks uploaded, combine them into the final file
        $finalFilePath = $uploadDir . $fileName;
        $fileHandle = fopen($finalFilePath, 'a');

        for ($i = 0; $i < $chunks; $i++) {
            $chunkFilePath = $uploadDir . $fileName . '_' . $i;
            $chunkData = file_get_contents($chunkFilePath);
            fwrite($fileHandle, $chunkData);
            unlink($chunkFilePath); // Remove temporary chunk files
        }

        fclose($fileHandle);
    }

    echo 'success';
} else {
    echo 'Invalid request';
}
?>

 

사용 전에, upload.php 가 위치한 디렉토리에 uploads 라는 디렉토리만 하나 만들어주시면 됩니다.

 

더불어서 위의 HTML 코드에는 업로드가 제대로 되고 있는지, 아닌지에 대한 상태값이 없어서 이 부분은 직접 추가를 해주셔야합니다.

200번 status 코드가 넘어오면 그냥 업로드가 되었다 라고 판단하는 식이라서, 위의 올바르지 않은 요청(Invaild request)가 띄워지더라도 정상 업로드로 인식합니다.

 

위의 코드를 그대로 사용하시려거든, echo Invaild request 상단에 이 구문을 삽입해주세요.

http_response_code(500);

 

자바스크립트 코드가 status code가 200이면 재귀를 하고 있기 때문에, 코드가 200만 아니면 작동을 멈추게 되어 있습니다.

잘 활용하시면 쓸만한 코드가 될 수 있음은 자명합니다.

 

부디 많은 분들께서 자료로 활용해주시기를 빌겠습니다.

반응형
크리에이티브 커먼즈 라이선스
한빛가람 필자가 게시한 대부분의 블로그 게시물은 크리에이티브 커먼즈 저작자표시-비영리-동일조건변경허락 4.0 국제 라이선스에 따라 이용할 수 있습니다. 게시물에 CCL이 적용되지 아니하다고 적혀있는 경우엔 본 라이선스 적용 대상이 아닙니다.

댓글