japanese site
온라인강좌책메일다운로드동아리쇼핑기술지원기업정보갤러리동영상 블로그
『신개념 4단계 리눅스기술지원 시스템 공개』

※ 『무료 리눅스 배포판 배송서비스』 ※

 
소. 자료실 기능 및 잘잘한 기능 붙이기 -3 이외 필요하면 추가하죠
조회 : 2,793  


[강좌] php+mysql 게시판 만들기 #소-2

안녕하세요. 디망쉬입니다.

이걸로 강좌는 끝나는군요. 쩝. 마지막 강좌입니다.
유종의 미를 거둘 수 있게 나름대로 열심히 타이핑 하겠습니다. ^^; (하지만 감기로 인해 어질 어질)


소. 자료실 기능 및 잘잘한 기능 붙이기

b. 자료실 기능


사실 매우 꺼려지는 부분입니다. 게시판에 파일 업로드 기능을 넣음으로서 여러 분이 사용하실 서버의 보안은 한단계 낮아지게 되거든요.
여차하면 순식간에 한바탕 난리가 날 수도 있습니다.
이 기능을 넣고 말고는 여러 분의 선택이지만, 넣으신다면 여러모로 부족한 기능을 훨씬 강화시키고(기능=보안 라는 의미에요) 다듬어서 안정적인 서비스가 되도록 하셔요.

1. 자료실 기능에 필요한 정보들 별 거 없습니다. 보안상 저는 자료실 기능에서 파일 이름과 경로, 파일 사이즈 등을 DB 에 직접 저장시키기로 했지요. 이 정보가 필요하겠죠.

이것 뿐입니다. 이것의 attribute 는 어떻게 될까요?

filepath varchar(100),
filename varchar(100),
filesize int(4),

바로 이겁니다. filepath 에 업로드한 파일이 존재할 디렉토리 이름을, filename 에 업로드한 파일의 이름을,
filesize 에는 업로드한 파일의 용량을 저장합니다.

그렇다면 일단 글 쓸 때 파일 업로드 입력 폼을 만들어볼까요?

?
/*--------------------------
filename : fill.php3
--------------------------*/
require "function.php3";

error_board();

