본문 바로가기

Web/Web 개발

파일 업로드/다운로드 기능

서론

오늘은 파일 업로드 및 다운로드 기능을 구현해보자

 


최종화면

 


 

1. 파일 업로드

1.1. board_write.php (글 작성 기능 페이지)

<form action="/board/board_write_proc.php" method="POST" enctype="multipart/form-data">
	<input type="text" name="title" placeholder="제목" required>
	<textarea name="content" placeholder="내용을 입력하세요."></textarea>
	<input type="file" name="upload_file" id="file_btn">
	<div class="write_btn2">
		<button type="submit">작성하기</button>
	</div>
</form>
  • <form>태그에 enctype="multipart/form-data"를 넣어줬다.
    multipart/form-data는 파일,이미지 등 여러 데이터를 각 파트로 나눠 경계(boundary)로 구분해 전송하는 폼 데이터 포맷으로, 문자 자체를 변환하는 인코딩은 적용하지 않는다.
  • <input> 태그에 type이 file로 하나 추가해주고, 이후 php로 처리하기 위해 upload_file값을 지정해준다.

 

1.2. board_write_proc.php (글 작성 처리기능)

    $title = $_POST['title'];
    $content = $_POST['content'];
    $author = $_SESSION['user_id'];

    if (isset($_FILES['upload_file']) && $_FILES['upload_file']['error'] !== UPLOAD_ERR_NO_FILE) {
        // 파일 업로드가 있는 경우만 확장자 검사
        $filename = $_FILES['upload_file']['name'];//사용자가 올린 파일 이름 (ex: hello.jpg)
        $tmp_path = $_FILES['upload_file']['tmp_name'];//서버가 저장하는 임시경로
        $error = $_FILES['upload_file']['error'];//파일업로드 시 에러코드

        $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));//확장자 소문자로 변환
        $allow_ext = ['jpg', 'jpeg', 'png', 'gif', 'mp4', 'txt', 'pdf', 'doc', 'hwp', 'ppt'];//허용된 확장자

        // 허용되지 않은 확장자 알림
        if (!in_array($ext, $allow_ext)) {
            echo "<script>alert('허용되지 않는 파일 형식입니다.'); history.back();</script>";
            exit;
        }
  • input으로 받은 값들을 각각 변수에 할당해준다.
  • 파일이 업로드 됐을때 그리고 파일에러가 4(파일없음)일 때 확장자 검사를 진행한다.
  • 파일 이름을 $filename으로 변수를 할당해준다.
  • 웹 서버는 파일을 업로드 할 때 임시 디렉토리에 저장한다. 이 때 생성 되는 경로를 $tmp_path로 할당해줬다.
    웹 서버가 임시로 저장하는 이유?
    →여러 사용자가 동시에 파일을 전송해도 서버가 안정적으로 파일을 수신·검증한 뒤
    최종 저장 단계로 안전하게 처리하기 위해서다.
  • 게시글 작성이 안될 시 이유를 알려주기 위해서 $error에 에러코드를 할당했다.
  • 확장자를 소문자로 변환 후 $ext에 할당해줬다.
  • 허용이 가능한 확장자만 사용하게 화이트 리스트를 만들어줬다 (내가 주로 자주 봤던 확장자로만 설정했다..)

 

$unique_idx = uniqid('', true); // 랜덤값
$unique_filename = $unique_idx . '.' . $ext;//랜덤값+확장자
$file_path = $_SERVER['DOCUMENT_ROOT']."/board/uploads/".$unique_filename; //저장할 파일 경로
  • 파일들이 중복되지 않게 uniqid함수를 사용해서 랜덤값을 $unique_idx에 할당했다.
  • 화이트 리스트를 통과한 확장자를 랜덤값 뒤에 붙여줬다.
  • 파일을 저장할 경로를 $file_path에 할당해줬다.
        if ($error === UPLOAD_ERR_OK) {
            if (move_uploaded_file($tmp_path, $file_path)) {
                // 파일과 함께 저장
                $sql = "INSERT INTO board (title,author,content,post_date,file,views,likes) VALUES ('$title','$author','$content',now(),'$unique_filename',0,0)";
            }else {
                // 파일 이동 실패 → 파일 없이 저장
                $sql = "INSERT INTO board (title,author,content,post_date,views,likes) VALUES ('$title','$author','$content',now(),0,0)";
            }
        }else if ($error === UPLOAD_ERR_INI_SIZE) {
            echo "<script>alert('파일 사이즈가 너무 큽니다.'); history.back();</script>";
            exit;
        }
    }else {
    // 파일이 첨부되지 않은 경우
    $sql = "INSERT INTO board (title,author,content,post_date,views,likes) VALUES ('$title','$author','$content',now(),0,0)";
    }

    $result = $mysqli->query($sql);
        if ($result) {
            echo "<script>
                    alert('게시글이 작성되었습니다.');
                    location.href='/board/board.php';
                </script>";
        } else {
            echo "관리자에게 문의하세요";
        }
