해당 소스에서 form 데이터 내 input type을 "file"로 설정했었던 적이 있었죠?"file" 속성은 말 그대로 파일 업로드를 위해 설정하는 속성인데요, 이를 form 데이터에 담아 서버 측으로 전송하는 원리입니다. 이 때, form 태그의 enctype을 "multipart/form-data"로 지정했었습니다.
"multipart/form-data"로 보내는 이유를 알려면 먼저 HTTP 통신 원리와 Content-type을 알아야 합니다.
간략하게 설명을 드리자면 통신 원리는 매우 간단해요!
스펙에 맞게 클라이언트와 서버가 문자로 구성된 데이터를 송수신하는 것이라고 생각해주시면 됩니다. 파일을 전송한다고 해서 jpg 파일이나 txt 파일 자체가 전송되는게 아니라 파일 역시도 문자의 형태로 전송되는 것이고, 이러한 문자의 형태를 스펙에 맞추어 서버에 송신하는 것 뿐이거든요. 이미지, 텍스트, 동영상 파일 모두 문자열로 이루어져 있는데 확인하고 싶으신 분들은 아무 이미지 파일이나 동영상 파일 등을 메모장 등 텍스트 편집기로 열어보세요!
Content-Type은 Body에 전송되는 메세지(데이터)의 타입을 정의하는 속성으로 HTTP 요청 Header에서 정의합니다. 이 중에서 파일을 송신할 때 사용하는 속성값이 바로 "multipart/form-data" 형식인 것이지요.
<?php
header("Content-Type:text/html; charset=utf-8;");
// 요청 데이터(API 요구 사항에 맞게 파라미터 설정)
$value_1 = "value"; // key_1에 대한 value
$value_2 = "value"; // key_2에 대한 value
// 파일 절대 경로 설정 (예시)
$fileDir = "C:/Bitnami/wampstack-7.2.14-0/apache2/htdocs/img/test.jpg";
// 요청자 검증을 위한 암호화 데이터(수정 금지).
$userId = "ID";
$userPwd = "Password";
$EncryptBase64 = base64_encode($userId.":".$userPwd);
// 요청 데이터를 배열 형식으로 최초 set.
$data = array(
'key_1' => $value_1,
'key_2' => $value_2,
'fileDir' => $fileDir, // 파일경로
'fileContents' => file_get_contents($fileDir) // 파일 내용
);
// 배열 형식의 데이터를 multipart/form-data 형식으로 변환
$postData = setMultipartFormData($data);
// 요청 API Header 정보(Api-Key 값은 전달받은 값 그대로 사용.)
$headers = array(
'Content-Type: multipart/form-data;boundary=^*******^',
'Content-Length: '.strlen($postData),
'Authorization: Basic '.$EncryptBase64
);
// 요청 URL
$postUrl = "https://";
// 응답 파라미터 변수 선언 및 초기화
$response = "";
// API 호출 및 응답 데이터 저장
$response = reqPost($postData, $postUrl, $headers);
// 응답 데이터 print
echo "Response : " . $response;
// api 요청
function apiCall($data, $url, Array $headers){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30); //connection timeout 30
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, true); // API 호출 시 method를 post로 지정
curl_setopt($ch, CURLOPT_POSTFIELDS, $data); // Data
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
// multipart/form-data 형식으로 변환하는 함수
function setMultipartFormData($arrayData){
$fileData = $arrayData['fileContents'];
$boundary = "^*******^";
$data = "";
unset($arrayData['fileContents']);
foreach ($arrayData as $name => $content) {
$data .= '--' . $boundary . '\r\n'
. 'Content-Disposition: form-data; name="' . $name . '\"\r\n\r\n'
. $content . '\r\n';
}
// 파일 데이터 저장
$data .= '--' . $boundary . '\r\n'
. 'Content-Disposition: form-data; name="file"; filename="' . $arrayData['fileDir'] . '"' . '\r\n'
. 'Content-Type:application/octet-stream' . '\r\n\r\n';
$data .= $fileData . '\r\n';
$data .= "--" . $boundary . '--\r\n';
return $data;
}
?>
** 주의사항 : 요청 필드는 제가 임의로 작성해서 Key_1, Key_2 등으로 만든 것이지만 실제로는 API 명세서에서 요구하는 필드를 입력하여야 합니다. 일반적으로 파일 절대 경로나 파일명을 요구하므로 fileDir이라는 필드를 만들었지만 이는 제가 임의로 작성한 코드이므로 API 명세에 따라 수정이 필요합니다.
또한 예시 코드로 작성하다보니 인증 방식 역시 HTTP 기본 인증 방식인 Basic Authorization을 선택하여 만들었습니다. 실제 코드를 작성하실 때 API 명세에 별도 암호화 방식을 요구할 수 있으니 주의하셔야 합니다.
이렇게 예시 코드를 작성해 보았는데요,
위에서 언급했던 "multipart/form-data" 파라미터는 $headers에 담아 HTTP header 정보에 담았습니다!
이중 가장 자세히 보아야 할 부분은 제가 임의로 만든 setMultipartFormData 함수입니다!
배열로 세팅한 $data를 매개변수로 받아 key-value 데이터와 파일 내용을 분리해서 다시 세팅하는 형태로 구성되어 있는데요, 굳이 이러한 과정을 거치는 이유는 multipart/form-data 형식을 준수하기 위함입니다!
우선 첫째로 $boundary 변수는 header에 Content-type=multipart/form-data 지정 시 함께 지정한 boundary와 같은 값을 가져야 합니다. 이유는 boundary가 HTTP 요청 Body 데이터 중 파일 데이터를 구분하기 위한 역할을 가지기 때문입니다.
둘째로 $boundary 양 옆으로 "--" 기호와 "\r\n" 이스케이프 시퀀스를 넣어 놓았는데요,
먼저 "--" 기호는 body의 끝을 알리는 구분자이며 "\r\n" 이스케이프 시퀀스는 Header와 Body, Body 데이터 간의 구분을 위해 넣었다고 생각하시면 됩니다!
...?
이해가 되지 않는 부분들이 생기실 거에요..!
여기선 요청 Body 데이터만 세팅하는데 왜 또 header/body를 구분하느냐 말이죠??
이유는 body의 내용을 보시면 이해되실 거에요!!
body 데이터에서 Content-Disposition을 지정해주었죠? 이는 HTTP 응답 Header의 한 종류로 브라우저에 Content가 웹페이지로 나타낼지, 다운로드될지 정하는 속성이에요. 즉, $headers에 담은 HTTP 요청 Header 정보가 아닌 응답 Header 정보와 구분하기 위해 "\r\n"을 두번 써서 구분하는 것이랍니다 ㅎㅎ
<?php
/*********************************************
* 넘어오는 데이터가 정상인지 검사하기 위한 절차
* 실제 페이지에서는 적용 X
**********************************************/
//$_FILES에 담긴 배열 정보 구하기.
var_dump($_FILES);
// php 내부 소스에서 html 태그 적용 - 선긋기
echo "<hr>";
/*********************************************
* 실제로 구축되는 페이지 내부.
**********************************************/
// 임시로 저장된 정보(tmp_name)
$tempFile = $_FILES['imgFile']['tmp_name'];
// 파일타입 및 확장자 체크
$fileTypeExt = explode("/", $_FILES['imgFile']['type']);
// 파일 타입
$fileType = $fileTypeExt[0];
// 파일 확장자
$fileExt = $fileTypeExt[1];
// 확장자 검사
$extStatus = false;
switch($fileExt){
case 'jpeg':
case 'jpg':
case 'gif':
case 'bmp':
case 'png':
$extStatus = true;
break;
default:
echo "이미지 전용 확장자(jpg, bmp, gif, png)외에는 사용이 불가합니다.";
exit;
break;
}
// 이미지 파일이 맞는지 검사.
if($fileType == 'image'){
// 허용할 확장자를 jpg, bmp, gif, png로 정함, 그 외에는 업로드 불가
if($extStatus){
// 임시 파일 옮길 디렉토리 및 파일명
$resFile = "./img/{$_FILES['imgFile']['name']}";
// 임시 저장된 파일을 우리가 저장할 디렉토리 및 파일명으로 옮김
$imageUpload = move_uploaded_file($tempFile, $resFile);
// 업로드 성공 여부 확인
if($imageUpload == true){
echo "파일이 정상적으로 업로드 되었습니다. <br>";
echo "<img src='{$resFile}' width='100' />";
}else{
echo "파일 업로드에 실패하였습니다.";
}
} // end if - extStatus
// 확장자가 jpg, bmp, gif, png가 아닌 경우 else문 실행
else {
echo "파일 확장자는 jpg, bmp, gif, png 이어야 합니다.";
exit;
}
} // end if - filetype
// 파일 타입이 image가 아닌 경우
else {
echo "이미지 파일이 아닙니다.";
exit;
}
?>
이렇게 코드를 작성해보았습니다.
.
이전 PHP 포스팅에서 $_POST 필드를 이용해 form 태그의 method가 post인 data를 가져온 것을 볼 수 있습니다.
즉, $_POST 필드는 요청 시 method 속성의 값이 post 방식인 데이터를 배열 형태로 가져온다는 것을 알 수 있습니다.
일반적인 데이터라면 $_POST 필드를 이용해 값을 가져왔겠지만, 파일 데이터는 조금 다릅니다.
$_FILES라는 필드를 이용해 파일에 대한 데이터 값을 배열로 가져옵니다.
자세한 내용을 설명드리기 위해 var_dump() 함수를 이용해 $_FILES 필드에 담긴 배열 데이터를 모두 print하였습니다.
(PHP에서 데이터를 체크하기 위해 자주 사용하는 함수이니 꼭 알아두세요!!)
실행된 예시 화면을 보면서 조금 더 상세하게 설명드릴게요ㅎㅎ
.
1) fileUploadRequest.html 실행
2) fileUploadRequest.html 페이지와 동일 경로 내 img 폴더 임의 생성
** 이미지가 업로드되는지 확인하기 위해 빈 폴더로 생성해 놓았습니다!
3) fileUploadRequest.html 페이지에서 "파일 선택" 버튼 클릭 후 첨부할 이미지 선택
4) fileUploadRequest.html 페이지에서 "업로드" 버튼 클릭 후 결과 확인
5) fileUploadResult.php 페이지에서 설정한 경로에 선택한 이미지가 업로드 되었는지 확인.
.
이런 식으로 테스트가 진행되었습니다.
1-3번 과정은 요청하는 페이지에 대한 과정이니 추가 설명을 생략하고, 바로 4번부터 상세하게 설명을 드리겠습니다.
fileUploadResult.php 소스 내 9번째 라인에서 "var_dump($_FILES);"로 체크한 부분을 통해
회사일과 네이버 블로그를 동시에 병행하다 보니 정신이 없지만, 힘내서 포스팅을 진행해보겠습니다!
이번 포스팅에서는 PHP에서 시간을 표시하는 date() 함수에 대해서 알아보겠습니다.
우선 PHP에서 시간을 표시할 때 아래와 같이 작성합니다.
date(시간 포맷)
시간 포맷이라는 알고 싶은 시간의 단위값(연도 또는 월일, 요일)을 입력합니다.
어떤 형식인지 아래 표를 같이 보시죠!
(예시 데이터는 2021년 1월 18일을 기준입니다.)
문자
의미
예시
Y
연도를 4자리로 표시
2021
y
연도를 2자리로 표시
21
m
0이 붙은 상태로 월을 표시
01
n
0 없이 월을 표시
1
d
0이 붙은 상태로 일을 표시
18
j
0 없이 일을 표시
18
H
0이 붙은 상태로 시를 표시(24시 표현)
01
G
0 없이 시를 표시(24시 표현)
01
i
0이 붙은 상태로 분을 표시
56
s
0이 붙은 상태로 초를 표시
55
A
오전, 오후를 대문자로 표시
AM
a
오전, 오후를 소문자로 표시
am
D
요일을 세글자로 표시
Mon
l(소문자 엘)
요일을 전체 글자로 표시
Monday
w
요일을 숫자로 표시(0~6, 0:일요일, 6:토요일)
1
M
월을 세글자로 표시
Jan
F
월을 전체 글자로 표시
January
z
올해 1월 1일부터 며칠 지났는지 표시
17
각각 대소문자를 구별하고 기능이 다르므로 사용 시 주의하셔야 합니다.
설명도 다 했으니 이제 예시 데이터를 만들어봅시다!
(date.php라는 파일명으로 아래와 같이 샘플을 작성해봅시다.)
<?php
//시간 표시 (연도, 월, 일, 시, 분, 초) : 0이 붙어 있는 예시
echo date("Y년 m월 d일 H시 i분 s초")."<br>";
//시간 표시 (연도, 월, 일, 시, 분, 초) : 0이 붙어 있지 않은 예시
echo date("y년 n월 j일 G시 i분 s초")."<br>";
//오전 오후 여부
echo date("A")."<br>";
//요일 표기
echo date("D")."<br>";
//올해가 며칠 째인지 표기
echo date("z")."<br>";
?>
결과는 아래와 같이 조회됩니다.
생각보다 간단하죠?
date() 함수 안에 괄호에서 큰따옴표(")를 사용하고 그 안에서 문자열로 표기되고,
표에서 설명한 문자열이 아닌 다른 문자열은 그대로 표기가 되는 것으로 보여집니다.
.
php를 배우시는 분들이라면, date() 함수를 자유자재로 계속 테스트 해보시길 권장 드립니다.
이번 포스팅에서는 여기서 짧게 마치고, 다음 포스팅에서는 로그(log)라는 파일을 만들어볼건데요,
개발자라면 자신이 만든 프로그램에서 어떠한 이벤트가 발생했을 때 그에 대한 기록들을 남겨두기 때문에
오늘 포스팅에서 배운 date() 함수를 포함하여 그동안 포스팅을 통해 알려드린 mkdir, fopen, fclose, fwrite 기능을
<?php
//req html 페이지로부터 전달받은 데이터 저장
$dirName = $_POST["ReqData"];
//폴더 생성
$makeDir = mkdir($dirName, '777');
//폴더 생성 여부 확인
if($makeDir){
echo $dirName." 폴더 생성 완료!";
}
else {
echo $dirName." 폴더 생성 실패!";
}
?>
<실행 결과>
.
html 페이지에서 입력한 'test'라는 이름으로 폴더가 정상적으로 생성된 것을 확인할 수 있네요!
html 페이지는 단순히 생성할 폴더명을 받는 페이지고,
지난번 txt 파일 생성하는 포스팅에서도 한번 언급했으니 넘어가겠습니다.
mkdir_res.php 페이지를 보시면 mkdir 함수가 보이실텐데요! 두가지 매개변수($dirName, '777')를 가지고 있습니다!
mkdir 함수에 들어가는 매개변수는 아래와 같이 설정해주어야 합니다.
mkdir(경로 및 생성할 폴더 이름, 퍼미션 설정값)
위에서 언급한 매개변수 중 퍼미션(permission)이라 함은 권한을 말하는데요, 3자리의 숫자로 구성됩니다.
첫번째 자리
두번째 자리
세번째 자리
소유자 권한
그룹 권한
사용자 권한
여기서 소유자는 파일 혹은 폴더 생성하는 사람, 그룹은 소유자가 속한 그룹(혹은 같은 그룹원), 사용자는 폴더를 사용하는 사람(보통 유저)을 의미합니다. 각 권한은 읽기(read), 쓰기(write), 실행(execute)으로 구성됩니다.
읽기(read)
쓰기(write)
실행(execute)
4
2
1
표를 보면서 정리하면, 읽기 권한을 적용하려면 4, 쓰기 권한을 적용하려면 2, 실행 권한을 적용하려면 1을 입력합니다.
읽기+쓰기 권한을 같이 주고 싶다면 6, 읽기+실행 권한을 같이 주고 싶다면 5,
읽기+쓰기+실행 권한을 모두 주고 싶다면 7을 입력해주시면 됩니다.
윈도우에서는 파일을 오른쪽 버튼 클릭해서 나오는 속성 창에서 보안 항목을 통해 직접 권한을 줄 수도 있습니다.
.
일단 저는 테스트이기도 하고, 보안적으로 문제될 것도 없어서 777로 설정을 하였으나
회사에서는 보안적인 이슈를 위해 755나 744 등으로도 쓰이니 용도에 맞게 권한 설정을 해주시면 될 것 같습니다.