PHP Unconference Europe 2015

POST 방식 업로드

이 기능은 사람들이 텍스트와 바이너리 파일을 올릴 수 있게 합니다. PHP의 인증과 파일 조작 함수로, 누군가에게 업로드를 허용하거나, 업로드한 파일에 대한 모든 조작을 할 수 있습니다.

PHP는 RFC-1867 호환 브라우저(넷스케이프 네비게이터 3 이상, 마이크로소프트 인터넷 익스플로러 3+패치나 패치 없이 그 이상 버전을 포함)라면 파일 업로드를 받을 수 있는 능력을 가지고 있습니다.

Note: 관련 환경설정

php.inifile_uploads, upload_max_filesize, upload_tmp_dir, post_max_size, max_input_time 지시어를 참고하십시오.

PHP는 넷스케이프 컴포저와 W3C의 Amaya 클라이언트가 사용하는 PUT 방식 파일 업로드도 지원합니다. 자세한 내용은 PUT 방식 지원을 참고하십시오.

Example #1 파일 업로드 폼

파일 업로드 화면은 다음과 같은 특별한 폼으로 만들어집니다:

<!-- 데이터 인코딩형 enctype은 꼭 아래처럼 설정해야 합니다 -->
<form enctype="multipart/form-data" action="_URL_" method="POST">
    <!-- MAX_FILE_SIZE는 file 입력 필드보다 먼저 나와야 합니다 -->
    <input type="hidden" name="MAX_FILE_SIZE" value="30000" />
    <!-- input의 name은 $_FILES 배열의 name을 결정합니다 -->
    이 파일을 전송합니다: <input name="userfile" type="file" />
    <input type="submit" value="파일 전송" />
</form>

위 예제에서 사용한 __URL__은 PHP 파일을 지정하도록 교체해야 합니다.

숨겨진 MAX_FILE_SIZE(바이트로 측정) 필드는 file 입력 필드보다 먼저 나와야하며, PHP가 받아들이는 최대 파일 크기값을 가집니다. 브라우저에서 이 값을 속이는건 매우 간단하므로, 이 기능으로 이보다 큰 파일이 막힐거라고 생각하지 마십시오. 대신, 최대 크기에 대한 PHP 설정은 속일 수 없습니다. 이 폼 요소는 사용자가 큰 파일이 전송되는걸 기다린 후에서야 파일이 너무 커서 전송에 실패한다는걸 알게 되는걸 방지하기 위해서 사용해야 합니다.

Note:

파일 업로드 폼이 enctype="multipart/form-data"를 가졌는지 확인하십시오. 그렇지 않으면 파일 업로드는 작동하지 않습니다.

전역 $_FILES가 PHP 4.1.0부터 존재합니다. (이전 버전에서는 $HTTP_POST_FILES를 사용하십시오) 이 배열은 업로드된 파일 정보를 가지고 있습니다.

예제 폼에서 $_FILES의 내용은 다음과 같습니다. 위 예제 스크립트에서 사용한 파일 업로드 이름 userfile로 표현함에 주의하십시오. 어떠한 이름이라도 가질 수 있습니다.

$_FILES['userfile']['name']

클라이언트 머신에 존재하는 파일의 원래 이름.

$_FILES['userfile']['type']

브라우저가 이 정보를 제공할 경우에, 파일의 mime 형식. 예를 들면 "image/gif". 그러나 이 mime 형은 PHP 측에서 확인하지 않으므로 이 값을 신용하지 마십시오.

$_FILES['userfile']['size']

업로드된 파일의 바이트로 표현한 크기.

$_FILES['userfile']['tmp_name']

서버에 저장된 업로드된 파일의 임시 파일 이름.

$_FILES['userfile']['error']

파일 업로드에 관련한 에러 코드. PHP 4.2.0에서 추가되었습니다.

php.ini에서 upload_tmp_dir을 이용하여 다른 위치를 지정하지 않는 한, 파일은 서버의 기본 임시 디렉토리에 저장됩니다. 서버의 기본 디렉토리는 PHP를 실행하는 환경의 환경 변수 TMPDIR을 통해서 변경할 수 있습니다. PHP 스크립트 내부에서 putenv()를 통해서 설정하는 것은 작동하지 않습니다. 물론, 이 환경 변수는 업로드된 파일에 다른 작업을 할 때 사용할 수 있습니다.

Example #2 파일 업로드 확인하기

추가 정보는 is_uploaded_file()move_uploaded_file()에 대한 함수 정보를 참고하십시오. 다음 예제는 폼에서 전송된 파일 업로드를 처리합니다.

