반응형

웹에디터를 이용해서 이미지를 등록하게 해 주면 좋긴한데 한가지 걱정거리가 있다. 사용자가 악의적으로 이미지를 등록하고 지우기를 반복해서 서버에 무한으로 이미지를 등록하는 경우이다. 이런 경우 이미지 첨부 갯수를 제한할수도 있겠지만 올렸다가 지우고를 반복하면 제대로 체크하기 어렵다. 그래서 이미지를 올릴때마다 디비에 저장하고 게시물을 저장할때 디비에 저장된 값과 실제 웹에디터에 남아 있는 이미지 정보를 비교해서 없는 정보는 삭제하는 방법을 해보려고 한다. 우선 앞서 말한 삽입한 이미지 정보를 담을 테이블을 하나 만들어보자.

 

CREATE TABLE `file_table_summer` (
  `fid` int(11) NOT NULL AUTO_INCREMENT,
  `bid` int(11) DEFAULT NULL,
  `userid` varchar(100) DEFAULT NULL,
  `filename` varchar(100) DEFAULT NULL,
  `regdate` datetime DEFAULT current_timestamp(),
  `status` tinyint(4) DEFAULT 1,
  `memoid` int(11) DEFAULT NULL,
  `type` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`fid`),
  KEY `idx_file_table_userid` (`userid`),
  KEY `idx_file_table_type` (`type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

 

그리고 글쓰기 페이지부터 수정하도록 하자.

 

/write.php

<?php
include $_SERVER["DOCUMENT_ROOT"]."/inc/header.php";

if(!$_SESSION['UID']){
    echo "<script>alert('회원 전용 게시판입니다.');history.back();</script>";
    exit;
}

$bid=$_GET["bid"];//get으로 넘겼으니 get으로 받는다.
$parent_id=$_GET["parent_id"];

if($bid){//bid가 있다는건 수정이라는 의미다.

    $result = $mysqli->query("select * from board where bid=".$bid) or die("query error => ".$mysqli->error);
    $rs = $result->fetch_object();

    if($rs->userid!=$_SESSION['UID']){
        echo "<script>alert('본인 글이 아니면 수정할 수 없습니다.');history.back();</script>";
        exit;
    }

    $fquery="select * from file_table where status=1 and bid=".$rs->bid." order by fid asc";
    $file_result = $mysqli->query($fquery) or die("query error => ".$mysqli->error);
    while($frs = $file_result->fetch_object()){
        $fileArray[]=$frs;
    }

}

if($parent_id){//parent_id가 있다는건 답글이라는 의미다.

    $result = $mysqli->query("select * from board where bid=".$parent_id) or die("query error => ".$mysqli->error);
    $rs = $result->fetch_object();
    $rs->subject = "[RE]".$rs->subject;
}

    //기존에 삽입한 이미지가 있다면 정보를 불러온다.   
if($bid){
        $results = $mysqli->query("select GROUP_CONCAT(fid) as fids from file_table_summer where bid=".$bid) or die("query error => ".$mysqli->error);
        $fs = $results->fetch_object();
    }
?>
    <!-- summernote 추가분 -->
<!-- summernote 추가분 -->
        <form method="post" action="write_ok.php" onsubmit="return sendform();" enctype="multipart/form-data">
            <input type="hidden" name="bid" value="<?php echo $bid;?>">
            <input type="hidden" name="parent_id" value="<?php echo $parent_id;?>">
            <input type="hidden" name="file_table_id" id="file_table_id" value="">
            <input type="hidden" name="summer_fid" id="summer_fid" value="<?php echo $fs->fids;?>">
            <div class="mb-3">
            <label for="exampleFormControlInput1" class="form-label">제목</label>
                <input type="text" name="subject" class="form-control" id="exampleFormControlInput1" placeholder="제목을 입력하세요." value="<?php echo $rs->subject;?>">
            </div>
            <div class="mb-3">
            <label for="exampleFormControlTextarea1" class="form-label">내용</label>
            <div id="summernote"><?php echo $rs->content;?></div><!-- summernote 추가분 -->
            <textarea id="content" name="content" style="display: none;"></textarea>
            </div>
            <div class="mb-3">
                <input type="file" name="upfile[]" id="upfile" multiple class="form-control form-control-lg" aria-label="Large file input example">
            </div>
            <!-- 첨부된 이미지 표시 -->
            <div class="row row-cols-1 row-cols-md-6 g-4" id="imageArea">
                <?php
                    foreach($fileArray as $fa){
                ?>
                <div class="col" id="f_<?php echo $fa->fid;?>">
                    <div class="card h-100">
                        <img src="/data/<?php echo $fa->filename?>" class="card-img-top" >
                    <div class="card-body">
                        <button type="button" class="btn btn-warning" onclick="file_del(<?php echo $fa->fid;?>)">삭제</button>
                    </div>
                    </div>
                </div>
                <?php }?>
               
            </div>
            <!-- 첨부된 이미지 -->
            <br />
            <button type="submit" class="btn btn-primary">등록</button>
        </form>

<script>
    //summernote 추가분
    $(document).ready(function() {
        var $summernote = $('#summernote').summernote({
            codeviewFilter: false,
            codeviewIframeFilter: true,
            lang: 'ko-KR',
            height: 500,
            callbacks: {
                onImageUpload: function (files) {
                    if(files.length>5){
                        alert('5개까지만 등록할 수 있습니다.');
                        return;
                    }
                    for(var i=0; i < files.length; i++) {
                        sendFile($summernote, files[i]);
                    }
                   
                }
            }
        });
    });

    function sendFile($summernote, file) {
        var formData = new FormData();
        formData.append("file", file);
        $.ajax({
            url: 'summerimagesave.php',
            data: formData,
            cache: false,
            contentType: false,
            processData: false,
            dataType : 'json' ,
            type: 'POST',
            success: function (data) {
                if(data.result=="fail"){
                    alert(data.msg);
                    return;
                }else{
                    filename="/data/"+data.filename;
                    $('#summernote').summernote('insertImage', filename, function ($image) {
                        $image.css('width', '90%');
                        $image.css('padding', '10px');
                    });
                    var fidval=data.fid+","+$("#summer_fid").val()//본문에 삽입한 이미지가 저장된 테이블의 정보를 입력한다.
                    $("#summer_fid").val(fidval);
                }
            }
        });

    }

    function sendform(){
        var contents=$('#summernote').summernote('code');

        if ($('#summernote').summernote('isEmpty')) {
          alert('내용을 입력하세요.');
          return false;
        }

        $("#content").html(contents);

        return true;
    }

    $("#upfile").change(function(){

        var files = $('#upfile').prop('files');
        for(var i=0; i < files.length; i++) {
            attachFile(files[i]);
        }

        $('#upfile').val('');

    });  

    function attachFile(file) {
        var formData = new FormData();
        formData.append("savefile", file);
        $.ajax({
            url: 'save_image.php',
            data: formData,
            cache: false,
            contentType: false,
            processData: false,
            dataType : 'json' ,
            type: 'POST',
            success: function (return_data) {
                if(return_data.result=="member"){
                    alert('로그인 하십시오.');
                    return;
                }else if(return_data.result=="size"){
                    alert('10메가 이하만 첨부할 수 있습니다.');
                    return;
                }else if(return_data.result=="image"){
                    alert('이미지 파일만 첨부할 수 있습니다.');
                    return;
                }else if(return_data.result=="error"){
                    alert('첨부하지 못했습니다. 관리자에게 문의하십시오.');
                    return;
                }else{
                    fid = $("#file_table_id").val() + return_data.fid + ",";
                    $("#file_table_id").val(fid);
                    var html = "<div class='col' id='f_"+return_data.fid+"'><div class='card h-100'><img src='/data/"+return_data.savename+"' class='card-img-top'><div class='card-body'><button type='button' class='btn btn-warning' onclick='file_del("+return_data.fid+")'>삭제</button></div></div></div>";
                    $("#imageArea").append(html);
                }
            }
        });

    }



    function file_del(fid){

        if(!confirm('삭제하시겠습니까?')){
        return false;
        }
           
        var data = {
            fid : fid
        };
            $.ajax({
                async : false ,
                type : 'post' ,
                url : 'file_delete.php' ,
                data  : data ,
                dataType : 'json' ,
                error : function() {} ,
                success : function(return_data) {
                    if(return_data.result=="member"){
                        alert('로그인 하십시오.');
                        return;
                    }else if(return_data.result=="my"){
                        alert('본인이 작성한 글만 삭제할 수 있습니다.');
                        return;
                    }else if(return_data.result=="no"){
                        alert('삭제하지 못했습니다. 관리자에게 문의하십시오.');
                        return;
                    }else{
                        $("#f_"+fid).hide();
                    }
                }
        });

    }

</script>

<?php
include $_SERVER["DOCUMENT_ROOT"]."/inc/footer.php";
?>

앞서 작업한 파일과 많이 달라졌다. 우선 이미지를 삽입할때 사용하는 ajax에서 리턴값을 받을때 html을 사용하던것을 json으로 바꿨다. 이제 ajax부분을 살펴보자.

 

/summerimagesave.php

<?php session_start();
include $_SERVER['DOCUMENT_ROOT']."/inc/dbcon.php";
ini_set( 'display_errors', '0' );

        if($_FILES['file']['size']>10240000){//10메가
            $retun_data = array("result"=>"fail", "msg"=>"이미지의 용량은 10메가까지만 등록할 수 있습니다.");
            echo json_encode($retun_data);
            exit;
        }
        $ext = substr(strrchr($_FILES['file']['name'],"."),1);
        $ext = strtolower($ext);
        if ($ext != "jpg" and $ext != "png" and $ext != "jpeg" and $ext != "gif")
        {
            $retun_data = array("result"=>"fail", "msg"=>"이미지파일만 등록할 수 있습니다.");
            echo json_encode($retun_data);
            exit;
        }

        $name = "summer_".date("YmdHis").substr(rand(),0,4);
        $filename = $name.'.'.$ext;
        $destination = $_SERVER['DOCUMENT_ROOT'].'/data/'.$filename;
        $location =  $_FILES["file"]["tmp_name"];
        if(move_uploaded_file($location,$destination)){//이미지를 등록하면 테이블에 저장한다.
            $sql="INSERT INTO testdb.file_table_summer
            (userid, filename)
            VALUES('".$_SESSION['UID']."', '".$filename."')";
            $result = $mysqli->query($sql) or die($mysqli->error);
            $fid = $mysqli -> insert_id;
        }else{
            $retun_data = array("result"=>"fail", "msg"=>"등록에 실패했습니다. 관리자에게 문의하십시오.");
            echo json_encode($retun_data);
            exit;
        }

        $retun_data = array("result"=>"ok",
                            "filename"=>$filename,
                            "fid"=>$fid
                        );
        echo json_encode($retun_data);


?>

json으로 받기로 했으니 json으로 넘겨준다. 삽입한 이미지들을 저장하고 디비에 값을 입력해준다.그리고 그 테이블 인덱스값을 넘겨준다.

 

이렇게 하면 글 작성 페이지인 write.php에 저장된 이미지의 정보가 담기게 된다. 그러면 사용자가 웹에디터에서 삽입했다가 삭제한 파일의 정보도 쉽게 알 수 있다. 이제 이 게시물을 저장할때 어떻게 처리하는지 확인해보자.

 

/write_ok.php

 

<?php session_start();
include $_SERVER["DOCUMENT_ROOT"]."/inc/dbcon.php";
ini_set( 'display_errors', '0' );
if(!$_SESSION['UID']){
    echo "<script>alert('회원 전용 게시판입니다.');location.href='/index.php';</script>";
    exit;
}

// echo "<pre>";
// print_r($_FILES);
// exit;


$subject=$_POST["subject"];
$content=$_POST["content"];
$bid=$_POST["bid"];//bid값이 있으면 수정이고 아니면 등록이다.
$parent_id=$_POST["parent_id"];//parent_id가 있으면 답글이다.
$userid=$_SESSION['UID'];//userid는 세션값으로 넣어준다.
$status=1;//status는 1이면 true, 0이면 false이다.
$file_table_id=$_POST["file_table_id"];
$summer_fid=rtrim($_POST["summer_fid"],',');

if($bid){//bid값이 있으면 수정이고 아니면 등록이다.
    $result = $mysqli->query("select * from board where bid=".$bid) or die("query error => ".$mysqli->error);
    $rs = $result->fetch_object();

    if($rs->userid!=$_SESSION['UID']){
        echo "<script>alert('본인 글이 아니면 수정할 수 없습니다.');location.href='/';</script>";
        exit;
    }

    $sql="update board set subject='".$subject."', content='".$content."', modifydate=now() where bid=".$bid;

}else{

    if($parent_id){//답글인 경우 쿼리를 수정해서 parent_id를 넣어준다.
        $sql="insert into board (userid,subject,content,parent_id) values ('".$userid."','".$subject."','".$content."','".$parent_id."')";
    }else{
        $sql="insert into board (userid,subject,content) values ('".$userid."','".$subject."','".$content."')";
    }
   
}
$result=$mysqli->query($sql) or die($mysqli->error);
if(!$bid)$bid = $mysqli -> insert_id;

if($_FILES["upfile"]["name"][0]){//첨부한 파일이 있으면

    for($k=0;$k<count($_FILES["upfile"]["name"]);$k++){

        if($_FILES['upfile']['size'][$k]>10240000){//10메가
            echo "<script>alert('10메가 이하만 첨부할 수 있습니다.');history.back();</script>";
            exit;
        }

        if($_FILES['upfile']['type'][$k]!='image/jpeg' and $_FILES['upfile']['type'][$k]!='image/gif' and $_FILES['upfile']['type'][$k]!='image/png'){//이미지가 아니면, 다른 type은 and로 추가
            echo "<script>alert('이미지만 첨부할 수 있습니다.');history.back();</script>";
            exit;
        }

        $save_dir = $_SERVER['DOCUMENT_ROOT']."/data/";//파일을 업로드할 디렉토리
        $filename = $_FILES["upfile"]["name"][$k];
        $ext = pathinfo($filename,PATHINFO_EXTENSION);//확장자 구하기
        $newfilename = date("YmdHis").substr(rand(),0,6);
        $upfile = $newfilename.".".$ext;//새로운 파일이름과 확장자를 합친다
       
        if(move_uploaded_file($_FILES["upfile"]["tmp_name"][$k], $save_dir.$upfile)){//파일 등록에 성공하면 디비에 등록해준다.
            $sql="INSERT INTO testdb.file_table
            (bid, userid, filename)
            VALUES(".$bid.", '".$_SESSION['UID']."', '".$upfile."')";
            $result=$mysqli->query($sql) or die($mysqli->error);
        }

    }

}

if($file_table_id){
    $fid=explode(",",$file_table_id);
    foreach($fid as $f){
        if($f){
            $uq="update file_table set bid=".$bid." where fid=".$f;
            $ur=$mysqli->query($uq) or die($mysqli->error);
        }
    }
}

if($summer_fid){
    $results = $mysqli->query("select * from file_table_summer where fid in (".$summer_fid.")") or die("query error => ".$mysqli->error);
    while($fs = $results->fetch_object()){
        if(strpos($content,$fs->filename)){//등록한 글에 삽입한 이미지가 남아 있으면 테이블 정보 업데이트
            $uq="update file_table_summer set bid=".$bid." where fid=".$fs->fid;
            $ur=$mysqli->query($uq) or die($mysqli->error);
        }else{//그렇지 않으면 파일을 지우고 테이블의 정보도 지운다.
            $delete_file=$_SERVER["DOCUMENT_ROOT"]."/data/".$fs->filename;
            unlink($delete_file);
            $dq="delete from file_table_summer where fid=".$fs->fid;
            $dr=$mysqli->query($dq) or die($mysqli->error);
        }
    }
}


if($result){
    echo "<script>location.href='/index.php';</script>";
    exit;
}else{
    echo "<script>alert('글등록에 실패했습니다.');history.back();</script>";
    exit;
}


?>

 

맨마지막 부분에 추가한 내용을 확인해보면 된다. 테이블에 저장된 이미지 정보와 저장시 content 내용을 비교해서 해당 이미지가 여전히 남아 있는지 확인해서 없으면 삭제해버리는 프로세스다. 다른 방법도 있을 수 있겠다. 꼭 이것이 정답은 아닐테니. 

 

테이블에서 값이 사라지고 첨부한 이미지가 없어졌는지 확인해보자.

 

그런데 만약 사용자가 정말 악랄한 놈이라서 새로고침을 해버린다던지 아니면 글을 저장하지 않고 그냥 브라우저를 닫아버린다면 이것도 소용없을 것이다. 그럴때는 file_table_summer 이 테이블에 bid가 없는 정보를 가져와서 지워주는 파일을 만들어서 cron으로 처리해주어야 한다.

 

또는 file_table_summer 테이블에 있는 파일 정보가 게시물에 들어 있는지 모두 확인하면서 없는 정보라면 삭제해주는 파일을 만들어서 cron으로 처리해주는 것도 좋은 방법일 것이다.(이런건 한달에 한번이나 1년에 한번정도만)

 

어느정도가 적당할지는 개발자가 판단해서 처리해주면 된다. 심심하다면 직접 확인해보고 하나씩 삭제해줘도 된다.

 

게시물 삭제하는건 각자 알아서 해도록 하자.

반응형

+ Recent posts