Como lidar com arquivos que excedem o `post_max_size` do PHP?

86

Estou trabalhando em um formulário PHP que anexa um arquivo a um e-mail e estou tentando lidar com casos em que o arquivo carregado é muito grande.

Aprendi que existem duas configurações php.inique afetam o tamanho máximo de um upload de arquivo: upload_max_filesizee post_max_size.

Se o tamanho de um arquivo exceder upload_max_filesize, o PHP retorna o tamanho do arquivo como 0. Tudo bem; Eu posso verificar isso.

Mas se exceder post_max_size, meu script falha silenciosamente e volta para o formulário em branco.

Existe alguma maneira de detectar esse erro?

Nathan Long
fonte
1
Você tem acesso ao php.ini? post_max_size deve ser maior que upload_max_filesize. Você também deve usar <input type = "hidden" name = "MAX_FILE_SIZE" value = "30000" /> no formulário conforme descrito ca2.php.net/manual/en/features.file-upload.post-method.php
Matt McCormick
@Matt McCormick - a entrada MAX_FILE_SIZE funciona muito bem - se o tamanho do arquivo exceder isso, o tamanho do arquivo agora será exibido como 0, que é um caso que já resolvi. Mesmo que isso possa ser contornado por um usuário mal-intencionado, serve aos meus propósitos aqui, porque estou apenas tentando falhar normalmente para usuários regulares.
Nathan Long

Respostas:

55

Da documentação :

Se o tamanho dos dados de postagem for maior que post_max_size, os superglobais $ _POST e $ _FILES estão vazios . Isso pode ser rastreado de várias maneiras, por exemplo, passando a variável $ _GET para o script que processa os dados, ou seja, <form action = "edit.php? Processing = 1">, e então verificando se $ _GET ['processado'] é conjunto.

Então, infelizmente, não parece que o PHP enviou um erro. E como ele envia um array $ _POST vazio, é por isso que seu script está voltando para a forma em branco - ele não pensa que é um POST. (Uma decisão de design bastante pobre, IMHO)

Este comentador também tem uma ideia interessante.

Parece que uma forma mais elegante é a comparação entre post_max_size e $ _SERVER ['CONTENT_LENGTH']. Observe que o último inclui não apenas o tamanho do arquivo enviado mais os dados de postagem, mas também sequências de várias partes.

Matt McCormick
fonte
3
Por favor, leia a parte em negrito. Ou seja, se o tamanho do arquivo exceder upload_max_filesize, o usuário está perguntando o que acontece quando o formulário excede post_max_size. post_max_size deve ser definido mais alto que upload_max_filesize para tentar evitar esse problema, mas o OP pode ter razões para mantê-lo o mesmo.
Matt McCormick
1
Desculpe, eu misturei post_max_size e upload_max_filesize. +1
Pekka
@Matt - post_max_size é definido mais alto que upload_max_filesize, mas ainda recebo a falha se o upload exceder ambos. Se ficar entre os dois, vejo que o tamanho do arquivo mostra 0.
Nathan Long
1
Você resolveu, mas sim post_max_size está disponível. Basta fazer ini_get ('post_max_size'). ini_get () também pode ser usado para verificar outras configurações INI.
Matt McCormick
1
@SavasVedova - obrigado por apontar isso, mas não consigo mais solucionar o problema. Já faz alguns anos e não trabalho mais nessa empresa nem em PHP. :)
Nathan Long,
45

existe uma maneira de capturar / manipular arquivos que excedam o tamanho máximo de postagem, esta é a minha preferida, pois informa ao usuário final o que aconteceu e quem é o culpado;)

if (empty($_FILES) && empty($_POST) &&
        isset($_SERVER['REQUEST_METHOD']) &&
        strtolower($_SERVER['REQUEST_METHOD']) == 'post') {
    //catch file overload error...
    $postMax = ini_get('post_max_size'); //grab the size limits...
    echo "<p style=\"color: #F00;\">\nPlease note files larger than {$postMax} will result in this error!<br>Please be advised this is not a limitation in the CMS, This is a limitation of the hosting server.<br>For various reasons they limit the max size of uploaded files, if you have access to the php ini file you can fix this by changing the post_max_size setting.<br> If you can't then please ask your host to increase the size limits, or use the FTP uploaded form</p>"; // echo out error and solutions...
    addForm(); //bounce back to the just filled out form.
}
else {
    // continue on with processing of the page...
}
AbdullahAJM
fonte
2
Isso funciona quando track_errors = Off, possivelmente também display_errors = Off e display_startup_errors = Off no php.ini. Caso contrário, o PHP nem chegará tão longe e enviará o aviso, como no título da pergunta. Mas em um sistema de produção, esta deve ser a configuração do php.ini, então funciona muito bem.
raoulsson
2
Essa ideia bacana está funcionando bem em meus testes (PHP / 5.5.8). Ele também pode ser reforçada tomada $_SERVER['CONTENT_LENGTH']e upload_max_filesizeem conta.
Álvaro González
Com base no comentário de raoulsson , há alguma maneira de suprimir o aviso se você não estiver em um ambiente com erros suprimidos?
brendo,
6