<?php
// 4.1.0 이전의 PHP에서는, $_FILES 대신에 $HTTP_POST_FILES를
// 사용해야 합니다.

$uploaddir '/var/www/uploads/';
$uploadfile $uploaddir basename($_FILES['userfile']['name']);

echo 
'<pre>';
if (
move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
    echo 
"파일이 유효하고, 성공적으로 업로드 되었습니다.\n";
} else {
    print 
"파일 업로드 공격의 가능성이 있습니다!\n";
}

echo 
'자세한 디버깅 정보입니다:';
print_r($_FILES);

print 
"</pre>";

?>

업로드된 파일을 받는 PHP 스크립트는 업로드된 파일로 무엇을 할 지 결정하는 로직을 포함하고 있어야 합니다. 예를 들면, $_FILES['userfile']['size'] 변수는 너무 작거나 큰 파일을 처리하는데 이용할 수 있습니다. $_FILES['userfile']['type'] 변수는 형식 기준에 맞지 않는 파일을 처리하는데 이용할 수 있습니다. 그러나 이것은 확인 작업 중 하나가 되어야 합니다. 이 값은 완전히 클라이언트에서 만들어지며, PHP 측에서 확인하지 않습니다. PHP 4.2.0부터, $_FILES['userfile']['error']를 이용하여 에러 코드에 따라서 처리하게 할 수 있습니다. 어떠한 로직이건 간에, 임시 디렉토리로부터 파일을 지우거나 다른 곳으로 이동해야 합니다.

폼에서 어떠한 파일도 선택하지 않으면, PHP는 $_FILES['userfile']['size']를 0으로, $_FILES['userfile']['tmp_name']은 없습니다.

요청이 끝날 때, 이동하거나 이름을 변경하지 않은 임시 디렉토리의 파일은 삭제됩니다.

Example #3 파일 배열 업로드하기

PHP는 파일에서도 HTML 배열 기능을 지원합니다.

<form action="" method="post" enctype="multipart/form-data">
<p>그림들:
<input type="file" name="pictures[]" />
<input type="file" name="pictures[]" />
<input type="file" name="pictures[]" />
<input type="submit" name="전송" />
</p>
</form>
<?php
foreach ($_FILES["pictures"]["error"] as $key => $error) {
    if (
$error UPLOAD_ERR_OK) {
        
$tmp_name $_FILES["pictures"]["tmp_name"][$key];
        
$name $_FILES["pictures"]["name"][$key];
        
move_uploaded_file($tmp_name"data/$name");
    }
}
?>

apc.rfc1867에 따라서 파일 업로드 진행바를 적용할 수 있습니다.

add a note add a note

User Contributed Notes 10 notes

up
19
daevid at daevid dot com
5 years ago
I think the way an array of attachments works is kind of cumbersome. Usually the PHP guys are right on the money, but this is just counter-intuitive. It should have been more like:

Array
(
    [0] => Array
        (
            [name] => facepalm.jpg
            [type] => image/jpeg
            [tmp_name] => /tmp/phpn3FmFr
            [error] => 0
            [size] => 15476
        )

    [1] => Array
        (
            [name] =>
            [type] =>
            [tmp_name] =>
            [error] => 4
            [size] =>
        )
)

and not this
Array
(
    [name] => Array
        (
            [0] => facepalm.jpg
            [1] =>
        )

    [type] => Array
        (
            [0] => image/jpeg
            [1] =>
        )

    [tmp_name] => Array
        (
            [0] => /tmp/phpn3FmFr
            [1] =>
        )

    [error] => Array
        (
            [0] => 0
            [1] => 4
        )

    [size] => Array
        (
            [0] => 15476
            [1] => 0
        )
)

Anyways, here is a fuller example than the sparce one in the documentation above:

<?php
foreach ($_FILES["attachment"]["error"] as $key => $error)
{
      
$tmp_name = $_FILES["attachment"]["tmp_name"][$key];
       if (!
$tmp_name) continue;

      
$name = basename($_FILES["attachment"]["name"][$key]);

    if (
$error == UPLOAD_ERR_OK)
    {
        if (
move_uploaded_file($tmp_name, "/tmp/".$name) )
           
$uploaded_array[] .= "Uploaded file '".$name."'.<br/>\n";
        else
           
$errormsg .= "Could not move uploaded file '".$tmp_name."' to '".$name."'<br/>\n";
    }
    else
$errormsg .= "Upload error. [".$error."] on file '".$name."'<br/>\n";
}
?>
up
13
michael
4 years ago
Just a little note, when I was trying to get this to work on my webserver I got error telling me the permissions were wrong, I checked and couldn't see anything wrong then I thought to try "./" for my upload directory instead of the full address which was something like "home/username/public_html/uploaddir". This will save it in the same directory as your script for the program above thats something like