echo (" // 전체 화면 출력

<html>

<head>
<title>글 입력</title>
</head>

<body>
<center>
광고 및 욕설 글은 언제나 발견되는 대로 삭제됩니다. ^^<p>

<form method="post" action="run.php3"
enctype="multipart/form-data">
<input type="hidden" name="board"
value="$board">

<input type="hidden" name="mode" value="write">

<table width="550" cellspacing="1"
border="0" cellpadding="2">
<tr>
<td align="left"><font size="2"><b>
|<a href="list.php3?board=$board&page=$nowpage">
게시물 목록</a> |
</b></font></td>
</tr>
</table>

<table width="400" cellspacing="1" bgcolor="#A5A595"
border="0" cellpadding="5">
<!-- 작성자명 -->
<tr>
<td width="30%" bgcolor="#A5A595" align="center">
<font size="2" color="white"><b>작성자명</b></font></td>
<td width="70%" bgcolor="white" align="left">
<font color="white">
<input type="text" name="fil[name]" value="$mwbwd[name]"
size="10" maxlength="10">
</font></td>
</tr>

<!-- Email -->
<tr>
<td width="30%" bgcolor="#A5A595" align="center">
<font size="2" color="white"><b>E-mail</b></font></td>
<td width="70%" bgcolor="white" align="left">
<font color="white">
<input type="text" name="fil[email]" value="$mwbwd[email]"
size="20" maxlength="256">
</font></td>
</tr>

<!-- Homepage -->
<tr>
<td width="30%" bgcolor="#A5A595" align="center">
<font size="2" color="white"><b>Homepage</b></font></td>
<td width="70%" bgcolor="white" align="left">
<font color="white">
<input type="text" name="fil[homepage]" value="$mwbwd[homepage]"
size="20" maxlength="256">
</font></td>
</tr>

<!-- Password -->
<tr>
<td width="30%" bgcolor="#A5A595" align="center">
<font size="2" color="white"><b>비밀번호</b></font></td>
<td width="70%" bgcolor="white" align="left">
<font color="white">
<input type="password" name="fil[pw]"
size="10" maxlength="20">
</font></td>
</tr>

<!-- Subject -->
<tr>
<td width="30%" bgcolor="#A5A595" align="center">
<font size="2" color="white"><b>글 제목</b></font></td>
<td width="70%" bgcolor="white" align="left">
<font color="white">
<input type="text" name="fil[title]"
size="30" maxlength="256">
</font></td>
</tr>

<!-- 파일 업로드 -->
<tr>
<td width="30%" bgcolor="#A5A595" align="center">
<font size="2" color="white"><b>파 일</b></font></td>
<td width="70%" bgcolor="white" align="left">
<font color="white">
<input type="file" name="userfile"
size="20" maxlength="256">
</font></td>
</tr>

<!-- Text -->
<tr>
<td width="30%" bgcolor="#C5C5B5" align="center">
<font size="2" color="white"><b>본 문</b></font></td>
<td width="70%" bgcolor="white" align="left">
<font color="white">
<textarea name="fil[text]" wrap="hard" rows="10" cols="36"></textarea>
</font></td>
</tr>
</table>
<input type="submit" value="저장하자">
<input type="reset" value="재작성">
</form>

<table width="550" cellspacing="1"
border="0" cellpadding="2">
<tr>
<td align="right"><font size="2"><b>
|<a href="list.php3?board=$board&page=$nowpage">
게시물 목록</a> |
</b></font></td>
</tr>
</table>

</center>

</body>

</html>
"); // 전체 화면 출력 완료
?>

여기에다가

<!-- 파일 업로드 -->
<tr>
<td width="30%" bgcolor="#A5A595" align="center">
<font size="2" color="white"><b>파 일</b></font></td>
<td width="70%" bgcolor="white" align="left">
<font color="white">
<input type="file" name="userfile"
size="20" maxlength="256">
</font></td>
</tr>

이걸 넣었습니다. 잘 보시면 보이실 듯. ^^;

이제 업로드가 되어 서버로 넘어오는 파일의 정보를 DB 에 저장하고 파일 자체는 파일로 저장하면 됩니다.

여기서 바로 재미있는게 나옵니다. 위에서 파일 업로드 관련 정보가 userfile 이라는 이름의 변수로 넘어가게 했죠? (<input> tag 에서용) 이걸 통해 업로드된 파일은 $userfile_name 과 $userfile_size 라는 변수를 통해 새로 태어납니다. $userfile_name 에는 업로드된 파일의 이름이, $userfile_size 에는 업로드된 파일의 용량이. 오오오. 편하죠? 매우 편합니다.

이제 run.php3 에서 글 입력하는 부분을 약간 다듬어보죠.

$result = mysql_query("INSERT INTO $board VALUES('',
$ist[num], $ist[idx], '$fil[title]', '$fil[text]',
0, 0, 0, 0, 0, '', '', 0, '$fil[name]', '$passwd',
'$fil[email]', '$fil[homepage]', $intime)", $connect);

여기에서 세번째 줄에 있는 '', '', 0 이 부분이 바로 파일 업로드 관련입니다. 이것을

$result = mysql_query("INSERT INTO $board VALUES('',
$ist[num], $ist[idx], '$fil[title]', '$fil[text]',
0, 0, 0, 0, 0, '', '$userfile_name', $userfile_size,
'$fil[name]', '$passwd', '$fil[email]',
'$fil[homepage]', $intime)", $connect);

로 고칩니다. 이제 파일이 저장될 경로명을 정한 뒤 넣으면 되겠군요.

그런데 왜 저장될 디렉토리를 지정해줄까요?
여러 사람들이 파일을 업로드하다보면 이미 업로드된 파일과 동일한 이름의 파일이
업로드될 수 있습니다. 이럴 경우 기존의 파일이 사라질 수 있죠. 그래서 각 각의 파일을 제 각 각의 디렉토리에 넣어주는 겁니다. 물론 이러한 디렉토리는 고유한 이름을 지녀야하구요.

저는 설명만 하면 되니(여러분은 설명을 이해하고 응용하면 되구요) 간단하게 경로를 입력된 시각으로 하겠습니다.

$result = mysql_query("INSERT INTO $board VALUES('',
$ist[num], $ist[idx], '$fil[title]', '$fil[text]',
0, 0, 0, 0, 0, '$intime', '$userfile_name', $userfile_size,
'$fil[name]', '$passwd', '$fil[email]',
'$fil[homepage]', $intime)", $connect);

이제 업로드된 파일을 해당 디렉토리를 만든 뒤 그곳에 저장하면 됩니다.

그전에 어느 디렉토리를 파일 저장 폴더로 쓸지 정할 필요가 있겠군요.

간단하게 upload/게시판명 에 하겠습니다.
현재 게시판 만들며 테스트하고 있는 게시판의 이름(mysql 에서 테이블명)이 testboard 니까 이건 파일을 올리면 upload/testboard 에 올라가겠죠?
이때 중요한 것은 testboard 디렉토리의 퍼미션(permission)이 777 이어야 합니다. 만약 리눅스나 유닉스등의 시스템에서 root 권한이 있다면 디렉토리의 access 그룹을 nobody 로 한 뒤 770 으로 하셔도 되구요. 퍼미션이 뭔지 잘 모르신다구요? ^^; 어쩔 수 없죠. 넘어갈 수 밖에. ^^;
(흑. 그래요. 어지러워서 빨리 타이핑 완료하고픈 마음이 커서 그래유 T_T)

mkdir upload
mkdir upload/testboard
chmod 777 upload/testboard

라고 하시면 됩니다. 물론 run.php3 파일이 있는 곳에서요.

자아. 준비가 됐습니다. 이제 run.php3 에서 파일이 올라가게 하죠.

if ($userfile_size > 0) {
mkdir("upload/$board/$intime", 0755);
exec("mv "$userfile" "upload/$board/$intime/$userfile_name"");
chmod("upload/$board/$intime/$userfile_name", 0644);
}

상당히 쉽습니다. 일단 $userfile_size, 즉 파일의 용량이 0 바이트가 아닐 경우 해당 내용을 실행한다는거죠. 0 바이트면? 파일을 올린게 아니거나 깨진 파일이므로 업로드를 하지 않습니다.

mkdir("upload/$board/$intime", 0755);

으흠. 이건 upload/$board 에 글이 작성된 시각의 디렉토리를 만듭니다.
디렉토리의 퍼미션은 755(rwxr-xr-x). 여기서 mkdir 함수는 디렉토리를 만드는 함수죠.

그런 뒤

exec("mv "$userfile" "upload/$board/$intime/$userfile_name"");

업로드된 파일을 방금 만든 디렉토리에 업로드된 파일 이름으로 이동시킵니다(mv).
이때 exec 함수는 해당 시스템에서의 시스템 명령어(?)를 실행시키는 것입니다.

자 이제 업로드된 파일의 퍼미션을 644 로 바꿉니다.
왜냐구요? 해당 파일을 읽기만 가능하고 실행을 하지 못하게 하려구요.
그건 또 왜냐구요? ^^? 보안때문입니다.
만약에, 그러니까 마아아안약에 업로드된 파일이 cgi 확장자이고 서버는 cgi 확장자를 지원하며, 어떠한 방법을 통해 cgi 의 위치를 알아내어 이것을 웹 브라우저를 통해 실행시킬 경우? 헤. 끔찍하군요.
그래서 해당 파일의 권한에서 실행 권한을 뺏어버린 겁니다.

이걸로 업로드는 끝났습니다.
이제 다운로드가 되게 해보죠.
이것을 하기 위해 우리는 dn.php3 를 새로 만들어야 합니다. 어떻게 만들까요?
그걸 생각하기 전에 어떻게 파일을 다운로드 시킬까요? 순서를 짜보죠.

해당 파일의 글 번호를 넘겨줌
--> DB 에서 해당 글 번호를 근거로 파일 이름과 위치를 알아냄
--> 웹 브라우저로 해당 파일을 보내줌

일단 첫 번째 것은

dn.php3?board=testboard&no=10

뭐 이런 식이면 되겠죠. 정확히 하자면

dn.php3?board=$board&no=$no

이겠구요.

두 번째 과정은 우리가 많이 해왔던 select 함수를 통해 할 수 있습니다.

$result = mysql_query("SELECT * FROM $board
WHERE no=$no", $connect);

$fileinfo = mysql_fetch_array($result);

이렇게 하면

$fileinfo[filepath] 에는 파일 경로가,
$fileinfo[filename] 에는 파일 이름이,
$fileinfo[filesize] 에는 파일 용량이

저장됩니다. 우리가 필요한 것은 물론 앞의 두 개입니다. 이것을 이제 웹 브라우저로 보내주면 끝납니다.

그것을 하기 위해서는 파일 access 를 해야 합니다. fopen, fread 가 그것이죠.

일단 fopen 함수는 파일을 읽어오는, 즉 여는 함수입니다. 해당 파일을 읽어와 이걸 fread 로 읽어낸 뒤 출력해주면 웹 브라우저가 이걸 받아내어 클라이언트 하드에 저장하는거죠. 어...어렵나요?
소스부터 보겠습니다.

<?
/*--------------------------
filename : dn.php3
--------------------------*/
$connect = mysql_connect("localhost","아이디","비번");
mysql_select_db("사용DB명",$connect);

$result = mysql_query("SELECT * FROM $board
WHERE no=$no", $connect);

$ff = mysql_fetch_array($result);

$paths =
"upload/$board/$ff[filluptime]/$ff[filename]";

if ($fp = fopen($paths, "r")) {
Header("Content-type: application/octet-stream");
Header("Content-Disposition: attachment; filename=".$ff[filename]);
Header("Content-Description: PHP Generated Data");

while ($fd=fread( $fp,filesize($paths))) {
print($fd);
}
}
fclose($fp);
?>

대부분의 것은 이미 우리가 구현해본 것들입니다. 안해본 것은

if ($fp = fopen($paths, "r")) {
Header("Content-type: file/unknown");
Header("Content-Disposition: attachment; filename=".$ff[filename]);
Header("Content-Description: PHP Generated Data");

while ($fd=fread( $fp,filesize($paths]))) {
print($fd);
}
}

이거군요. 일단 파일을 열어야겠죠.
fopen 으로요. 이때 파일을 읽기 모드로 열기 위해 "r" 이라는 걸 넣은겁니다.
"w" 로 하면 쓰기 모드로 열기 때문에 이 파일을 지우거나 할 수 있죠.
어쨌건 단지 파일을 읽어오면 되기에 r 로 합니다.

이렇게 열은 파일 정보를 $fp 에 저장합니다.
왜 fp 냐구요? C 언어로 파일 다룰 때 File Pointer 의 약자로 fp 를 많이 쓰는데 그게 습관이 되서리. ^^;

자아. 파일일 열었으니 파일을 다운로드 시킬 정보를 클라이언트, 즉 웹 브라우저에게 보내줘야 합니다.

헤더 정보를 뭐가 한다고 했죠? 예이 Header 함수죠.

Header("Content-type: file/unknown");
Header("Content-Disposition: attachment; filename=".$ff[filename]);
Header("Content-Description: PHP Generated Data");

이겁니다. 여기서 중요한 건

Header("Content-type: file/unknown");

이거죠. 이것의 파일 타입을 gif 라던가 뭐 그런 식으로 했고, 그 타입을 웹 브라우저가 자체적으로 열 수 있는
타입이라면 하드에 저장하려 하지 않고 일단 웹 브라우저가 처리하려 합니다.
골치 아프죠. 그래서 해당 파일이 어떤 타입인지 나도 몰라! 라고 해주기 위해 file/unknown 이라고 한거랍니다.

그 아래 아래는 뭐 그다지 중요하진 않구요. 정말 중요한 건

while ($fd=fread( $fp,filesize($paths]))) {
print($fd);
}

이놈이죠. fread 함수로 해당 파일의 내용을 읽어와(가져와서) fd 에 저장하고 화면에 뿌려주는 겁니다.
이걸 파일을 다 읽을 때까지 무한 반복시키는거죠.

어째

while ($view=mysql_fetch_array($result)) {
}

이것과 모양이 비슷하죠? ^^;

자. 이렇게 읽어오고 뿌려주고를 완료했으면 파일 열은 것을 닫아줘야 합니다. 반드시요.
그렇지 않으면? 무슨 일이 일어날지. ^^;

fclose($fp);

짠. 닫았습니다. :]