?>
  • 업로드 에러가 없는 경우 즉, 파일 업로드가 된 경우에 move_uploaded_file함수를 이용해 
    임시 경로에서 저장할 경로로 옮겨줬다.
  • 파일이 있을 때는 파일과 함께 DB에 저장
  • 파일이 이동 시 실패했을 때 파일 없이 DB에 저장
  • 에러가 사이즈에러와 같다면 "파일 사이즈가 너무 큽니다" 알림창 출력 
  • 파일이 없을 때도 파일 없이 DB에 저장
  • 이렇게 SQL구문을 만들어놓고 실행
  • DB구문이 성공했다면 게시글 작성 완료!
  • UPLOAD_ERR_OK 등 에러메세지는 밑에 링크 참고
    https://www.php.net/manual/en/features.file-upload.errors.php
 

PHP: Error Messages Explained - Manual

PHP is a popular general-purpose scripting language that powers everything from your blog to the most popular websites in the world.

www.php.net

 

1.3. 테스트

➡️테스트 파일을 만들어 준 뒤 업로드

➡️목록에 잘 출력된다.

➡️하지만 파일이 생성되지 않은 것을 확인했고 권한을 바꿔줬다.

➡️그런데 여전히 생성이 되지 않았고 찾아보니 웹 서버는 소유자가 아니었다

➡️웹 서버 계정을 알아냈고, uploads 디렉토리의 그룹 권한에 추가해줬다.

➡️uploads 디렉토리의 권한도 775로 그룹권한에 쓰기 기능을 추가해줬다.
→디렉토리는 쓰기권한이 있어야 생성/수정/삭제 등을 할 수 있다.
→파일 업로드는 파일을 해당 디렉토리에 생성을 해야해서 권한을 부여해야한다.

 

➡️다시 작성해보니 uploads 디렉토리에 파일이 생긴 것을 확인할 수 있다.

 


 

2. 파일 다운로드

2.1. board_read.php( 게시글 읽기 기능)

	<div class="read_wrapper">
        <div class="read_title">
            <p><?=$row['title'] ?>
            <div class="post_meta">
                작성자: <?=$row['author']?> | 작성일: <?=$row['post_date']?>
            </div>
        </div>
        <div class="read_content">
            <p><?=nl2br($row['content'])?></p>
        </div>
        <div class="download_file">
            <a href="/board/uploads/<?= $row['file']?>"download><?= $row['file']?></a>
        </div>
        <div class="btn3">
            <form action="/board/board_modify.php">
                <button type="submit">수정하기</button>
            </form>
            <form action="/board/board_delete.php">
                <button type="submit">삭제하기</button>
            </form>
        </div>
    </div>
  • php echo를 이용해 화면에 출력했다.
  • <a>태그를 사용해서 해당경로를 링크로 출력한다.
  • <a>태그 download 속성을 이용해 다운로드 할 수 있도록 설정했다.
  • 게시글 조회 후 링크를 클릭하면 다운로드 창이 열리고 사용자가 원하는 루트에 다운로드 할 수 있게 된다.

2.2. 테스트

➡️아까 작성한 글 조회하면 파일이 하단에 출력된다.

➡️클릭하면 다운로드 루트를 정할 수 있는데 다운로드 테스트 파일로 바탕화면에 저장해보자

➡️다운로드 성공!! 잘 다운로드 된 것을 확인할 수 있다.

'Web > Web 개발' 카테고리의 다른 글