Files
archived-doc-pt-br/features/file-upload.xml
2025-08-29 20:19:29 -03:00

545 lines
21 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!-- EN-Revision: 3944dc63330edde959cfd3e7d113c999cbec10ff Maintainer: ae Status: ready --><!-- CREDITS: Davi Crystal,royopa,diegopires,geekcom,adiel,lhsazevedo,gsrbr,ae,leonardolara -->
<chapter xml:id="features.file-upload" xmlns="http://docbook.org/ns/docbook">
<title>Gerenciamento de envio de arquivos</title>
<sect1 xml:id="features.file-upload.post-method">
<title>Envio de arquivos com o método POST</title>
<simpara>
Este recurso permite realizar envios de arquivos de texto e binários.
Com as funções de autenticação e manipulação de arquivos do PHP,
você tem o controle completo de quem pode fazer o envio e
o que deve ser feito com o arquivo após o envio estar completo.
</simpara>
<simpara>
O PHP é capaz de receber envios de arquivos de qualquer navegador
compatível com RFC-1867.
</simpara>
<note>
<title>Nota Sobre Configurações Relacionadas</title>
<para>
Veja também as diretivas <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>,
<link linkend="ini.max-input-time">max_input_time</link>
no &php.ini;
</para>
</note>
<para>
O PHP também suporta o método PUT para envio de arquivos como o usado pelo
<productname>Netscape Composer</productname> e clientes para
<productname>Amaya</productname> W3C. Veja <link
linkend="features.file-upload.put-method">Suporte ao Método PUT
</link> para maiores detalhes.
</para>
<para>
<example>
<title>Formulário para Envio de Arquivo</title>
<para>
Uma interface para envio de arquivo pode ser criada com um formulário especial
parecido com este:
</para>
<programlisting role="html">
<![CDATA[
<!-- O tipo de codificação de dados, enctype, DEVE ser especificado abaixo -->
<form enctype="multipart/form-data" action="__URL__" method="POST">
<!-- MAX_FILE_SIZE deve preceder o campo input -->
<input type="hidden" name="MAX_FILE_SIZE" value="30000" />
<!-- O nome do elemento input determina o nome do array $_FILES -->
Enviar esse arquivo: <input name="userfile" type="file" />
<input type="submit" value="Enviar arquivo" />
</form>
]]>
</programlisting>
<para>
O parâmetro <literal>__URL__</literal> no exemplo acima deve ser substituído
e apontar para um arquivo PHP.
</para>
<para>
O campo escondido (input type="hidden") <literal>MAX_FILE_SIZE</literal> (medido em bytes) deve
preceder o campo de entrada do arquivo (input type="file"), e o seu valor é o tamanho limite aceito pelo PHP.
Este elemento do formulário deve sempre ser usado para evitar que usuários tenham o problema de
ter que esperar pela transferência de um arquivo para só então descobrir que ele era grande
demais e a transferência falhar. Fique ciente: é muito simples burlar esse parâmetro pelo
lado do navegador, portanto nunca dependa exclusivamente desse recurso para
bloquear arquivos com maior tamanho. Isso é um recurso meramente conveniente para
usuários da aplicação no lado do cliente. Os parâmetros do PHP (no servidor)
para "maximum-size", no entanto, não podem ser burlados.
</para>
</example>
</para>
<note>
<para>
Tenha certeza que seu formulário de envio tenha o atributo <literal>enctype="multipart/form-data"</literal>
do contrário o envio não irá funcionar.
</para>
</note>
<para>
A variável global <varname>$_FILES</varname> conterá toda a informação do arquivo enviado.
O conteúdo do formulário de exemplo acima será como o exemplo descrito abaixo. Note que isso assume o
nome do arquivo enviado <emphasis>userfile</emphasis>, como foi usado no script de exemplo
acima, contudo isso pode ter qualquer nome.
<variablelist>
<varlistentry>
<term><varname>$_FILES['userfile']['name']</varname></term>
<listitem>
<para>
O nome original do arquivo na máquina do cliente.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$_FILES['userfile']['type']</varname></term>
<listitem>
<para>
O tipo mime do arquivo, se o navegador fornecer essa
informação. Um exemplo poderia ser
<literal>"image/gif"</literal>. O tipo mime
não é verificado pelo PHP portanto não considere que esse valor
será concedido.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$_FILES['userfile']['size']</varname></term>
<listitem>
<para>
O tamanho, em bytes, do arquivo enviado.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$_FILES['userfile']['tmp_name']</varname></term>
<listitem>
<para>
O nome temporário com o qual o arquivo enviado
foi armazenado no servidor.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$_FILES['userfile']['error']</varname></term>
<listitem>
<para>
O <link linkend="features.file-upload.errors">código de erro</link>
associado a esse envio de arquivo.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$_FILES['userfile']['full_path']</varname></term>
<listitem>
<para>
O caminho completo conforme enviado pelo navegador. Esse valor nem sempre contém uma estrutura de diretórios real, e não se pode confiar nele.
Disponível a partir do PHP 8.1.0.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
Os arquivos serão guardados por padrão no diretório temporário do
servidor, a menos que outro local seja especificado através da diretiva
<link linkend="ini.upload-tmp-dir">upload_tmp_dir</link> no
&php.ini;. O diretório padrão do servidor pode
ser alterado através da configuração da variável de ambiente
<envar>TMPDIR</envar> no ambiente onde o PHP está sendo executado.
Configurando isso usando <function>putenv</function> em um script PHP
não irá funcionar. Esta variável de ambiente também pode ser usada
para certificar que outras operações também estão funcionando nos arquivos
enviados.
<example>
<title>Validando o envio de arquivos</title>
<para>
Veja também as funções <function>is_uploaded_file</function>
e <function>move_uploaded_file</function> para maiores informações. O
exemplo a seguir irá processar o envio de um arquivo que vem de um formulário.
</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 "Arquivo válido e enviado com sucesso.\n";
} else {
echo "Possível ataque de upload de arquivo!\n";
}
echo 'Aqui está mais informações de debug:';
print_r($_FILES);
print "</pre>";
?>
]]>
</programlisting>
</example>
</para>
<simpara>
O script PHP que recebe o arquivo enviado deve implementar
qualquer lógica que for necessária para determinar o que deve ser feito
com o arquivo enviado. Você pode, por exemplo, usar a
variável <varname>$_FILES['userfile']['size']</varname>
para descartar qualquer arquivo que seja muito pequeno ou muito grande. Você
pode usar a
variável <varname>$_FILES['userfile']['type']</varname>
para descartar arquivos incompatíveis com um determinado critério, mas
use isso apenas como a primeira de uma série de verificações, porque esse valor
está totalmente sob o controle do cliente e não é verificado pelo
PHP.
Você também pode usar <varname>$_FILES['userfile']['error']</varname>
e planejar sua lógica de acordo com os <link
linkend="features.file-upload.errors">Códigos de erro</link>.
Independente da lógica, você também deve apagar o arquivo do
diretório temporário ou movê-lo para outro local.
</simpara>
<simpara>
Se nenhum arquivo for selecionado em seu formulário, o PHP irá retornar
<varname>$_FILES['userfile']['size']</varname> como 0, e
<varname>$_FILES['userfile']['tmp_name']</varname> como "none" (nenhum).
</simpara>
<simpara>
O arquivo será excluído do diretório temporário ao fim da requisição
se ele não for movido ou renomeado.
</simpara>
<example>
<title>Enviando um array de arquivos</title>
<para>
O PHP suporta <link linkend="faq.html.arrays">array HTML</link>
mesmo com arquivos.
</para>
<programlisting role="html">
<![CDATA[
<form action="" method="post" enctype="multipart/form-data">
<p>Imagens:
<input type="file" name="pictures[]" />
<input type="file" name="pictures[]" />
<input type="file" name="pictures[]" />
<input type="submit" value="Enviar" />
</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() pode prevenir ataques de percorrer o sistema de arquivos,
// mas mais validações são necessárias
$name = basename($_FILES["pictures"]["name"][$key]);
move_uploaded_file($tmp_name, "data/$name");
}
}
?>
]]>
</programlisting>
</example>
<para>
O progresso do envio de arquivos pode ser implementado usando a <link
linkend="session.upload-progress">Sessão de Progresso de Envio</link>.
</para>
</sect1>
<sect1 xml:id="features.file-upload.errors">
<title>Explicando mensagens de erro</title>
<simpara>
O PHP retorna um código de erro apropriado na array do arquivo.
O código de erro pode ser encontrado no segmento
<literal>error</literal> da array que é criada pelo PHP durante o envio do arquivo.
Em outras palavras, o erro pode ser encontrado em
<varname>$_FILES['userfile']['error']</varname>.
</simpara>
<simpara>
O valor desse código de erro é uma das
constantes
<constant>UPLOAD_ERR_<replaceable>*</replaceable></constant>.
</simpara>
</sect1>
<sect1 xml:id="features.file-upload.common-pitfalls">
<title>Problemas Comuns</title>
<simpara>
O item <literal>MAX_FILE_SIZE</literal> não pode assumir um tamanho de arquivo
maior que o tamanho de arquivo configurado em <link
linkend="ini.upload-max-filesize">upload_max_filesize</link> no arquivo &php.ini;.
O valor padrão é de 2 megabytes.
</simpara>
<simpara>
Se um limite de memória está ativo, um valor maior para o limite de memória no parâmetro <link
linkend="ini.memory-limit">memory_limit</link> pode ser necessário. Tenha certeza de
estabelecer um limite de memória grande o suficiente através do parâmetro
<link linkend="ini.memory-limit">memory_limit</link>.
</simpara>
<simpara>
Se o tempo máximo de execução definido através do parâmetro <link linkend="ini.max-execution-time">max_execution_time</link>
for muito pequeno, a execução do script pode ultrapassar o limite de tempo. Tenha
certeza de estabelecer um tempo máximo de execução grande o suficiente através do parâmetro <literal>max_execution_time</literal>.
</simpara>
<note>
<simpara>
O parâmetro <link linkend="ini.max-execution-time">max_execution_time</link> somente
afeta o tempo de execução do script em si. Qualquer tempo gasto com
atividades que aconteçam fora da execução do script
como chamadas de sistema usando <function>system</function>, a função
<function>sleep</function>, pesquisas em banco de dados, tempo gasto pelo
processo de enviar um arquivo, etc. não são considerados no momento de determinar o limite de tempo
máximo de um script em execução.
</simpara>
</note>
<warning>
<simpara>
O parâmetro <link linkend="ini.max-input-time">max_input_time</link> define o tempo máximo,
em segundos, que é permitido ao script a receber entradas, isto inclui
envio de arquivos. Para um arquivo grande, múltiplos arquivos ou usuários com conexões lentas,
o padrão de <literal>60</literal> segundos pode ser ultrapassado.
</simpara>
</warning>
<simpara>
Se o parâmetro <link linkend="ini.post-max-size">post_max_size</link> for muito pequeno,
arquivos grandes podem não ser carregados. Tenha certeza de definir o parâmetro
<literal>post_max_size</literal> grande o suficiente.
</simpara>
<simpara>
O
parâmetro <link linkend="ini.max-file-uploads">max_file_uploads</link>
controla o número máximo de arquivos que podem ser enviados em uma única
requisição. Caso sejam enviados mais arquivos que o definido neste limite, então
<varname>$_FILES</varname> interrompe o processamento de arquivos uma vez que o limite
tenha sido atingido. Por exemplo, se o parâmetro
<link linkend="ini.max-file-uploads">max_file_uploads</link> está definido como
<literal>10</literal>, então <varname>$_FILES</varname> nunca possuirá
mais de 10 itens.
</simpara>
<simpara>
Não validar o arquivo que você está operando pode permitir que os usuários acessem
informações sensíveis em outros diretórios.
</simpara>
<simpara>
Devido ao grande número de estilos de listagem de diretórios não podemos garantir
que arquivos com nomes exóticos (como os que contém espaços) sejam manuseados corretamente.
</simpara>
<simpara>
Um desenvolvedor não deve misturar campos comuns de entrada <literal>input</literal> e campos de envio de arquivo na mesma
variável de formulário (usando um nome de campo <literal>input</literal> como <literal>foo[]</literal>).
</simpara>
</sect1>
<sect1 xml:id="features.file-upload.multiple">
<title>Enviando múltiplos arquivos</title>
<simpara>
Múltiplos arquivos podem ser enviados usando diferentes
nomes (<literal>name</literal>) para entradas (<literal>input</literal>).
</simpara>
<simpara>
Também é possível carregar vários arquivos simultaneamente e
ter a informação automaticamente organizada em arrays. Para
isso, é necessário usar a mesma sintaxe das arrays submetidas pelo
formulário HTML que você usa para múltiplos selects e checkboxes:
</simpara>
<para>
<example>
<title>Carregando múltiplos arquivos</title>
<programlisting role="html">
<![CDATA[
<form action="file-upload.php" method="post" enctype="multipart/form-data">
Envie esses arquivos:<br />
<input name="userfile[]" type="file" /><br />
<input name="userfile[]" type="file" /><br />
<input type="submit" value="Enviar arquivos" />
</form>
]]>
</programlisting>
</example>
</para>
<simpara>
Quando o formulário acima é submetido, os arrays
<varname>$_FILES['userfile']</varname>,
<varname>$_FILES['userfile']['name']</varname>, e
<varname>$_FILES['userfile']['size']</varname> serão
inicializados.
</simpara>
<simpara>
Por exemplo, suponha os nomes dos arquivos
<filename>/home/test/review.html</filename> e
<filename>/home/test/xwp.out</filename>. Neste caso,
<varname>$_FILES['userfile']['name'][0]</varname>
deve conter o valor <filename>review.html</filename>, e
<varname>$_FILES['userfile']['name'][1]</varname>
deve conter o valor <filename>xwp.out</filename>. Similarmente,
<varname>$_FILES['userfile']['size'][0]</varname> deve
conter o tamanho do arquivo <filename>review.html</filename>, e assim por diante.
</simpara>
<simpara>
<varname>$_FILES['userfile']['name'][0]</varname>,
<varname>$_FILES['userfile']['tmp_name'][0]</varname>,
<varname>$_FILES['userfile']['size'][0]</varname>, e
<varname>$_FILES['userfile']['type'][0]</varname>
também são definidas.
</simpara>
<warning>
<simpara>
O parâmetro
<link linkend="ini.max-file-uploads">max_file_uploads</link>
atua como um limite no número de arquivos que podem ser
enviados em uma única requisição. Você precisa ter certeza que
seu formulário não tentará enviar mais arquivos que o limite definido.
</simpara>
</warning>
<para>
<example>
<title>Carregando um diretório inteiro</title>
<simpara>
Nos campos de carregamento de arquivos HTML, é possível carregar um diretório inteiro com o atributo <literal>webkitdirectory</literal>.
Esse recurso é suportado na maioria dos navegadores modernos.
</simpara>
<simpara>
Com a informação <literal>full_path</literal>, é possível armazenar os caminhos relativos,
ou reconstruir o mesmo diretório no servidor.
</simpara>
<programlisting role="html">
<![CDATA[
<form action="file-upload.php" method="post" enctype="multipart/form-data">
Enviar este diretório:<br />
<input name="userfile[]" type="file" webkitdirectory multiple />
<input type="submit" value="Enviar arquivos" />
</form>
]]>
</programlisting>
</example>
<warning>
<simpara>
O atributo <literal>webkitdirectory</literal> não é padronizado e não está no roteiro de padronização.
Não utilize-o em sites em produção voltados para a Web: ele não funcionará para todos os usuários.
Também podem haver grandes incompatibilidades entre implementações e o comportamento pode mudar no futuro.
</simpara>
<simpara>
O PHP analisa apenas a informação de caminho relativo enviada pelo navegador/user-agent,
e passa essa informação para o array <varname>$_FILES</varname>.
Não há garantia de que os valores no array <literal>full_path</literal> contenham uma estrutura de diretórios real,
e a aplicação PHP não deve confiar nesta informação.
</simpara>
</warning>
</para>
</sect1>
<sect1 xml:id="features.file-upload.put-method">
<title>Suporte ao método PUT</title>
<para>
O PHP oferece suporte para o método HTTP PUT utilizado por alguns clientes
para armazenar arquivos em um servidor.
Requisições PUT são muito mais simples que o envio de arquivos usando
requisições POST, elas se parecem com algo assim:
<informalexample>
<programlisting role="HTTP">
<![CDATA[
PUT /path/filename.html HTTP/1.1
]]>
</programlisting>
</informalexample>
</para>
<para>
Isso normalmente significaria que o cliente remoto gostaria de salvar
o seguinte conteúdo: <filename>/path/filename.html</filename> em seu diretório web.
É óbvio não se tratar de uma boa ideia que o Apache ou o PHP permitam automaticamente que todos
sobrescrevam qualquer arquivo em seu diretório web. Então, para considerar isso como uma
requisição é necessário dizer ao seu servidor web que você quer que um determinado
script PHP cuide dessa requisição. Para fazer isso no Apache utilize
a diretiva <emphasis>Script</emphasis>. Ela pode ser colocada
quase em qualquer local de seu arquivo de configuração do Apache. Um
local comum é dentro de um bloco <literal>&lt;Directory&gt;</literal> ou talvez dentro de um bloco <literal>&lt;VirtualHost&gt;</literal>.
Uma linha como a seguinte deve funcionar:
<informalexample>
<programlisting>
<![CDATA[
Script PUT /put.php
]]>
</programlisting>
</informalexample>
</para>
<simpara>
Isto diz ao Apache para enviar todas as requisições PUT para as URIs que combinem com o
contexto dessa linha para o script <filename>put.php</filename>. Isto
assume que o PHP esteja ativo e permite extensões <filename class="extension">.php</filename>.
O destino para todas as requisições PUT
para esse script deve ser o próprio script,
não o nome do arquivo enviado.
</simpara>
<simpara>
Com o PHP pode ser feito algo como se segue no
script put.php. Isso copia o conteúdo de um arquivo enviado para o
arquivo <filename>myputfile.ext</filename> no servidor.
Provavelmente é necessário a realização de algumas verificações e/ou
autenticação de usuário antes de realizar essa cópia.
</simpara>
<para>
<example>
<title>Salvando arquivos HTTP PUT</title>
<programlisting role="php">
<![CDATA[
<?php
/* PUT data vem do fluxo stdin */
$putdata = fopen("php://input", "r");
/* Abre um arquivo para escrita */
$fp = fopen("myputfile.ext", "w");
/* Lê os dados 1KB de cada vez
e escreve no arquivo */
while ($data = fread($putdata,1024))
fwrite($fp,$data);
/* Fecha os fluxos */
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">Segurança do sistema de arquivos</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
-->