<?php
// In PHP versions earlier than 4.1.0, $HTTP_POST_FILES should be used instead
// of $_FILES.

$uploaddir = './';//<----This is all I changed
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);

echo
'<pre>';
if (
move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
    echo
"File is valid, and was successfully uploaded.\n";
} else {
    echo
"Possible file upload attack!\n";
}

echo
'Here is some more debugging info:';
print_r($_FILES);

print
"</pre>";

?>

I know something trivial but when your new to this, those kind of things elude you.
up
5
eslindsey at gmail dot com
5 years ago
Also note that since MAX_FILE_SIZE hidden field is supplied by the browser doing the submitting, it is easily overridden from the clients' side.  You should always perform your own examination and error checking of the file after it reaches you, instead of relying on information submitted by the client.  This includes checks for file size (always check the length of the actual data versus the reported file size) as well as file type (the MIME type submitted by the browser can be inaccurate at best, and intentionally set to an incorrect value at worst).
up
1
Anonymous
1 year ago
Normalizing $_FILES structure:

<?php
    $files
= [];
   
$fix = function (&$files, $values, $prop) use (&$fix) {
        foreach (
$values as $key => $value) {
            if (
is_array($value)) {
               
$fix($files[$key], $value, $prop);
            } else {
               
$files[$key][$prop] = $value;
            }
        }
    };
    foreach (
$_FILES as $name => $props) {
        foreach (
$props as $prop => $value) {
            if (
is_array($value)) {
               
$fix($files[$name], $value, $prop);
            } else {
               
$files[$name][$prop] = $value;
            }
        }
    }
?>
up
1
Age Bosma
3 years ago
"If no file is selected for upload in your form, PHP will return $_FILES['userfile']['size'] as 0, and $_FILES['userfile']['tmp_name'] as none."

Note that the situation above is the same when a file exceeding the MAX_FILE_SIZE hidden field is being uploaded. In this case $_FILES['userfile']['size'] is also set to 0, and $_FILES['userfile']['tmp_name'] is also empty. The difference would only be the error code.
Simply checking for these two conditions and assuming no file upload has been attempted is incorrect.

Instead, check if $_FILES['userfile']['name'] is set or not. If it is, a file upload has at least been attempted (a failed attempt or not). If it is not set, no attempt has been made.
up
-4
Mark
4 years ago
$_FILES will be empty if a user attempts to upload a file greater than post_max_size in your php.ini

post_max_size should be >= upload_max_filesize in your php.ini.
up
-3
claude dot pache at gmail dot com
5 years ago
Note that the MAX_FILE_SIZE hidden field is only used by the PHP script which receives the request, as an instruction to reject files larger than the given bound. This field has no significance for the browser, it does not provide a client-side check of the file-size, and it has nothing to do with web standards or browser features.
up
-4
gustavo at roskus dot com
3 years ago
Some hosting does not have permission to use the move_uploaded_file function should replace the copy function
up
-12
zingaburga at hotmail dot com
3 years ago
The documentation is a little unclear about the MAX_FILE_SIZE value sent in the form with multiple file input fields.  The following is what I have found through testing - hopefully it may clarify it for others.

The MAX_FILE_SIZE is applied to each file (not the total size of all files) and to all file inputs which appear after it.  This means that it can be overridden for different file fields.  You can also disable it by sending no number, or sending 0 (probably anything that == '0' if you think about it).

Example:

<form enctype="multipart/form-data" action="." method="POST">
  <!-- no maximum size for userfile0 -->
  <input name="userfile0" type="file" />

  <input type="hidden" name="MAX_FILE_SIZE" value="1000" />
  <!-- maximum size for userfile1 is 1000 bytes -->
  <input name="userfile1" type="file" />
  <!-- maximum size for userfile2 is 1000 bytes -->
  <input name="userfile2" type="file" />

  <input type="hidden" name="MAX_FILE_SIZE" value="2000" />
  <!-- maximum size for userfile3 is 2000 bytes -->
  <input name="userfile3" type="file" />

  <input type="hidden" name="MAX_FILE_SIZE" value="0" />
  <!-- no maximum size for userfile4 -->
  <input name="userfile4" type="file" />
</form>
up
-12
mail at markuszeller dot com
3 years ago
If you want to increase the upload size, it could make sense to allow it only to a specified directory and not in the php.ini for the whole domain or server. In my case it worked very well placing that into the .htaccess file like this:

php_value    upload_max_filesize    100M
php_value    post_max_size    101M

Remember, post_max_size must be bigger than the upload_max_filesize.
To Top