Bagaimana cara menangani file yang melebihi `post_max_size` PHP?

86

Saya sedang mengerjakan formulir PHP yang melampirkan file ke email, dan mencoba menangani kasus di mana file yang diunggah terlalu besar dengan baik.

Saya telah mempelajari bahwa ada dua pengaturan php.iniyang mempengaruhi ukuran maksimal dari unggahan file: upload_max_filesizedan post_max_size.

Jika ukuran file melebihi upload_max_filesize, PHP mengembalikan ukuran file sebagai 0. Tidak apa-apa; Saya bisa memeriksanya.

Tetapi jika melebihi post_max_size, skrip saya gagal secara diam-diam dan kembali ke formulir kosong.

Apakah ada cara untuk mengatasi kesalahan ini?

Nathan Long
sumber
1
Apakah Anda memiliki akses ke php.ini? post_max_size harus disetel lebih besar dari upload_max_filesize. Anda juga harus menggunakan <input type = "hidden" name = "MAX_FILE_SIZE" value = "30000" /> pada formulir seperti yang diuraikan ca2.php.net/manual/en/features.file-upload.post-method.php
Matt McCormick
@Matt McCormick - input MAX_FILE_SIZE berfungsi dengan baik - jika ukuran file melebihi itu, ukuran file sekarang ditampilkan sebagai 0, yang merupakan kasus yang sudah saya tangani. Meskipun ini dapat dilewati oleh pengguna jahat, ini melayani tujuan saya di sini, karena saya hanya mencoba gagal dengan anggun untuk pengguna biasa.
Nathan Long

Jawaban:

55

Dari dokumentasi :

Jika ukuran data posting lebih besar dari post_max_size, superglobals $ _POST dan $ _FILES kosong . Ini dapat dilacak dengan berbagai cara, misalnya dengan meneruskan variabel $ _GET ke skrip yang memproses data, yaitu <form action = "edit.php? Processing = 1">, dan kemudian memeriksa apakah $ _GET ['diproses'] adalah set.

Sayangnya, sepertinya PHP tidak mengirimkan kesalahan. Dan karena ini mengirimkan array $ _POST kosong, itulah mengapa skrip Anda akan kembali ke formulir kosong - ia tidak menganggapnya sebagai POST. (Keputusan desain yang cukup buruk IMHO)

Pemberi komentar ini juga punya ide menarik.

Tampaknya cara yang lebih elegan adalah perbandingan antara post_max_size dan $ _SERVER ['CONTENT_LENGTH']. Harap dicatat bahwa yang terakhir tidak hanya mencakup ukuran file yang diunggah ditambah data posting tetapi juga urutan multi bagian.

Matt McCormick
sumber
3
Silakan baca bagian yang dicetak tebal. Yaitu jika ukuran file melebihi upload_max_filesize, maka pengguna menanyakan apa yang terjadi jika bentuk post_max_size melebihi. post_max_size harus disetel lebih tinggi dari upload_max_filesize untuk mencoba menghindari masalah ini tetapi OP mungkin memiliki alasan untuk membuatnya tetap sama.
Matt McCormick
1
Maaf, saya mencampur post_max_size dan upload_max_filesize. +1
Pekka
@Matt - post_max_size IS diatur lebih tinggi dari upload_max_filesize, tetapi saya masih mendapatkan kegagalan jika upload melebihi keduanya. Jika berada di antara keduanya, saya melihat bahwa ukuran file menunjukkan 0.
Nathan Long
1
Anda telah menyelesaikannya, tapi ya post_max_size tersedia. Lakukan saja ini_get ('post_max_size'). ini_get () juga dapat digunakan untuk memeriksa pengaturan INI lainnya.
Matt McCormick
1
@SavasVedova - terima kasih telah menunjukkan hal itu, tetapi saya tidak dapat lagi memecahkan masalah. Sudah beberapa tahun dan saya tidak lagi bekerja di perusahaan itu atau di PHP. :)
Nathan Long
45

ada cara untuk menangkap / menangani file yang melebihi ukuran posting maksimal, ini adalah pilihan saya, karena memberi tahu pengguna akhir apa yang telah terjadi dan siapa yang salah;)

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
sumber
2
Ini bekerja ketika track_errors = Off, mungkin juga display_errors = Off dan display_startup_errors = Off di php.ini. Jika tidak, PHP tidak akan sampai sejauh itu dan mengirimkan peringatan, seperti dalam judul pertanyaan. Tetapi dalam sistem produksi ini harus menjadi pengaturan php.ini, jadi ini berfungsi dengan baik.
raoulsson
2
Ide rapi ini bekerja dengan baik dalam pengujian saya (PHP / 5.5.8). Ini juga dapat ditingkatkan dengan mempertimbangkan $_SERVER['CONTENT_LENGTH']dan upload_max_filesizemempertimbangkan.
Álvaro González
Memimpin dari komentar raoulsson , adakah cara untuk menekan peringatan jika Anda tidak berada di lingkungan dengan kesalahan ditekan?
brendo
6

Kami mendapat masalah untuk permintaan SOAP di mana pemeriksaan kekosongan $ _POST dan $ _FILES tidak berfungsi, karena keduanya juga kosong pada permintaan yang valid.

Oleh karena itu kami menerapkan pemeriksaan, membandingkan CONTENT_LENGTH dan post_max_size. Exception yang dilempar kemudian diubah menjadi XML-SOAP-FAULT oleh penangan pengecualian kami yang terdaftar.

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
sumber
@ manuel-azar: kalimat "istirahat" yang ditambahkan tidak benar. mereka tidak termasuk di sana .. lihat 3v4l.org/ABfGs
staabm
Tidak perlu memeriksa cek jika ukurannya melebihi. Tidak akan ada panjang_konten jika tidak ada file. Jadi, Anda hanya perlu memeriksa apakah content_length telah disetel dan variabel post dan file kosong. Tidak?
ADJenks
4

Berdasarkan jawaban @Matt McCormick dan @ AbdullahAJM, berikut adalah kasus uji PHP yang memeriksa variabel yang digunakan dalam pengujian telah disetel dan kemudian memeriksa apakah $ _SERVER ['CONTENT_LENGTH'] melebihi setelan 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
sumber
Ini adalah cara paling logis untuk melakukannya. Cukup periksa apakah _post kosong tetapi panjang konten tidak. Tidak perlu membandingkan ukuran.
ADJenks
1

Itu adalah cara sederhana untuk memperbaiki masalah ini:

Cukup panggil "checkPostSizeExceeded" di awal kode Anda

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.'
                    );
        }
    }

Ingat salin fungsi bantu ini:

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
sumber