기타/WWW

HTML, Javascript, PHP로 서버에 파일 올리기.

하늘이푸른오늘 2013. 10. 15. 14:33

Javascript 공부중입니다. 제가 생각하고 있는 간단한 프로그램을 짜려니... 파일을 서버에 올리는 기능이 필요한데... 이리저리 찾아봐도 마땅한 코드가 별로 없어서 많이 고민했었습니다. 


일단 원하는 기능은 간단히 HTML + CSS + Javascript 만으로 구현 가능한 것을 찾았는데, 결국 이것만으로는 불가능하고 서버측에는 PHP 등으로 클라이언트측 Request를 처리할 수 있는 게 필요하다는 걸 알았습니다. 


아래는 고민하던 중 찾아낸 코드를 간단히 정리한 것입니다. 윗쪽은 가장 간단한 버전. 그리고 아래쪽엔 간단 버전을 확장하여 좀더 복잡한 여러가지 기능이 들어간 코드입니다. 도움이 되면 좋겠네요. 참, 제가 참고한 자료는 여기입니다. 


HTML Form


제일 먼저 클라이언트쪽 HTML에 Form 요소 넣는다. <input type='file'> 을 사용하면 [파일 선택] 버튼이 생기고 이를 이용해 원하는 파일을 선택할 수 있다. 단, 이때, enctype을 반드시 'multipart/form-data'로 주어야 한다.


<form method='post' enctype='multipart/form-data' action='upload.php'>

    File: <input type='file' name='file_upload'>

    <input type='submit'>

</form>


PHP


Form 을 php로 보내면, 각각의 필드가 $_POST 에 들어간다. 하지만, 파일은 배열 $_FILES 로 들어간다. 인덱스로 input 의 name 이 사용된다. 따라서 $_FILES[file_upload] 에서 올려진 파일의 정보를 확인할 수 있다. 다음은 e4f.png 를 올렸을 때의 대략적인 구조.


Array

(

    [file_upload] => Array

    (

        [name] => e4f.png

        [type] => image/png

        [tmp_name] => /Applications/MAMP/tmp/php/phpodzfRk

        [error] => 0

        [size] => 328119

    )

)


- error가 1 이상이면 오류가 발생한 것임. 반드시 체크


<?php


// Check for errors 에러가 1 이상이면 오류가 발생한 것.

if($_FILES['file_upload']['error'] > 0){

    die('An error ocurred when uploading.');

}


// 이미지 파일 크기 체크. 실제 image가 아니면 0으로 나오게 됨.

if(!getimagesize($_FILES['file_upload']['tmp_name'])){

    die('Please ensure you are uploading an image.');

}


// Check filetype. 파일 타입 체크

if($_FILES['file_upload']['type'] != 'image/png'){

    die('Unsupported filetype uploaded.');

}


// Check filesize. 파일 크기 체크

if($_FILES['file_upload']['size'] > 500000){

    die('File uploaded exceeds maximum upload size.');

}


// Check if the file exists. 파일이 이미 존재하는지 체크

// unlink("upload/$fileName"); 를 사용하여 삭제 가능???

if(file_exists('upload/' . $_FILES['file_upload']['name'])){

    die('File with that name already exists.');

}


// Upload file. move_uploaded_file() 을 사용하여 최종적으로 업로드. 

if(!move_uploaded_file($_FILES['file_upload']['tmp_name'], 'upload/' . $_FILES['file_upload']['name'])){

    die('Error uploading file - check destination is writeable.');

}


die('File uploaded successfully.');

?>


=======

프로그레스 바를 포함한 HTML


위의 방식을 이용하면 파일이 업로드된 후, 화면이 바뀌게 되고, "이전"을 눌러야 원래 상태로 돌아온다. 이것은 사용자 경험을 나쁘게 만든다. 


<div class='container'> 부분을 살펴보면, 2개의 버튼 (File 버튼과 Submit 버튼)과, 업로드 상태를 보여줄 수 있는 div 가 있다. <style> 부분에는 이들에 대한 CSS 가 있다.


<!doctype html>

<html>

<head>

   <meta charset="utf-8">

