mirror of
https://github.com/sigmasternchen/php-doc-en
synced 2025-03-15 16:38:54 +00:00
573 lines
20 KiB
XML
573 lines
20 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<!-- $Revision$ -->
|
|
<chapter xml:id="features.file-upload" xmlns="http://docbook.org/ns/docbook">
|
|
<title>Handling file uploads</title>
|
|
|
|
<sect1 xml:id="features.file-upload.post-method">
|
|
<title>POST method uploads</title>
|
|
<simpara>
|
|
This feature lets people upload both text and binary files.
|
|
With PHP's authentication and file manipulation functions,
|
|
you have full control over who is allowed to upload and
|
|
what is to be done with the file once it has been uploaded.
|
|
</simpara>
|
|
<simpara>
|
|
PHP is capable of receiving file uploads from any RFC-1867
|
|
compliant browser.
|
|
</simpara>
|
|
|
|
<note>
|
|
<title>Related Configurations Note</title>
|
|
<para>
|
|
See also the <link linkend="ini.file-uploads">file_uploads</link>,
|
|
<link linkend="ini.upload-max-filesize">upload_max_filesize</link>,
|
|
<link linkend="ini.upload-tmp-dir">upload_tmp_dir</link>,
|
|
<link linkend="ini.post-max-size">post_max_size</link> and
|
|
<link linkend="ini.max-input-time">max_input_time</link> directives
|
|
in &php.ini;
|
|
</para>
|
|
</note>
|
|
|
|
<para>
|
|
PHP also supports PUT-method file uploads as used by
|
|
<productname>Netscape Composer</productname> and W3C's
|
|
<productname>Amaya</productname> clients. See the <link
|
|
linkend="features.file-upload.put-method">PUT Method
|
|
Support</link> for more details.
|
|
</para>
|
|
|
|
<para>
|
|
<example>
|
|
<title>File Upload Form</title>
|
|
<para>
|
|
A file upload screen can be built by creating a special form which
|
|
looks something like this:
|
|
</para>
|
|
<programlisting role="html">
|
|
<![CDATA[
|
|
<!-- The data encoding type, enctype, MUST be specified as below -->
|
|
<form enctype="multipart/form-data" action="__URL__" method="POST">
|
|
<!-- MAX_FILE_SIZE must precede the file input field -->
|
|
<input type="hidden" name="MAX_FILE_SIZE" value="30000" />
|
|
<!-- Name of input element determines name in $_FILES array -->
|
|
Send this file: <input name="userfile" type="file" />
|
|
<input type="submit" value="Send File" />
|
|
</form>
|
|
]]>
|
|
</programlisting>
|
|
<para>
|
|
The <literal>__URL__</literal> in the above example should be replaced,
|
|
and point to a PHP file.
|
|
</para>
|
|
<para>
|
|
The <literal>MAX_FILE_SIZE</literal> hidden field (measured in bytes) must
|
|
precede the file input field, and its value is the maximum filesize accepted by PHP.
|
|
This form element should always be used as it saves users the trouble of
|
|
waiting for a big file being transferred only to find that it was too
|
|
large and the transfer failed. Keep in mind: fooling this setting on the
|
|
browser side is quite easy, so never rely on files with a greater size
|
|
being blocked by this feature. It is merely a convenience feature for
|
|
users on the client side of the application. The PHP settings (on the server
|
|
side) for maximum-size, however, cannot be fooled.
|
|
</para>
|
|
</example>
|
|
</para>
|
|
|
|
<note>
|
|
<para>
|
|
Be sure your file upload form has attribute <literal>enctype="multipart/form-data"</literal>
|
|
otherwise the file upload will not work.
|
|
</para>
|
|
</note>
|
|
|
|
<para>
|
|
The global <varname>$_FILES</varname> will contain all the uploaded file information.
|
|
Its contents from the example form is as follows. Note that this assumes the use of
|
|
the file upload name <emphasis>userfile</emphasis>, as used in the example
|
|
script above. This can be any name.
|
|
<variablelist>
|
|
<varlistentry>
|
|
<term><varname>$_FILES['userfile']['name']</varname></term>
|
|
<listitem>
|
|
<para>
|
|
The original name of the file on the client machine.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term><varname>$_FILES['userfile']['type']</varname></term>
|
|
<listitem>
|
|
<para>
|
|
The mime type of the file, if the browser provided this
|
|
information. An example would be
|
|
<literal>"image/gif"</literal>. This mime type is however
|
|
not checked on the PHP side and therefore don't take its value
|
|
for granted.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term><varname>$_FILES['userfile']['size']</varname></term>
|
|
<listitem>
|
|
<para>
|
|
The size, in bytes, of the uploaded file.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term><varname>$_FILES['userfile']['tmp_name']</varname></term>
|
|
<listitem>
|
|
<para>
|
|
The temporary filename of the file in which the uploaded file
|
|
was stored on the server.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term><varname>$_FILES['userfile']['error']</varname></term>
|
|
<listitem>
|
|
<para>
|
|
The <link linkend="features.file-upload.errors">error code</link>
|
|
associated with this file upload.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
</variablelist>
|
|
</para>
|
|
|
|
<para>
|
|
Files will, by default be stored in the server's default temporary
|
|
directory, unless another location has been given with the <link
|
|
linkend="ini.upload-tmp-dir">upload_tmp_dir</link> directive in
|
|
&php.ini;. The server's default directory can
|
|
be changed by setting the environment variable
|
|
<envar>TMPDIR</envar> in the environment in which PHP runs.
|
|
Setting it using <function>putenv</function> from within a PHP
|
|
script will not work. This environment variable can also be used
|
|
to make sure that other operations are working on uploaded files,
|
|
as well.
|
|
<example>
|
|
<title>Validating file uploads</title>
|
|
<para>
|
|
See also the function entries for <function>is_uploaded_file</function>
|
|
and <function>move_uploaded_file</function> for further information. The
|
|
following example will process the file upload that came from a form.
|
|
</para>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
$uploaddir = '/var/www/uploads/';
|
|
$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>";
|
|
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
<simpara>
|
|
The PHP script which receives the uploaded file should implement
|
|
whatever logic is necessary for determining what should be done
|
|
with the uploaded file. You can, for example, use the
|
|
<varname>$_FILES['userfile']['size']</varname> variable
|
|
to throw away any files that are either too small or too big. You
|
|
could use the
|
|
<varname>$_FILES['userfile']['type']</varname> variable
|
|
to throw away any files that didn't match a certain type criteria, but
|
|
use this only as first of a series of checks, because this value
|
|
is completely under the control of the client and not checked on the PHP
|
|
side.
|
|
Also, you could use <varname>$_FILES['userfile']['error']</varname>
|
|
and plan your logic according to the <link
|
|
linkend="features.file-upload.errors">error codes</link>.
|
|
Whatever the logic, you should either delete the file from the
|
|
temporary directory or move it elsewhere.
|
|
</simpara>
|
|
<simpara>
|
|
If no file is selected for upload in your form, PHP will return
|
|
<varname>$_FILES['userfile']['size']</varname> as 0, and
|
|
<varname>$_FILES['userfile']['tmp_name']</varname> as none.
|
|
</simpara>
|
|
<simpara>
|
|
The file will be deleted from the temporary directory at the end
|
|
of the request if it has not been moved away or renamed.
|
|
</simpara>
|
|
<example>
|
|
<title>Uploading array of files</title>
|
|
<para>
|
|
PHP supports <link linkend="faq.html.arrays">HTML array feature</link>
|
|
even with files.
|
|
</para>
|
|
<programlisting role="html">
|
|
<![CDATA[
|
|
<form action="" method="post" enctype="multipart/form-data">
|
|
<p>Pictures:
|
|
<input type="file" name="pictures[]" />
|
|
<input type="file" name="pictures[]" />
|
|
<input type="file" name="pictures[]" />
|
|
<input type="submit" value="Send" />
|
|
</p>
|
|
</form>
|
|
]]>
|
|
</programlisting>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
foreach ($_FILES["pictures"]["error"] as $key => $error) {
|
|
if ($error == UPLOAD_ERR_OK) {
|
|
$tmp_name = $_FILES["pictures"]["tmp_name"][$key];
|
|
// basename() may prevent filesystem traversal attacks;
|
|
// further validation/sanitation of the filename may be appropriate
|
|
$name = basename($_FILES["pictures"]["name"][$key]);
|
|
move_uploaded_file($tmp_name, "data/$name");
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
<para>
|
|
File upload progress bar can be implemented using <link
|
|
linkend="session.upload-progress">Session Upload Progress</link>.
|
|
</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="features.file-upload.errors">
|
|
<title>Error Messages Explained</title>
|
|
<simpara>
|
|
PHP returns an appropriate error code along with the
|
|
file array. The error code can be found in the
|
|
<literal>error</literal> segment of the file array that is created
|
|
during the file upload by PHP. In other words, the error might be
|
|
found in <varname>$_FILES['userfile']['error']</varname>.
|
|
</simpara>
|
|
<para>
|
|
<variablelist>
|
|
<varlistentry>
|
|
<term><constant>UPLOAD_ERR_OK</constant></term>
|
|
<listitem>
|
|
<para>
|
|
Value: 0; There is no error, the file uploaded with success.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term><constant>UPLOAD_ERR_INI_SIZE</constant></term>
|
|
<listitem>
|
|
<para>
|
|
Value: 1; The uploaded file exceeds the
|
|
<link linkend="ini.upload-max-filesize">upload_max_filesize</link>
|
|
directive in &php.ini;.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term><constant>UPLOAD_ERR_FORM_SIZE</constant></term>
|
|
<listitem>
|
|
<para>
|
|
Value: 2; The uploaded file exceeds the <emphasis>MAX_FILE_SIZE</emphasis>
|
|
directive that was specified in the HTML form.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term><constant>UPLOAD_ERR_PARTIAL</constant></term>
|
|
<listitem>
|
|
<para>
|
|
Value: 3; The uploaded file was only partially uploaded.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term><constant>UPLOAD_ERR_NO_FILE</constant></term>
|
|
<listitem>
|
|
<para>
|
|
Value: 4; No file was uploaded.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term><constant>UPLOAD_ERR_NO_TMP_DIR</constant></term>
|
|
<listitem>
|
|
<para>
|
|
Value: 6; Missing a temporary folder.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term><constant>UPLOAD_ERR_CANT_WRITE</constant></term>
|
|
<listitem>
|
|
<para>
|
|
Value: 7; Failed to write file to disk.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term><constant>UPLOAD_ERR_EXTENSION</constant></term>
|
|
<listitem>
|
|
<para>
|
|
Value: 8; A PHP extension stopped the file upload. PHP does not
|
|
provide a way to ascertain which extension caused the file upload to
|
|
stop; examining the list of loaded extensions with <function>phpinfo</function> may help.
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
</variablelist>
|
|
</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="features.file-upload.common-pitfalls">
|
|
<title>Common Pitfalls</title>
|
|
<simpara>
|
|
The <literal>MAX_FILE_SIZE</literal> item cannot specify a file size
|
|
greater than the file size that has been set in the <link
|
|
linkend="ini.upload-max-filesize">upload_max_filesize</link> in
|
|
the &php.ini; file. The default is 2 megabytes.
|
|
</simpara>
|
|
<simpara>
|
|
If a memory limit is enabled, a larger <link
|
|
linkend="ini.memory-limit">memory_limit</link> may be needed. Make
|
|
sure you set <link linkend="ini.memory-limit">memory_limit</link>
|
|
large enough.
|
|
</simpara>
|
|
<simpara>
|
|
If <link linkend="ini.max-execution-time">max_execution_time</link>
|
|
is set too small, script execution may be exceeded by the value. Make
|
|
sure you set <literal>max_execution_time</literal> large enough.
|
|
</simpara>
|
|
<note>
|
|
<simpara>
|
|
<link linkend="ini.max-execution-time">max_execution_time</link> only
|
|
affects the execution time of the script itself. Any time spent
|
|
on activity that happens outside the execution of the script
|
|
such as system calls using <function>system</function>, the
|
|
<function>sleep</function> function, database queries, time taken by
|
|
the file upload process, etc. is not included when determining the maximum
|
|
time that the script has been running.
|
|
</simpara>
|
|
</note>
|
|
<warning>
|
|
<simpara>
|
|
<link linkend="ini.max-input-time">max_input_time</link> sets the maximum
|
|
time, in seconds, the script is allowed to receive input; this includes
|
|
file uploads. For large or multiple files, or users on slower connections,
|
|
the default of <literal>60 seconds</literal> may be exceeded.
|
|
</simpara>
|
|
</warning>
|
|
<simpara>
|
|
If <link linkend="ini.post-max-size">post_max_size</link> is set too
|
|
small, large files cannot be uploaded. Make sure you set
|
|
<literal>post_max_size</literal> large enough.
|
|
</simpara>
|
|
<simpara>
|
|
The
|
|
<link linkend="ini.max-file-uploads">max_file_uploads</link> configuration
|
|
setting controls the maximum number of files that can uploaded in one
|
|
request. If more files are uploaded than the limit, then
|
|
<varname>$_FILES</varname> will stop processing files once the limit is
|
|
reached. For example, if
|
|
<link linkend="ini.max-file-uploads">max_file_uploads</link> is set to
|
|
<literal>10</literal>, then <varname>$_FILES</varname> will never contain
|
|
more than 10 items.
|
|
</simpara>
|
|
<simpara>
|
|
Not validating which file you operate on may mean that users can access
|
|
sensitive information in other directories.
|
|
</simpara>
|
|
<simpara>
|
|
Please note that the <productname>CERN httpd</productname> seems to strip off everything
|
|
starting at the first whitespace in the content-type mime header
|
|
it gets from the client. As long as this is the case, <productname>CERN httpd</productname>
|
|
will not support the file upload feature.
|
|
</simpara>
|
|
<simpara>
|
|
Due to the large amount of directory listing styles we cannot guarantee
|
|
that files with exotic names (like containing spaces) are handled properly.
|
|
</simpara>
|
|
<simpara>
|
|
A developer may not mix normal <literal>input</literal> fields and file upload fields in the same
|
|
form variable (by using an <literal>input</literal> name like <literal>foo[]</literal>).
|
|
</simpara>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="features.file-upload.multiple">
|
|
<title>Uploading multiple files</title>
|
|
<simpara>
|
|
Multiple files can be uploaded using different
|
|
<literal>name</literal> for <literal>input</literal>.
|
|
</simpara>
|
|
<simpara>
|
|
It is also possible to upload multiple files simultaneously and
|
|
have the information organized automatically in arrays for you. To
|
|
do so, you need to use the same array submission syntax in the
|
|
HTML form as you do with multiple selects and checkboxes:
|
|
</simpara>
|
|
<para>
|
|
<example>
|
|
<title>Uploading multiple files</title>
|
|
<programlisting role="html">
|
|
<![CDATA[
|
|
<form action="file-upload.php" method="post" enctype="multipart/form-data">
|
|
Send these files:<br />
|
|
<input name="userfile[]" type="file" /><br />
|
|
<input name="userfile[]" type="file" /><br />
|
|
<input type="submit" value="Send files" />
|
|
</form>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
<simpara>
|
|
When the above form is submitted, the arrays
|
|
<varname>$_FILES['userfile']</varname>,
|
|
<varname>$_FILES['userfile']['name']</varname>, and
|
|
<varname>$_FILES['userfile']['size']</varname> will be
|
|
initialized.
|
|
</simpara>
|
|
<simpara>
|
|
For instance, assume that the filenames
|
|
<filename>/home/test/review.html</filename> and
|
|
<filename>/home/test/xwp.out</filename> are submitted. In this
|
|
case, <varname>$_FILES['userfile']['name'][0]</varname>
|
|
would contain the value <filename>review.html</filename>, and
|
|
<varname>$_FILES['userfile']['name'][1]</varname> would
|
|
contain the value <filename>xwp.out</filename>. Similarly,
|
|
<varname>$_FILES['userfile']['size'][0]</varname> would
|
|
contain <filename>review.html</filename>'s file size, and so forth.
|
|
</simpara>
|
|
<simpara>
|
|
<varname>$_FILES['userfile']['name'][0]</varname>,
|
|
<varname>$_FILES['userfile']['tmp_name'][0]</varname>,
|
|
<varname>$_FILES['userfile']['size'][0]</varname>, and
|
|
<varname>$_FILES['userfile']['type'][0]</varname> are
|
|
also set.
|
|
</simpara>
|
|
<warning>
|
|
<simpara>
|
|
The
|
|
<link linkend="ini.max-file-uploads">max_file_uploads</link>
|
|
configuration setting acts as a limit on the number of files that can be
|
|
uploaded in one request. You will need to ensure that your form does not
|
|
try to upload more files in one request than this limit.
|
|
</simpara>
|
|
</warning>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="features.file-upload.put-method">
|
|
<title>PUT method support</title>
|
|
<para>
|
|
PHP provides support for the HTTP PUT method used by some clients to store
|
|
files on a server.
|
|
PUT requests are much simpler than a file upload using POST requests
|
|
and they look something like this:
|
|
<informalexample>
|
|
<programlisting role="HTTP">
|
|
<![CDATA[
|
|
PUT /path/filename.html HTTP/1.1
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
</para>
|
|
<para>
|
|
This would normally mean that the remote client would like to save
|
|
the content that follows as: <filename>/path/filename.html</filename> in your web tree.
|
|
It is obviously not a good idea for Apache or PHP to automatically
|
|
let everybody overwrite any files in your web tree. So, to handle
|
|
such a request you have to first tell your web server that you
|
|
want a certain PHP script to handle the request. In Apache you do
|
|
this with the <emphasis>Script</emphasis> directive. It can be
|
|
placed almost anywhere in your Apache configuration file. A
|
|
common place is inside a <literal><Directory></literal> block or perhaps inside
|
|
a <literal><VirtualHost></literal> block. A line like this would do the trick:
|
|
<informalexample>
|
|
<programlisting>
|
|
<![CDATA[
|
|
Script PUT /put.php
|
|
]]>
|
|
</programlisting>
|
|
</informalexample>
|
|
</para>
|
|
<simpara>
|
|
This tells Apache to send all PUT requests for URIs that match the
|
|
context in which you put this line to the <filename>put.php</filename> script. This
|
|
assumes, of course, that you have PHP enabled for the <filename>.php</filename>
|
|
extension and PHP is active. The destination resource for all PUT
|
|
requests to this script has to be the script itself, not a filename the
|
|
uploaded file should have.
|
|
</simpara>
|
|
<simpara>
|
|
With PHP you would then do something like the following in
|
|
your put.php. This would copy the contents of the uploaded file to the
|
|
file <filename>myputfile.ext</filename> on the server.
|
|
You would probably want to perform some checks and/or
|
|
authenticate the user before performing this file copy.
|
|
</simpara>
|
|
<para>
|
|
<example>
|
|
<title>Saving HTTP PUT files</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
/* PUT data comes in on the stdin stream */
|
|
$putdata = fopen("php://input", "r");
|
|
|
|
/* Open a file for writing */
|
|
$fp = fopen("myputfile.ext", "w");
|
|
|
|
/* Read the data 1 KB at a time
|
|
and write to the file */
|
|
while ($data = fread($putdata, 1024))
|
|
fwrite($fp, $data);
|
|
|
|
/* Close the streams */
|
|
fclose($fp);
|
|
fclose($putdata);
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="features.file-upload.errors.seealso">
|
|
&reftitle.seealso;
|
|
<para>
|
|
<simplelist>
|
|
<member><link linkend="security.filesystem">Filesystem Security</link></member>
|
|
</simplelist>
|
|
</para>
|
|
</sect1>
|
|
|
|
</chapter>
|
|
|
|
<!-- Keep this comment at the end of the file
|
|
Local variables:
|
|
mode: sgml
|
|
sgml-omittag:t
|
|
sgml-shorttag:t
|
|
sgml-minimize-attributes:nil
|
|
sgml-always-quote-attributes:t
|
|
sgml-indent-step:1
|
|
sgml-indent-data:t
|
|
indent-tabs-mode:nil
|
|
sgml-parent-document:nil
|
|
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
|
|
sgml-exposed-tags:nil
|
|
sgml-local-catalogs:nil
|
|
sgml-local-ecat-files:nil
|
|
End:
|
|
vim600: syn=xml fen fdm=syntax fdl=2 si
|
|
vim: et tw=78 syn=sgml
|
|
vi: ts=1 sw=1
|
|
-->
|