으흠. 이제 파일이 있을 경우 파일 명을 출력하고 그걸 클릭하면 다운로드 되게 해보겠습니다.

일단 간단하게 글 본문 읽는 상태(view.php3) 에서 파일 다운로드가 가능하게 해보겠습니다.

if ($view[filesize] > 0) {
$view[contents] .= "<p align="right">
<a href="dn.php3?board=$board&no=$view[no]">
$view[filename]</a>";
}


에게. 간단하죠? 이걸 화면 출력하기 전에 넣어주면 됩니다.

위 루틴은 해당 글의 파일 용량 attribute 를 검색해서 용량이 0 보다 크면 파일이 존재한다고 체크하고, 글 본문 맨 뒤에다가

<p align="right">
<a href="dn.php3?board=$board&no=$view[no]">
$view[filename]</a>

이걸 붙이는 겁니다. 이렇게 하면 글 본문 맨 아래 우측에 업로드된 파일 이름이 출력되어 있고 이걸 클릭하면 다운로드가 되죠. ^^;


이 기능은 약간의 문제가 있습니다. 큰 용량의 파일을 업로드할 경우 실제로 글이 DB 에 저장된 시각과 글 입력이 완료된 시각이 달라지게 됩니다. 그걸 방지하기 위해 파일을 맨 나중에 업로드 되게 해놨죠.
그런데 역시 문제가 생깁니다. 글이 입력되면 게시물 리스트에 해당 글을 읽을 수 있습니다.
물론 파일도 다운로드 되겠죠. 그런데 만일 파일 용량이 커서 업로드 되고 있는 상태라면 글은 읽어지지만
업로드된 자료는 다운로드가 안될 것입니다.
이거 어떻게 해결하면 좋을까요? ^^ 제 이번 강좌의 마지막 숙제입니다. 잘 풀어보세요. :]