<title>JS File Upload with Progress</title>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"> </script>

<script>

$(document).ready(function(){

// 버튼 2개와 progress div 부분을 가져옴.

var _submit = document.getElementById('_submit'), 

_file = document.getElementById('_file'), 

_progress = document.getElementById('_progress');


function upload(){

// 파일이 선택되었는지 확인. 선택된 파일 없으면 종료

// 현재 선택된 파일은 1개임. 여러개를 선택하려면, multiple 필요.

if(_file.files.length === 0){

return;

}


// 동적으로 Form 을 생성함. 이것을 보내게 됨.

var data = new FormData();

// 'SelectedFile'은 Form의 attribute name, 뒷부분은 value 임

data.append('SelectedFile', _file.files[0]);


// XMLHttpRequest 객체를 생성

var request = new XMLHttpRequest();

// XMLHttpRequest 객체의 상태가 변경되면 실행되는 함수

request.onreadystatechange = function(){

if(request.readyState == 4){ //완료되었을 때.

try {

// 서버측에서 보낸 JSON을 해석하여 처리함.

var resp = JSON.parse(request.response);

} catch (e){ //에러가 발생되었다면

var resp = {

status: 'error',

data: 'Unknown error occurred: [' + request.responseText + ']'

};

}

// 여기는 정상적으로 업로드가 되었을 때임. resp.data에는 실제 올려진 파일명이 들어 있음. 파일을 올린 뒤 처리하는 방법을 여기에 기술하면 됨.

console.log(resp.status + ': ' + resp.data);

}

};


// 업로드 상태를 체크하기 위한 event 등록.

request.upload.addEventListener('progress', function(e){

_progress.style.width = Math.ceil(e.loaded/e.total) * 100 + '%';

}, false);


// 이제 XMLHttpRequest 객체를 서버로 보냄.

request.open('POST', 'upload_png.php');

request.send(data);

}

// submit 버튼을 'click' 했을 때, upload()가 실행되도록 event 를 등록함.

_submit.addEventListener('click', upload);


});

</script>


<style>

.container {

width: 500px;

margin: 0 auto;

}

.progress_outer {

border: 1px solid #000;

}

.progress {

width: 20%;

background: #DEDEDE;

height: 20px;  

}

</style>


</head>


<body>

<div class='container'>

<p>

Select File: <input type='file' id='_file'> <input type='button' id='_submit' value='Upload!'>

</p>

<div class='progress_outer'>

<div id='_progress' class='progress'></div>

</div>

</div>

</body>

</html>



PHP

이제 서버측에서 처리해야 할 것.


<?php

// 처리 결과를 JSON으로 인코딩하여 보냄.

function outputJSON($msg, $status = 'error'){

    header('Content-Type: application/json');

    die(json_encode(array(

        'data' => $msg,

        'status' => $status

    )));

}


// 에러체크. 

if($_FILES['SelectedFile']['error'] > 0){

    outputJSON('An error ocurred when uploading.');

}


// 정말 이미지를 선택했는지 확인하는 절차. 실제 image가 아니면 0으로 나오게 됨.

if(!getimagesize($_FILES['SelectedFile']['tmp_name'])){

    outputJSON('Please ensure you are uploading an image.');

}


// Check filetype

if($_FILES['SelectedFile']['type'] != 'image/png'){

    outputJSON('Unsupported filetype uploaded.');

}


// Check filesize

if($_FILES['SelectedFile']['size'] > 5000000){

    outputJSON('File uploaded exceeds maximum upload size.');

}


// Check if the file exists

if(file_exists('upload/' . $_FILES['SelectedFile']['name'])){

    outputJSON('File with that name already exists.');

}


// Upload file

if(!move_uploaded_file($_FILES['SelectedFile']['tmp_name'], 'upload/' . $_FILES['SelectedFile']['name'])){

    outputJSON('Error uploading file - check destination is writeable.');

}


// Success!

outputJSON('File uploaded successfully to "' . 'upload/' . $_FILES['SelectedFile']['name'] . '".', 'success');

?>


======

흠... 이걸 어떻게 정리한다... 이제부터 고민해야겠네요~~


민, 푸른하늘