* 오늘의 진도
PHP
파일
17. 파일 - 파일의 제어 - 파일 다루기
newfile: readme.txt 파일이 카피된 결과
copy 함수의 첫번째 인자 - 원본 파일의 이름
두 번째 인자 - 원본 파일을 복사한 파일의 이름
이 copy라는 함수를 실행했을 때,
리턴된 값이 true이면 복사가 성공적으로 된 것이고
false으면 복사가 실패했다는 뜻이다.
copy 앞에 !가 있으므로
만약 copy의 리턴값이 true가 아니라면
이 if문이 실행되면서 복사가 실패했다는 것을 화면에 출력하는 앱이다.
이 7.php 파일을 실행시키면 복사가 된다.
검색어: php file function
www.php.net/manual/en/function.file.php
사이드바에 파일과 관련된 php 함수들의 리스트가 출력된다.
17. 파일 - 파일의 제어 - 읽기 쓰기
기존에 존재하고 있는 파일을 읽어서 문자열을 리턴
이 함수는 로컬 컴퓨터에 있는 파일만 읽을 수 있는 것이 아니라
컴퓨터 밖에 있는 파일도 읽을 수 있다.
이 url에 해당되는 정보(html 문서)를 네트워크를 통해 읽어서
그 결과를 $homepage라는 변수에 담고
우리가 $homepage라는 변수를 echo로 화면에 출력하게 되면
그 url 웹페이지와 똑같은 내용이 출력된다.
→ 어떤 웹페이지에 있는 내용을 읽어서 그것을 분석한 다음에 처리하는 작업을 할 때 이용할 수 있다.
파일을 새로 만들어서 그 파일에 데이터를 기록하는 것
(파일 이름, 기록할 정보)
fopen
https://www.php.net/manual/en/function.fopen.php
fopen이라는 함수의 역할은 파일을 여는 것이다.
그 후에 파일을 읽거나 쓸 수 있다.
fopen (읽거나 쓰고자 하는 파일, mode(아래 표 참조)
파일 포인터의 위치: 그 파일에 쓰기 작업을 할 경우
그 부분에 쓰기 작업이 이루어진다.
fopen은 파일을 열기만 해서는 아무 의미가 없다.
파일을 읽거나 써야 하는데,
읽을 때 사용하는 함수가 fread,
쓸 때 사용하는 함수가 fwrite이다.
파일에 대한 작업이 끝난 다음에는 fclose를 해서
그 파일에 대한 제어권을 다시 돌려줘야
다른 앱이 그 파일을 제어할 수 있게 된다.
17. 파일 - 파일의 제어 - 트러블 슈팅
유닉스, 윈도우, OS X와 같은 시스템을 사용할 때 발생할 수 있는 가장 대표적인 문제가
다음 그림과 같은 메시지를 만나는 것이다.
file_get_contents라는 함수를 썼을 때,
권한 때문에 거부되었다는 에러이다.
보안과 권한과 관련된 부분이다.
이유를 살펴보자.
1.php는 file_get_contents라는 함수를 이용해서
readme.txt라는 파일을 읽으려는 시도를 하고 있다.
까만 화면을 보면
readme.txt의 소유자는 root이고 그룹도 root이다.
그런데 현재 우리가 이 php 앱을 동작할 때는
이 php앱은 www-data라는 id값(사용자 계정)으로 동작하고 있는 것이다.
※ www-data라는 것은 어떤 OS에서 어떤 웹서버(아파치 등)를 쓰느냐에 따라서 달라질 수 있다.
어쨌든 www-data라는 사용자 계정으로 동작하고 있는 아파치이기 때문에,
이 php는 그 아파치에 모듈의 형태로 동작하고 있다.
그래서 이 php도 www-data라는 id로 이 시스템에서 활동하고 있는 것이라고 보면 된다.
그래서 www-data라는 사용자는 root도 아니고 그룹도 아닌, other이다.
여기↑에서 제일 끝에 3개의 칸이 other의 권한이다.
r도 없고 w도 없고 x도 없으므로
이 파일에 대한 소유자도 아니고 그룹도 아닌 사용자는
읽기, 쓰기, 실행 권한이 모두 없다는 뜻이다.
어떻게 하면 이 파일을 읽을 수 있을까?
방법1) 이 파일에 대한 소유자/그룹을 www-data로 바꾼다.
소유자를 www-data로 바꾸는 방법:
방법2) other로 읽을 수 있게 권한 조정
2.php는 writeme.txt 파일을 새로 만들어서
coding everybody라는 텍스트를 기록하려고 한다.
이번에도 권한 관련 에러가 떴다.
파일을 생성할 때는, 파일을 생성하려는 디렉토리에 write 권한이 있어야 한다.
.이 현재 디렉토리를 의미한다.
현재 디렉토리의 소유자는 root, 그룹도 root이다.
→ 이 php 앱을 동작시키면 other의 권한으로 실행돼야 하는데,
여기서 other의 권한은 읽기도 가능하고, 실행도 가능한데, 쓰기가 불가능하다.
방법1) other도 쓰기가 가능하게 바꾼다.
방법2) 소유자를 www-data로 바꾼다. ↓
∴ 파일 관련 작업을 하기 전에,
그 파일이 읽기/쓰기 가능한 상태인지 확인할 수 있는 방법이 있다.
17. 파일 - 디렉토리 제어
우리가 php 앱을 동작시키고 파일과 관련된 명령들을 내리게 되면,
그 앱이 동작하고 있는 디렉토리를 기준으로 명령이 이루어진다.
ex) 파일 읽기/쓰기
그런 경우에, 현재 어떤 디렉토리에 이 앱이 위치하고 있는지 알고 싶을 수 있다.
그리고 이 앱이 명령을 내렸을 때,
그 명령이 영향을 미치는 경로를 변경하고 싶을 수도 있다.
그 때 사용하는 함수가 getcwd와 chdir이다.
getcwd - 인자를 받지 않는다.
이 함수를 실행시키면, 현재 이 php 앱(1.php)이 동작하고 있는 현재 디렉토리를 알려줄 것이다.
그 다음에 chdir이라는 내장함수에 경로(디렉토리)를 주게 되면
그 경로로 현재 경로(디렉토리)가 바뀌게 된다.
그 상태에서 그 파일을 핸들링하는 명령을 내리게 되면
그 디렉토리를 대상으로 명령이 이루어진다.
..은 현재 디렉토리의 부모 디렉토리로 이동한다는 뜻이다.
2행의 ./은 현재 디렉토리라는 뜻이다.
그리고 그 디렉토리를 3행의 ( ) 안에 넣은 것이다.
4행의 1은 정렬 순서를 반대로 바꾸게 한다.
1st 인자: 생성하려는 디렉토리의 이름 (1 디렉토리 안에 2 디렉토리 안에 3 디렉토리 안에 4 디렉토리)
2nd 인자: 생성한 디렉토리가 파일 상의 어떠한 권한을 갖고 있을 것이냐
0
7 - owner - 읽기, 쓰기, 샐행 모두 가능
0 - group
0 - other
※ 읽기4 쓰기2 실행1
3rd 인자: true일 경우, 1st 인자의 경로가 만약 존재하지 않는다면, 그 경로를 만들어준다.
17. 파일 - 파일 업로드 - 업로드를 위한 폼
업로드: 사용자가 선택한 파일을 전송했을 때,
그것을 php 쪽에서 받아서 우리가 원하는 디렉토리에 위치시키는 것
9행 - 찾아보기와 그 오른쪽 칸을 의미한다.
8행의 type은 hidden이다.
그러면 화면에 어떠한 요소/부분도 표시되지 않는 컨트롤이 만들어진다.
이런건 왜 쓰느냐?
서버쪽으로 어떤 데이터를 전송하는데,
그것이 사용자가 입력한 데이터가 아니라,
어떤 필요에 의해서 전송할 필요가 있을 경우에.
MAX_FILE_SIZE는 사용자가 전송하는 파일의 최대 크기를 지정한다.
이것은 type="file" 보다 위치상 먼저 나타나야 한다.
이것은 개발자 도구를 통해서 수정하는 것으로 얼마든지 속일 수 있기 때문에
정말로 최대 크기를 강제하고 싶다면
php 설정에서 변경해야 한다.
이것은 언제 사용하냐면, 우리가 php 설정을 직접 변경하기 어려운 경우
(대표적으로 웹 호스팅을 사용하는 경우)에 적합하고,
이것 자체는 보안으로서의 의미는 없다.
파일을 전송하는 부분이 포함되어 있다면 반드시 지켜야할 것이 두 가지 있다. (②, ③)
7행의 form 태그는 그 form 태그가 감싸고 있는 컨트롤들이
어디로 데이터를 전송할 것인가와 같은 정보를 지장한다.
① action - 1.php 파일로 지금 입력한 정보들(선택한 파일을 포함해서)을 전송하겠다는 뜻
② method - get 말고 post를 사용해야 한다.
③ enctype(encoding type) - 반드시 multipart/form-data라고 지정해줘야 한다.
그렇지 않으면 파일이 전송되지 않는다.
17. 파일 - 파일 업로드 - HTTP Request
파일을 전송할 때 내부적으로는 어떤 일이 일어나는지 보자.
Fiddler - 웹브라우저가 만들어내는, 서버쪽에 요청하는 메시지를, 로우 레벨로 볼 수 있는 프로그램
이 프로그램이 없어도 웹브라우저의 네트워크 모니터링 기능을 참조하면 된다.
파일을 선택한 후에 업로드를 하게 되면 이쪽에 리스트가 추가될 것이다.
클라이언트와 서버, 즉 웹브라우저와 웹서버 사이에 통신하는 내용이 이곳에 기록된다.
여기 Raw 탭에 나오는 내용은
웹브라우저가 만들어낸, HTTP 프로토콜을 준수하는, HTTP 리퀘스트 데이터이다.
브라우저는 이렇게 생긴 텍스트 파일을 만들어서
그것을 서버쪽으로 전송한다.
서버는 이 텍스트 파일을 읽어서 여기에 적혀있는 것을 분석한 다음에
자신이 해야할 일을 결정하게 된다.
첫째줄 - 우리가 전송한 데이터 형식은 POST 형식이다.
끝에서 두번째줄 - 데이터를 전송할 때, 인코딩을 multipart/form-data라고 했었다.
그것이 여기에 Content-Type으로 적용된다.
그러면 서버는 이것을 보고, 이것에 맞게 HTTP 리퀘스트 데이터를 해석해서 처리하게 된다.
또한 multipart/form-data 형식으로 데이터를 전송하면,
Content-Disposition이라는 속성이 생기고, form-data라고 나온다.
MAX_FILE_SIZE는 1000으로 전달됐다. (아까 1000으로 수정해서)
그리고 Content-Disposition의 form-data로 이번에는 userfile이 있다.
(우리가 선택한 파일의 file 필드의 name값이 userfile이었다.)
그리고 사용자가 선택한 파일의 이름이 filename이라는 속성의 값으로 94.png라고 적혀있다.
그럼 서버가 그 파일의 내용을 받으면, 파일의 원래 이름이 94.png라는 사실을 알 수 있게 된다.
이 부분은 이 파일에 대한 실제 내용인데,
이미지 파일의 경우 내용이 2진수로 이루어져 있기 때문에,
사람이 해석할 수 있는 데이터가 아니다.
컴퓨터는 이것을 받아서 해석한 다음에,
이것을 적절하게 파일로 저장할 수 있다.
이런 식으로 파일의 내용이 기록돼서
HTTP 리퀘스트(요청) 포맷/형식에 따라서
HTTP 요청 메시지를 작성해서 클라이언트가 서버로 전송하게 되면,
서버 역시도 HTTP 리퀘스트 포맷이 어떤 형식을 갖고 있는지 알기 때문에
그 요청 문서의 요청 데이터를 분석해서 적절한 처리를 하게 되는 것이다.
그러면 php 앱이 처리한 다음에 아파치로 보내주면
아파치가 그것을 웹브라우저(클라이언트)로 전송해주는데,
전송해주는 실제 내용은 아래와 같다.
200 OK는 성공적으로 잘 처리했다는 뜻이다.
Content-Type이 text/html인 것은
서버가 클라이언트로 전송하는 데이터가 html 문서라는 것을 알려준다.
그러면 브라우저는 저것을 본 다음에
html 문서로 서버 쪽에서 전송된, 서버가 응답한 데이터를 해석할 수 있게 된다.
그리고 실질적인 html 내용은 한 줄 띄운 다음에 나타나게 된다.
그럼 브라우저는 위쪽의 데이터들을 참고해서 밑의 데이터를 해석한다.
밑의 데이터가 html이라는 것을 알고 있기 때문에,
html의 문법에 따라서 이것을 해석해서 브라우저의 화면에 표시해주게 된다.
17. 파일 - 파일 업로드 - 수신 애플리케이션
업로드된 파일을 서버쪽에서 받는,
수신자 측의 구현/동작 방법을 알아보자.
여기서 Upload 버튼을 클릭했을 때,
사용자가 선택한 파일이 서버쪽으로 전송된다.
전송되는 과정에서 HTTP 프로토콜에 따라서
요청 메시지를 서버 쪽으로 전달하면,
서버는 그것을 받아서 일련의 처리를 진행하게 된다.
그것을 담당하는 파일이 1.php 파일이다.
우리가 서버로 파일을 전송하게 되면,
이 전송한 파일에 대한 여러가지 정보들을 php 엔진이 우리하게 제공하게 되는데,
그 방법으로, $_FILES라는 변수안에, 업로드된 파일에 대한 정보가 담겨있다.
※ exit: 이것이 등장하면 이 뒤에 있는 코드는 그 어떤 것도 실행되지 않고,
exit에서 즉시 php 앱이 종료된다.
var_dump로 $_FILES 안에 담겨있는 데이터를 보자.
검은 화면을 저장하고 업로드 버튼을 누르면 다음과 같이 뜬다.
마우스 우클릭 후 페이지 소스보기를 하면 좀더 보기좋게 뜬다.
먼저 $_FILES는 배열이라는 것을 알 수 있다.
그리고 첫 번째 값은 userfile이라는 식별자/key/index 값을 갖고 있다.
userfile은 우리가 form에서 파일 컨트롤의 name으로 지정했던 값이다.
그리고 userfile에는 또 배열이 담겨져 있는데,
그 배열 안에는 5개의 요소가 있다.
① name - 94.png 우리가 업로드한 파일의 실제 이름
② type - 그 파일의 형식. 이미지이고 그 중에서 png이다.
③ tmp_name - tmp는 임시라는 뜻이다. 왜 임시 이름이 있을까?
클라이언트 브라우저를 통해서 어떤 파일을 서버로 전송하게 되면,
그 파일은 자동으로 이 서버에 있는 임시 디렉토리 안에 무조건 들어가게 된다.
그리고 우리는 그 임시 디렉토리 안에 있는 업로드된 파일을
우리가 원하는 디렉토리로 프로그래밍적으로 이동시켜야 한다.
tmp_name은 브라우저가 전송한 파일이 서버에 위치하는 임시 디렉토리의 경로를 나타내고 있다.
우리가 이 경로를 알아야지, 이 파일을 우리가 원하는 디렉토리로 옮길 수 있다.
④ error - 파일을 업로드하는 과정에서 오류가 있었다면, 여기에 어떤 값이 있을 것이다.
그런데 0이라는 것은 에러가 없었다는 뜻이다.
⑤ size - 업로드 된 파일의 바이트 단위 크기
13289바이트는 약 13kb
그럼 만약에 여러개의 파일을 전송했다면 어떻게 될까?
10행을 추가하고 저장했다.
찾아보기가 두개 생겨서 두 개 파일을 업로드했다.
$_FILES라는 변수 안에 배열이 두 개의 값을 갖는다.
그리고 그 두개의 값들은 연관배열 형태로 저장되어 있다.
하나는 userfile, 하나는 userfile2로 식별된다.
- input 태그의 name으로 지정한 이름
$_FILES라는 변수는 특수한 변수이고, 약속되어 있는 변수이다.
사용자가 전송한 파일이 $_FILES라는 변수에 담겨있다는 것을 알고 있어야 한다.
이것이 파일을 업로드하고 업로된 파일을 제어하는 것에 있어서 핵심적인 기본지식이다.
이것을 기반으로 1.php 파일을 보면 수월할 것이다.
$uploaddir의 값은 - 임시 디렉토리에 위치하고 있는, 업로드된 파일을,
이동시키려고 하는 파일 디렉토리의 경로를 지정한 것이다.
※ 주의: 맨 뒤에 \가 두개 들어가야 한다. 하나는 escaping 기능으로 사용한 것이다.
$uploadfile의 값은 - 업로드되어 임시 디렉토리에 머물고 있는 파일이
어느 디렉토리의 어느 파일명으로 저장돼야 하는지에 대한 정보를 담고 있다.
앞 쪽에는 저장 될 파일 디렉토리
.은 문자와 문자를 결합하는 연산자
basename은 이따가 보고(보안 관련)
$_FILES는 업로드된 파일에 대한 정보를 담고 있는 변수다.
userfile은 이 파일 컨트롤의 name값이 userfile인 컨트롤을 통해서 업로드된 파일에 대한 정보가 이 안에 담겨있다.
name은 그 파일의 원래 이름이 nam이라는 키의 값으로 저장돼 있다.
→ 이렇게 해서, 파일이 저장될 디렉토리와 파일의 이름을 결합해서, 그 파일의 최종적인 경로를 지정하는 것이다.
move_uploaded_file 함수
첫번째 인자 - temp_name은 업로드된 파일이 최초로 위치하고 있는 임시 디렉토리의 경로 (실제 파일의 경로)
두번째 인자 - $uploadfile는 임시 디렉토리에 있는 파일이 최종적으로 위치해야 하는 경로+파일의 이름의 값이 지정되어있다. (그 파일을 이동시킬 경로)
물론 우리는 move_uploaded_file 함수를 사용하지 않고,
그냥 파일을 이동시키는 명령을 쓸 수도 있다.
그런데도 move_uploaded_file 함수를 사용하는 이유는,
이 함수에, 업로드된 파일이 보안상 문제나 위험성이 있는지를 내부적으로 체크하는 로직이 포함되어 있기 때문이다.
그래서 이것이 php에서 권장하는 공식적인 접근 방법이다.
move_uploaded_file 함수는 두 가지의 값을 리턴한다. true/false
true - 업로드에 성공했고, 파일이 잘 이동됐고, 보안상 문제점이 없다.
false - 보안/권한에 문제가 있다.
print_r은 var_dump와 거의 비슷하다.
우리가 업로드한 파일에 대한 정보를 담고 있는 $_FILES라는 변수에 담겨있는 데이터를 화면에 출력한다.
img의 src 속성으로, 업로드된 파일이 임시 디렉토리에 저장이 되었다가 최종적으로 이동된 경로를 부여한다.
경로가 이렇게 쓰여있는 이유는 조금 이따가 살펴보자.
파일 업로드에는 대단히 복잡한 흐름이 내부적으로 깔려 있다.
그것을 php가 아주 간편하게 해준다.
이것이 우리가 php와 같은 서버측 언어를 사용하는 중요한 이유이다.
17. 파일 - 파일 업로드 - 기타 설명
ini_set은 php의 설정을 우리가 런타임으로 지정하는 함수이다.
※ 런타임: php 앱이 실행되는 시점 ↔ php 앱의 기본 설정(php.ini)
ini_set은 php.ini의 세팅을 무효화하고 실행되고 있는 이 php 코드 안에서만 유효한 설정을 세팅할 수 있게 해준다.
display_errors - 기본적인 php 세팅이, display_errors가 꺼져있는 경우가 많기 때문에,
이것을 켜는 명령을 여기에 준 것이다.
1 = On
8행은, 업로드를 할 때 display_errors를 켜야 한다는 뜻이 아니다.
다만 업로드를 할 때 에러가 많이 발생하기 때문에,
그것을 트러블슈팅하기 쉽게 하라고 코드를 추가한 것 뿐이다.
basename - 이 뒤에 있는 파일의 실제 이름이, 정확하게 그 파일의 이름을 의미하도록 하기 위해서 사용한 것이다.
https://www.php.net/manual/en/function.basename.php
basename의 사용법
1) 뒤에 있는 .d에 의해서 앞에꺼 마지막에 있는 .d와 /etc가 사라진 것이다.
2) /etc는 사라지고 뒤에 있는 파일명만 남게 된다.
3) 경로를 지정하게 되면, 앞뒤의 \가 사라져서, etc라고 하는 디렉토리 가장 끝에 있는 디렉토리 이름만 남는다.
그래서 basename은, 업로드된 파일의 이름에 경로를 포함시키면 잠재적으로 보안상의 이슈가 될 수 있기 때문에
업로드한 파일의 이름을 좀더 확실하게 하기 위해서, 파일로서의 의미만을 갖게 하기 위해서 사용한 것이다.
9행에서 C:\BitNami\wampstack-5.4.20-0\apache2\htdocs\는 document root에 해당한다.
$uploaddir이라는 디렉토리는,
업로드된 파일을 임시 저장소에서 파일 디렉토리로 이동시키기 위해서 사용되는 경로이기 때문에,
서버 컴퓨터 상의 경로를 사용해야 한다.
윈도우를 사용하면 C:\BitNami\wampstack-5.4.20-0\apache2\htdocs\ 이 주소가 document root이다.
cf) img 태그는 html 태그이다.
html 태그는 웹브라우저가 해석한다.
웹브라우저는 도메인을 통해서 서버에 접속하기 때문에,
여기에 나오는 경로는 시스템 파일 경로가 아니라
url, uli(도메인과, 그 파일이 시스템 상에 위치하는 경로)를 사용한 것이다.
그런데 앞에 도메인을 붙이지 않았기 때문에 이것은 상대경로가 된다.
상대경로: 현재 우리가 동작시키고 있는 1.php라고 하는 파일이 위치하고 있는 디렉토리를 기준으로 해서
1.php가 위치하고 있는 디렉토리의 하위 디렉토리 중에
file이라는 디렉토리 아래에 업로드된 파일의 이름(파일명)을 이미지의 소스(src)의 속성값으로 지정한 것이다.
17. 파일 - 이미지 다루기 - GD Lib 소개 및 설치
php에서 이미지를 다루는 방법은 여러가지가 있는데,
가장 많이 사용되는 것이 gd 라이브러리를 이용하는 것이다.
php 자체적으로 코어라는 부분에 이미지를 처리하는 기능은 포함되어 있지 않기 때문에,
외부 프로그램/외부 라이브러리의 힘을 빌려야 한다.
php를 설치하는 방법에 따라 다르긴 하지만,
gd 라이브러리가 포함된 상태로 php가 설치되는 경우도 있고,
웹호스팅과 같이 우리가 사용하는 서비스에서
기본적으로 gd 라이브러리같은 것을 제공하는 경우가 많다.
자신의 컴퓨터에 gd 라이브러리가 설치되어 있는지 확인하는 방법:
phpinfo 함수를 실행시킨다.
gd라는 항목이 있고, GD Support가 enabled로 되어 있으면
gd 라이브러리를 사용할 수 있는 것이다.
gif, jpg, png, bmp 등의 지원여부도 나와있다.
만약 gd 라이브러리가 없을 경우 - 2:33~
17. 파일 - 이미지 다루기 - 이미지에 글쓰기
여기부터 미수강
17. 파일 - 이미지 다루기 - 워터마크 만들기
.
'PHP > 생활코딩' 카테고리의 다른 글
5/25(3) 생활코딩 (0) | 2020.05.25 |
---|---|
5/25(2) 생활코딩 (0) | 2020.05.25 |
5/24(4) 생활코딩 * Composer 나중에 보기 * (0) | 2020.05.24 |
5/24(3) 생활코딩 (0) | 2020.05.24 |
5/24(2) 생활코딩 (0) | 2020.05.24 |
댓글