수고들 하셨어요. ^^/ 이제 강좌의 후기인 '년-0' 이 남았군요. ^^; (어감 이상하죠?)

----------------------------
함께하면 즐거운 사이트들 (-_-; 광고임 -_-;)
http://game.creple.com/delthia
http://creple.com
http://coco.st
----------------------------


- ?! 디망쉬


[원글링크] : https://www.linux.co.kr/home2/board/subbs/board.php?bo_table=lecture&wr_id=593


이 글을 트위터로 보내기 이 글을 페이스북으로 보내기 이 글을 미투데이로 보내기

 
디망쉬
E-mail : dimanche@coco.st

Homepage : http://dimanche.coco.st

 
   
 


『신개념 4단계 리눅스기술지원 시스템 공개』

CentOS 리눅스 전문서적 국내최초출시

 
회사소개 | 보도기사 | 채용안내 | 광고안내 | 이용약관 | 개인정보취급방침 | 책임한계와 법적고지 | 불편사항 신고하기 | 사이트맵
ㅇ 서울 : 서울특별시 강남구 노현로79길 66, 402호(역삼동,청송빌딩)  ㅇ 부산 : 부산광역시 해운대구 우동 1470 에이스하이테크21 914호
ㅇ 대표 : 1544-8128 ㅇ부산직통 : 051-744-0077 ㅇ서울직통 : 02-856-0055 ㅇ FAX : 02-6280-2663
ㅇ 사업자번호 : 128-86-68239 ㅇ 통신판매업 신고번호 : 2013-부산금정-0035호 ㅇ 개인정보책임자 : 이재석
ㅇ 상호명 : (주) 수퍼유저 ㅇ 대표자 : 박성수
Copyright ⓒ (주)수퍼유저. All rights reserved.

 아 이 디
 비밀번호
회원 가입 | 비밀번호 찾기

블로그~
 
▷ 웹개발언어
PHP
JSP
python
기타
책제목
리눅스서버관리실무바이블v3.0


저자 박성수
페이지수 2,032 pages
펴낸곳(주)수퍼유저

리눅스 서버 구축 및 관리에 필요한 거의 모든 부분과 전반적인 실무 기법들을 다루고 있음
책제목
리눅스보안관리실무(2/E)


저자홍석범
페이지수 820 pages
펴낸곳(주)수퍼유저

리눅스 보안에 관련된 내용을 종합적으로 쓴 책. 저자의 명성 그대로 보안관련의 모든 내용이 그대로 담겨져 있다.