|
|
Kapitola 20. Zpracov�n� uploadu soubor�
PHP umo��uje zpracov�n� uploadu soubor� z jak�hokoli prohl�e�e
vyhovuj�c�ho RFC-1867 (co� zahrnuje mj. Netscape Navigator 3 a pozd�j��,
Microsoft Internet Explorer 3 se z�platou od Microsoftu, nebo pozd�j��
bez z�platy). Tato schopnost umo��uje lidem uploadovat textov� i
bin�rn� soubory. S autentizac� poskytovanou PHP a s funkcemi pro manipulaci
se soubory m�te plnou kontrolu nad t�m, kdo sm� uploadovat a co se m�
ud�lat s uploadovan�m souborem.
Nezapome�te, �e PHP podporuje tak� uploady metodou PUT tak, jak se pou��v�
v Netscape Composeru a v editoru Amaya od W3C. Pro bli��� detaily viz
Podpora metody PUT.
Obrazovka pro upload souboru m��e b�t tvo�ena speci�ln�m formul��em, kter�
vypad� podobn� jako tento:
P��klad 20-1. Formul�� pro upload souboru <form enctype="multipart/form-data" action="_URL_" method="post">
<input type="hidden" name="MAX_FILE_SIZE" value="1000">
Send this file: <input name="userfile" type="file">
<input type="submit" value="Send File">
</form> |
|
_URL_ by m�lo ozna�ovat PHP soubor. Skryt� pole MAX_FILE_SIZE mus�
p�edch�zet pole pro vlo�en� souboru a jeho hodnota specifikuje maxim�ln�
akceptovanou velikost souboru. Hodnota je v bytech.
Varov�n� |
Hodnota MAX_FILE_SIZE je z hlediska prohl�e�e pouze informativn�.
Je snadn� ji obej�t. Tak�e nepo��tejte s t�m, �e prohl�e� se bude
chovat tak, jak si p�ejete. Nastaven� maxim�ln� velikosti v PHP v�ak
samoz�ejm� nem��e b�t obelst�no.
|
Prom�nn� definovan� pro uploadovan� soubory se li�� v z�vislosti na
verzi a konfiguraci PHP. Pokud je aktivn� volba
track_vars, bude inicializov�no pole
$HTTP_POST_FILES/$_FILES. Kone�n�, souvisej�c� prom�nn� mohou b�t
inicializov�ny jako glob�ln�, pokud je zapnuta volba
register_globals.
Ov�em pou��v�n� glob�ln�ch prom�nn�ch nen� doporu�eno.
Po �sp�n�m uploadu budou v c�lov�m skriptu definov�ny n�sleduj�c�
prom�nn�:
Pozn�mka:
track_vars je od PHP 4.0.3
v�dy zapnuto. U PHP 4.1.0 a pozd�j��ch m��e b�t pou�ito $_FILES
nam�sto $HTTP_POST_FILES. $_FILES
je v�dy glob�ln� prom�nn�, tak�e by se nem�la pou��vat specifikace
global pro prom�nnou $_FILES.
$HTTP_POST_FILES/$_FILES
obsahuje informace o uploadovan�m souboru.
Obsah $HTTP_POST_FILES je takov�to (uv�domte si, �e
se p�edpokl�d� pou�it� n�zvu uploadovan�ho souboru 'userfile' tak,
jako v p��kladu v��e):
- $HTTP_POST_FILES['userfile']['name']
Origin�ln� n�zev souboru na klientsk�m po��ta�i.
- $HTTP_POST_FILES['userfile']['type']
MIME typ souboru, pokud prohl�e� tuto informaci poskytuje
(nap�. "image/gif").
- $HTTP_POST_FILES['userfile']['size']
Velikost uploadovan�ho souboru v bytech.
- $HTTP_POST_FILES['userfile']['tmp_name']
Do�asn� n�zev souboru, pod n�m� byl uploadovan� soubor ulo�en na server.
Pozn�mka:
PHP 4.1.0 a pozd�j�� podporuj� zkr�cen� n�zev prom�nn�
$_FILES. PHP 3 nepodporuje
$HTTP_POST_FILES.
Obsah prom�nn�ch v situaci, kdy je prom�nn�
register_globals
zapnuta nastaven�m v souboru php.ini (uv�domte si, �e
se p�edpokl�d� pou�it� n�zvu uploadovan�ho souboru 'userfile' tak,
jako v p��kladu v��e):
$userfile - Do�asn� n�zev souboru, pod kter�m byl
uploadovan� soubor ulo�en na server.
$userfile_name - Origin�ln� n�zev souboru nebo cesta
na odes�laj�c�m syst�mu.
$userfile_size - Velikost uploadovan�ho souboru
v bytech.
$userfile_type - MIME typ souboru, pokud prohl�e�
tuto informaci poskytuje (nap�. "image/gif").
Uv�domte si, �e prom�nn� " $userfile" ve skute�nosti
p�edstavuje n�zev pole <input> se specifikac� type="file" ve
formul��i. Pro v��e uveden� p��klad jsme zvolili n�zev "userfile".
Pozn�mka:
Nastaven� register_globals = On se nedoporu�uje z
bezpe�nostn�ch a v�konnostn�ch d�vod�.
Soubory se implicitn� ukl�daj� do syst�mov�ho adres��e pro do�asn�
soubory, pokud nebylo direktivou
upload_tmp_dir v souboru
php.ini stanoveno jinak. Syst�mov� adres�� pro
do�asn� soubory m��e b�t zm�n�n nastaven� prom�nn� prost�ed�
TMPDIR v prost�ed�, kde PHP b��. Nastaven� za pou�it�
putenv() z PHP skriptu nebude fungovat. Tato
prom�nn� prost�ed� m��e b�t tak� pou�ita k uji�t�n� se, �e v�echny
ostatn� operace pracuj� s uploadovan�mi soubory.
P��klad 20-2. Ov��ov�n� uploadu souboru
N�sleduj�c� p��klady jsou pro verze PHP 4 vy��� ne� PHP 4.0.2.
(viz funkce
is_uploaded_file() a
move_uploaded_file()).
<?php
if (is_uploaded_file($HTTP_POST_FILES['userfile']['tmp_name'])) {
copy($HTTP_POST_FILES['userfile']['tmp_name'], "/place/to/put/uploaded/file");
} else {
echo "Possible file upload attack. Filename: " . $HTTP_POST_FILES['userfile']['name'];
}
move_uploaded_file($HTTP_POST_FILES['userfile']['tmp_name'], "/place/to/put/uploaded/file");
?>
|
|
PHP skript, kter� p�ij�m� uploadovan� soubory, by m�l implementovat
ve�kerou logiku pro stanoven�, co by se m�lo ud�lat s uploadovan�m
souborem. M��ete nap�. pou��t prom�nnou
$HTTP_POST_FILES['userfile']['size'] pro zahozen�
soubor�, kter� jsou p��li� mal� nebo velk�. Mohli byste pou��t tak�
prom�nnou $HTTP_POST_FILES['userfile']['type']
pro filtraci soubor� podle MIME datov�ho typu. Bez ohledu na �e�en�,
soubor by m�l b�t smaz�n nebo p�esunut jinam.
Soubor bude automaticky smaz�n z do�asn�ho adres��e na konci skriptu,
pokud nebyl p�esunut jinam nebo p�ejmenov�n.
add a note
User Contributed Notes
Zpracov�n� uploadu soubor�
captlid
08-Nov-2004 06:37
mime_content_type() is better to use if you want to find if a file sent is really a jpeg or a plain text file. :)
dev at kiwicore dot org
13-Oct-2004 07:58
Becuase uploading seems to rarely work "out of the box" even if you follow the above instructions, I'll reiterate: the main reason people fail to have successful uploads, is because the directory that you are uploading to must have the right permissions, 0755 seems to work fine. I'd also like to submit my shot at an end all, be all upload capturing script.
Usage :
list($success,$response) = captureUpload('./user_images/',false,'_upload');
Will look inside $_FILES['upload'] for an uploaded file. The second parameter is the name of a callbaack function (false, if you don't want it), to rename the file, it will send two parameters:
$file (array of file info) , $destinationDirectory
It should return the name of the file. This is useful if you want to do a database lookup (for CMS integration, to see who uploaded it, etc), or do some other fancy checking.
The function will also rename file "whatever_copy_1.gif" if "whatever.gif" already exists.
And here is the code:
function captureUpload($destDir,$nameCallback = false,$fieldName = '_upload',$maxFileSize = false){
//make sure something is there
if(!isset($_FILES[$fieldName]) ||!isset($_FILES)||!is_array($_FILES[$fieldName]) ||!$_FILES[$fieldName]['name'])
return array(false,'No files were uploaded. Make sure your form tag\'s enctype was set to multipart/form-data and that the right field is being checked for the uploaded file.');
//normalize the file variable
$file = $_FILES[$fieldName];
if (!isset($file['type'])) $file['type'] = '';
if (!isset($file['size'])) $file['size'] = '';
if (!isset($file['tmp_name'])) $file['tmp_name'] = '';
$file['name'] = preg_replace(
'/[^a-zA-Z0-9\.\$\%\'\`\-\@\{\}\~\!\#\(\)\&\_\^]/'
,'',str_replace(array(' ','%20'),array('_','_'),$file['name']));
//was it to big?
if($maxFileSize && ($file['size'] > $maxFileSize))
return array(false,'The file uploaded was to large.');
//normalize destDir
if(strlen($destDir)>0 && $destDir[strlen($destDir)-1] != "/")
$destDir = $destDir.'/';
//should we change the filename via a callback?
if($nameCallback)
$file['name'] = call_user_func_array($nameCallback, array($file,$destDir));
$i = 0;
//if the filename already exists, append _copy_x (with extension)
if(strpos($file['name'],'.') !== false){
$bits = explode('.',$file['name']);
$ext = array_pop($bits);
while(file_exists($destDir.implode('.', $bits).($i?'_copy_'.$i:'').'.'.$ext)){
++$i;
$file['name'] = implode('.',$bits).($i?'_copy_'.$i:'').'.'.$ext;
}
//if the filename already exists, append _copy_x (no extension)
} else {
while(file_exists($destDir.$file['name'].($i ?'_copy_'.$i:''))){
++$i;
$file['name'] = $file['name'].($i?'_copy_'.$i:'');
}
}
//and now the big moment
if(!@copy($file['tmp_name'], $destDir.$file['name']))
return array(false,'Could not write the file "'.$file['name'].'" to: "'.$destDir.'". Permission denied.');
else
return array(true,$file['name']);
}
therhinoman at hotmail dot com
27-Aug-2004 08:20
If your upload script is meant only for uploading images, you can use the image function getimagesize() (does not require the GD image library) to make sure you're really getting an image and also filter image types.
<?php getimagesize($file); ?>
...will return false if the file is not an image or is not accessable, otherwise it will return an array...
<?php
$file = 'somefile.jpg';
$result_array = getimagesize($file);
if ($result_array !== false) {
$mime_type = $result_array['mime'];
switch($mime_type) {
case "image/jpeg":
echo "file is jpeg type";
break;
case "image/gif":
echo "file is gif type";
break;
default:
echo "file is an image, but not of gif or jpeg type";
}
} else {
echo "file is not a valid image file";
}
?>
using this function along with others mentioned on this page, image ploading can be made pretty much fool-proof.
See for supported image types and more info.
olijon, iceland
19-Jun-2004 03:24
When uploading large images, I got a "Document contains no data" error when using Netscape and an error page when using Explorer. My server setup is RH Linux 9, Apache 2 and PHP 4.3.
I found out that the following entry in the httpd.conf file was missing:
<Files *.php>
SetOutputFilter PHP
SetInputFilter PHP
LimitRequestBody 524288 (max size in bytes)
</Files>
When this had been added, everything worked smoothly.
- Oli Jon, Iceland
brion at pobox dot com
11-May-2004 01:08
Note that with magic_quotes_gpc on, the uploaded filename has backslashes added *but the tmp_name does not*. On Windows where the tmp_name path includes backslashes, you *must not* run stripslashes() on the tmp_name, so keep that in mind when de-magic_quotes-izing your input.
steve dot criddle at crd-sector dot com
16-Apr-2004 06:43
IE on the Mac is a bit troublesome. If you are uploading a file with an unknown file suffix, IE uploads the file with a mime type of "application/x-macbinary". The resulting file includes the resource fork wrapped around the file. Not terribly useful.
The following code assumes that the mime type is in $type, and that you have loaded the file's contents into $content. If the file is in MacBinary format, it delves into the resource fork header, gets the length of the data fork (bytes 83-86) and uses that to get rid of the resource fork.
(There is probably a better way to do it, but this solved my problem):
<?php
if ($type == 'application/x-macbinary') {
if (strlen($content) < 128) die('File too small');
$length = 0;
for ($i=83; $i<=86; $i++) {
$length = ($length * 256) + ord(substr($content,$i,1));
}
$content = substr($content,128,$length);
}
?>
hisham
02-Mar-2004 06:54
On a similar note to jim dot dam at sympatico dot ca 27-Feb-2002 09:13
Browsers intepret png upload type differently too eg.
print_r() output from Mozilla 1.6
Array ( [name] => eg1.png [type] => image/png [tmp_name] => /var/tmp/phpIJd4FL [error] => 0 [size] => 66614 )
print_r() output from IE 6
Array ( [name] => eg1.png [type] => image/x-png [tmp_name] => /var/tmp/phpHJ04Dh [error] => 0 [size] => 66614 )
Note the difference of image/png and image/x-png type intepretation of the same image file.
Further note:
~caetin~ ( at ) ~hotpop~ ( dot ) ~com~
11-Feb-2004 04:37
From the manual:
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.
As of PHP 4.2.0, the "none" is no longer a reliable determinant of no file uploaded. It's documented if you click on the "error codes" link, but you need to look at the $_FILES['your_file']['error']. If it's 4, then no file was selected.
maya_gomez ~ at ~ mail ~ dot ~ ru
06-Feb-2004 01:20
when you upload the file, $_FILES['file']['name'] contains its original name converted into server's default charset.
if a name contain characters that aren't present in default charset, the conversion fails and the $_FILES['file']['name'] remains in original charset.
i've got this behavior when uploading from a windows-1251 environment into koi8-r. if a filename has the number sign "�" (0xb9), it DOES NOT GET CONVERTED as soon as there is no such character in koi8-r.
Workaround i use:
<?php
if (strstr ($_FILES['file']['name'], chr(0xb9)) != "")
{
$_FILES['file']['name'] = iconv (
"windows-1251",
"koi8-r",
str_replace (chr(0xb9), "N.", $_FILES['file']['name']));
};
?>
srikanth at ideaworks3d dot com
19-Jan-2004 06:18
If the file is empty (0 bytes) it is treated as if no file
is uploaded. $_FILES['userfile']['tmp_name'] returns "none".
Shekhar Govindarajan
26-Oct-2003 01:38
To upload large files, besides setting upload_max_filesize, you must also set post_max_size in php.ini or using ini_set() function. Keep the value to more than the maximum expected size of the upload. This is because, you may be sending other post data along with the upload file. For example:
post_max_size = 601M
This should be a safe setting if you want to upload files of around 600 MB (as specified by upload_max_filesize = 600M)
While uploading large files, you should also increase the values for max_execution_time and max_input_time directives. Else your script will timeout or timeout before being able to parse the entire input/uploaded data.
therebechips
06-Sep-2003 09:02
Re: Handling uploads and downloads of large files and storing in MySQL.
Use two tables to store data about the file and the file data itself. ***Important: to preserve the integrity of the data use base64_encode() NOT addslashes().
<?php
define("MAX_SQL",50000);
$filehandle = fopen($tmp, "rb") or die( "Can't open file!" );
$query= "INSERT INTO files (name, type, size) VALUES(".
$DB->quote($name).", ".
$DB->quote($type).", ".
$DB->quote($size).
")";
$result = $DB->query($query);
$file_id = mysql_insert_id();
while (!feof ($filehandle)) {
$data = base64_encode(fread($filehandle,MAX_SQL));
$query = "INSERT INTO filedata (file_id, data) VALUES($file_id,\"".$data."\")";
$result = $DB->query($query);
}
fclose ($filehandle);
?>
Decode the data fragments and recombine them:
<?php
$file_id =$_GET ['file_id'];
$query ="select file_id, name, type, size from files where file_id='$file_id'";
$result = $DB->query($query);
$row= mysql_fetch_array ($result);
$type = $row ["type"];
$name = $row ["name"];
$size = $row ["size"];
$file_id = $row ["file_id"];
$query = "select id, data from filedata where file_id='$file_id' ORDER by id";
$result = $DB->query($query);
$data = "";
while ($row = mysql_fetch_array($result)) {
$data .= base64_decode($row ["data"]);
}
header ("Content-type: $type");
header ("Content-length: $size");
header ("Content-Disposition: attachment; filename=$name");
header ("Content-Description: PHP Generated Data");
echo $data;
?>
e4c5 at raditha dot com
12-Aug-2003 09:22
Progress bar support has been a recurring theme in many a PHP mailing list over the years. You can find a free progress monitor component for PHP file uploads at
The advantage of this system is that you do not have to apply any patches to PHP to make use of it.
user at php dot net
26-Jul-2003 07:16
To add an example to the bart at combodata dot nl's note:
When you're using this type of form:
<form name="" method="post" action="dostuff.php" enctype="multipart/form-data" >
<input type="file" name="dat[foto]" />
<input type="file" name="dat[banner]" />
<input type="file" name="dat[pdf]" />
</form>
You can refer to the uploaded files this way:
$_FILES["dat"]["tmp_name"]["foto"]
name: $_FILES["dat"]["name"]["foto"]
size: $_FILES["dat"]["tmp_name"]["foto"]
mitchy_AT_spacemonkeylabs_DOT_com
17-Jun-2003 04:29
After hours of profanity-laced tirades and futility mixed with panic, I have found the solution to my ills: HTTP_Upload, written by Thomas V.V. Cox, found at:
First, it is done in PEAR[1], and you all should use PEAR too. Second, it has many additions, like creating unix-friendly filenames from those wacky Windows users (filenames with spaces, yuck!)...
I cut-n-pasted from the example page, and it just plain worked. No muss, no fuss, no messy applicator brush!
[1]What is PEAR? It is a means for PHP developers to share and reuse code, usually reducing development costs and efforts by an order of magnitude. Even more important, someone else figured out that blasted library of functions that have had you stumped for days, and it is wrapped up in a neat, easy-to-use object. With a simple command-line installer, PEAR makes getting PHP-based add-ons and libraries as easy as apt-get! It is found at
diegoful at yahoo dot com
25-Mar-2003 08:22
SECURITY CONSIDERATION: If you are saving all uploaded files to a directory accesible with an URL, remember to filter files not only by mime-type (e.g. image/gif), but also by extension. The mime-type is reported by the client, if you trust him, he can upload a php file as an image and then request it, executing malicious code.
I hope I am not giving hackers a good idea anymore than I am giving it to good-intended developers. Cheers.
garyds at miraclemedia dot ca
16-Mar-2003 02:12
As it has been mentioned above, Windows-based servers have trouble with the path to move the uploaded file to when using move_uploaded_file()... this may also be the reason copy() works and not move_uploaded_file(), but of course move_uploaded_file() is a much better method to use. The solution in the aforementioned note said you must use "\\" in the path, but I found "/" works as well. So to get a working path, I used something to the effect of:
"g:/rootdir/default/www/".$_FILES['userfile']['name']
...which worked like a charm.
I am using PHP 4.3.0 on a win2k server.
Hope this helps!
ov at xs4all dot nl
09-Mar-2003 03:08
This took me a few days to find out: when uploading large files with a slow connection to my WIN2K/IIS5/PHP4 server the POST form kept timing out at exactly 5 minutes. All PHP.INI settings were large enough to accomodate huge file uploads. Searched like hell with keywords like "file upload php timeout script" until I realised that I installed PHP as CGI and added that as a keyword. This was the solution:
To set the timeout value:
1. In the Internet Information Services snap-in, select the computer icon and open its property sheets.
2. Under Master Properties, select WWW Service, and then click the Edit button
3. Click the Home Directory tab.
4. Click the Configuration button.
5. Click the Process Options tab, and then type the timeout period in the CGI Script Timeout box.
mccorkle+php at devteam dot org
08-Jan-2003 07:59
To anyone that is trying to use values="foo" to set a default value in a input type of ``file'', I found this out from
* Internet Explorer, Netscape and Opera do not use the VALUE attribute as the default contents of the input area. Any default value set via HTML is not usable via scripting and the DOM as well (hence it is not listed as 'supported' in any of the browsers.) If a user enters text in the field however, that value is then reachable via the DOM as it normally would be for a normal INPUT field (via the .value property.) The reason for this behavior would presumably be to ensure the security/safety of users against malicious authors.
Tooke me a bit to find this, so I figured I'd share.
panayotis at yellownetroad dot com
18-Dec-2002 11:21
In order to enable $HTTP_RAW_POST_DATA, you can set always_populate_raw_post_data to On either in php.ini or .htaccess
travis dot lewis at amd dot com
04-Dec-2002 08:58
If you we dumb like me you installed Redhat 8.0 and kept the default install of packages for Apache 2.0 and PHP4.2.2. I could not upload any files larger than 512kB and all the php directorives were set to 32MB or higher.
memory_limit = 128M
post_max_size = 64M
upload_max_filesize = 32M
And my upload web page was set to 32MB as well:
<Form ID="frmAttach" Name="frmAttach" enctype="multipart/form-data" action="attachments.php" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="33554432" />
However, the insiduous php.conf (/etc/httpd/conf.d/php.conf) file used by default RPM install of Redhat httpd has a LimitRequestBody set to 512kB ("524288" ). Adjusting this to 32MB ("33554432") got things going for the larger files. Here is my php.conf file in its entirety. Hope this helps someone. L8er.
#
# PHP is an HTML-embedded scripting language which attempts to make it
# easy for developers to write dynamically generated webpages.
#
LoadModule php4_module modules/libphp4.so
#
# Cause the PHP interpreter handle files with a .php extension.
#
<Files *.php>
SetOutputFilter PHP
SetInputFilter PHP
LimitRequestBody 33554432
</Files>
#
# Add index.php to the list of files that will be served as directory
# indexes.
#
solja at gci dot net
04-May-2002 01:11
Just another way I found to keep an uploaded file from overwriting an already exisiting one - I prefix each uploaded file with time() timestamp. Something like:
$unique_id = time();
Then when I give the new name to move the file to, I use something like:
$unique_id."-".$filename
So I get a fairly unique filename each time a file is uploaded. Obviously, if two files are uploaded at once, both will have the same timestamp, but I don't worry too much about that. Hope this might help someone.
am at netactor dot NO_SPAN dot com
15-Mar-2002 06:20
Your binary files may be uploaded incorrectly if you use modules what recode characters. For example, for Russian Apache, you should use
<Files ScriptThatReceivesUploads.php>
CharsetDisable On
</Files>
| |