Temos o problema para solicitações SOAP em que uma verificação de vazio de $ _POST e $ _FILES não funciona, porque eles também estão vazios em solicitações válidas.

Portanto, implementamos uma verificação, comparando CONTENT_LENGTH e post_max_size. A exceção lançada é posteriormente transformada em XML-SOAP-FAULT por nosso manipulador de exceção registrado.

private function checkPostSizeExceeded() {
    $maxPostSize = $this->iniGetBytes('post_max_size');

    if ($_SERVER['CONTENT_LENGTH'] > $maxPostSize) {
        throw new Exception(
            sprintf('Max post size exceeded! Got %s bytes, but limit is %s bytes.',
                $_SERVER['CONTENT_LENGTH'],
                $maxPostSize
            )
        );
    }
}

private function iniGetBytes($val)
{
    $val = trim(ini_get($val));
    if ($val != '') {
        $last = strtolower(
            $val{strlen($val) - 1}
        );
    } else {
        $last = '';
    }
    switch ($last) {
        // The 'G' modifier is available since PHP 5.1.0
        case 'g':
            $val *= 1024;
            // fall through
        case 'm':
            $val *= 1024;
            // fall through
        case 'k':
            $val *= 1024;
            // fall through
    }

    return $val;
}
staabm
fonte
@ manuel-azar: as instruções de "pausa" adicionadas não estão corretas. aqueles não pertencem lá .. veja 3v4l.org/ABfGs
staabm
Não é necessário verificar se o tamanho excede. Não haveria content_length se não houvesse arquivo. Portanto, você só precisa verificar se content_length está definido e se as variáveis ​​de postagem e arquivo estão vazias. Não?
ADJenks
4

Com base nas respostas de @Matt McCormick e @ AbdullahAJM, aqui está um caso de teste de PHP que verifica se as variáveis ​​usadas no teste estão definidas e, em seguida, verifica se $ _SERVER ['CONTENT_LENGTH'] excede a configuração de php_max_filesize:

            if (
                isset( $_SERVER['REQUEST_METHOD'] )      &&
                ($_SERVER['REQUEST_METHOD'] === 'POST' ) &&
                isset( $_SERVER['CONTENT_LENGTH'] )      &&
                ( empty( $_POST ) )
            ) {
                $max_post_size = ini_get('post_max_size');
                $content_length = $_SERVER['CONTENT_LENGTH'] / 1024 / 1024;
                if ($content_length > $max_post_size ) {
                    print "<div class='updated fade'>" .
                        sprintf(
                            __('It appears you tried to upload %d MiB of data but the PHP post_max_size is %d MiB.', 'csa-slplus'),
                            $content_length,
                            $max_post_size
                        ) .
                        '<br/>' .
                        __( 'Try increasing the post_max_size setting in your php.ini file.' , 'csa-slplus' ) .
                        '</div>';
                }
            }
Lance Cleveland
fonte
Esta é a maneira mais lógica de fazer isso. Apenas verifique se _post está vazio, mas o comprimento do conteúdo não. Não há necessidade de comparar tamanhos.
ADJenks
1

Essa é uma maneira simples de resolver esse problema:

Basta chamar "checkPostSizeExceeded" no início de seu código

function checkPostSizeExceeded() {
        if (isset($_SERVER['REQUEST_METHOD']) and $_SERVER['REQUEST_METHOD'] == 'POST' and
            isset($_SERVER['CONTENT_LENGTH']) and empty($_POST)//if is a post request and $_POST variable is empty(a symptom of "post max size error")
        ) {
            $max = get_ini_bytes('post_max_size');//get the limit of post size 
            $send = $_SERVER['CONTENT_LENGTH'];//get the sent post size

            if($max < $_SERVER['CONTENT_LENGTH'])//compare
                throw new Exception(
                    'Max size exceeded! Were sent ' . 
                        number_format($send/(1024*1024), 2) . 'MB, but ' . number_format($max/(1024*1024), 2) . 'MB is the application limit.'
                    );
        }
    }

Lembre-se de copiar esta função auxiliar:

function get_ini_bytes($attr){
    $attr_value = trim(ini_get($attr));

    if ($attr_value != '') {
        $type_byte = strtolower(
            $attr_value{strlen($attr_value) - 1}
        );
    } else
        return $attr_value;

    switch ($type_byte) {
        case 'g': $attr_value *= 1024*1024*1024; break;
        case 'm': $attr_value *= 1024*1024; break;
        case 'k': $attr_value *= 1024; break;
    }

    return $attr_value;
}
Doglas
fonte