게시판 - 글보기 첨부파일 다운로드

이태현·2025년 9월 8일

Web 개발

목록 보기
42/53
post-thumbnail

다운로드

<a href="파일명">다운로드</a>

처음에는 이렇게 하면 그냥 다운이 되는 줄 알았습니다. 아래에는 제 생각에 대한 결과 값입니다. 파일이 다운로드 되긴 합니다만 이미지 같은 경우에는 제대로 다운로드 되지도 않고 파일 위치가 URL에 그대로 노출되고 엑셀파일 역시 파일명은 member.xls인데 저장되는 파일명은 또 파일 소스가 그대로 노출되어 담겨서 저장되었습니다.



echo "<a href='./data/board/" . $file_source . "'>" . $file_name . "</a><br>";
echo "<a href=\"data/board/$file_source\">$file_name</a>";

두 코드 중에 편하신 거 사용하시면 됩니다.

다운로드 - 문제점

  1. 파일 위치 노출
    특정한 사람만 다운로드하게 하려면 이렇게 하면 안 되고 파일 위치를 감춰야 합니다.
  2. 파일의 다운로드 횟수를 알 수 없다.
    중간의 무슨 게이트 역할을 할 수 있는 것들을 두어서 카운팅을 할 방법이 없습니다. 그래서 이러한 문제점을 위해서 프로그램을 수정을 하도록 하겠습니다.

문제 해결

<?php

$idx = isset($_GET['idx']) && $_GET['idx'] != '' && is_numeric($_GET['idx']) ? $_GET['idx'] : '';
$th = isset($_GET['th']) && $_GET['th'] != '' && is_numeric($_GET['th']) ? $_GET['th'] : '';

if ($idx == '') {
  die('
  <script>
  alert("게시물 번호가 누락되었습니다.")
  </script>');
}
if ($th == '') {
  die('
  <script>
  alert("파일의 순서가 누락되었습니다.")
  </script>');
}

include '../inc/dbconfig.php';
include '../inc/board.php'; // 게시판 클래스

$board = new Board($db);

$fileinfo = $board->getAttachFile($idx, $th);
list($file_source, $file_name) = explode('|', $fileinfo);

if ($file_source == '' || $file_name == '') {
  die('
  <script>
  alert("파일 정보가 누락되었습니다.")
  </script>');
}

$down = BOARD_DIR . '/' . $file_source;

if (!file_exists($down)) {
  die('
  <script>
  alert("존재하지 않는 파일입니다.")
  </script>');
}
$filesize = filesize($down);

header("Content-Type:application/octet-stream");
header("Content-Disposition:attachment;filename=$file_name"); // 다운로드 받을 파일이름 지정
header("Content-Transfer-Encoding:binary");
header("Content-Length:" . $filesize);
header("Cache-Control:cache,must-revalidate");
header("Pragma:no-cache");

$fp = fopen($down, 'r');
while (!feof($fp)) {
  $buf = fread($fp, 8096);
  print($buf);
  flush();
}
fclose($fp);
  • fopen () : 파일 열기
    • fopen('파일 절대 경로', '사용 방식') r=읽기, w=쓰기
  • feof () : 파일 끝까지 읽기
    ex) !feof => 파일 포인터가 파일의 끝에 도달하면, true를 반환 False 면 계속 읽고 있는 중
    • fgetc() : 한 글자 읽기
    • fgets() : 한 줄 읽기
  • fread () : 파일 읽기 8096바이트씩
  • print($buf) : 읽은 내용 출력
  • flush () : 파일 비우기
  • fclose () : 파일 닫기, 메모리 해제

서버에 있는 파일을 다운로드하기 위해서 header() 함수를 사용해야 합니다.
출 처 : https://m.blog.naver.com/imm7745/221782552329

파일 소스나 파일명을 가져오기 위해서 DB를 연결하거나 Get방식으로 변경하겠습니다.

파일 순서

        $th = 0;
        foreach ($filelist as $file) {
          list($file_source, $file_name) = explode('|', $file);
          echo "<a href=\"./pg/boarddownload.php?idx=$idx&th=$th\">$file_name</a><br>";
          // echo "<a href='./data/board/" . $file_source . "'>" . $file_name . "</a><br>";
          $th++;
        }

첨부파일을 다운로드하기 위해서 idx 값이 필요합니다. 그런데 파일이 2개 이상이면 어떤 파일이 먼저인지 알 수가 없어서 순서를 정해줘야 합니다. 그러기 위해선 링크에 쿼리 값을 추가를 해줘야 합니다. 그러면 th 값 첫 번째 파일은 0번째 두 번째 파일은 1번째가 됩니다.

DB

  // 첨부파일 구하기
  public function getAttachFile($idx, $th)
  {
    $sql = "SELECT files FROM board WHERE idx=:idx";
    $stmt = $this->conn->prepare($sql);
    $params = [":idx" => $idx];
    $stmt->setFetchMode(PDO::FETCH_ASSOC);
    $stmt->execute($params);
    $row = $stmt->fetch();

    $filelist = explode('?', $row['files']);
    return $filelist[$th];
  }

explode() 함수를 사용하여 '?'를 기준으로 각 배열에 첫 번째 파일과 두 번째 파일이 잘 들어간 걸 볼 수 있습니다. 이제 나눠진 두 개의 파일을 가지고 해당 첨부파일이 어떤 거지 알 수 있게 되었습니다.

$fileinfo = $board->getAttachFile($idx, $th);

위에 코드를 실행하면 idx값과 th값으로 인해 아래와 같이 해당 파일 하나만 출력됩니다.

list($file_source, $file_name) = explode('|', $fileinfo);

첫 번째 파일을 가지고 '|'를 기준으로 다시 한번 더 나눠서 list($file_source, $file_name) 함수에 담긴 $file_source 값과 $file_name에 각각 파일 소스와 파일명을 담아주면 끝입니다.

결과


이렇게 하면 더 이상 파일 소스가 노출되지 않고 파일 원본 이름으로 잘 나오는 걸 볼 수 있습니다.

마무리

다음 시간에는 다운로드 횟수 기록을 해보겠습니다.

감사합니다.

profile
이해하고 분석하고 지배한다

0개의 댓글