【问题标题】:Laravel 5.3, check if uploaded file is bigger than upload_max_filesize (optional upload)Laravel 5.3,检查上传的文件是否大于upload_max_filesize(可选上传)
【发布时间】:2017-03-30 22:13:26
【问题描述】:

在 Laravel 5.3 中,我试图捕捉上传文件的文件大小是否大于 upload_max_filesize。上传字段不是必需的。

这个方法我试过了,还是不行

public function checkFile($field)
{
    if (request()->hasFile($field)){ // check if field is present
        $file = request()->file($field);
        if (!$file->isValid()){ // now check if it's valid
            return back()->with('error', $file->getErrorMessage());
        }
    }
}

我不能只使用if (!$file->isValid()),因为文件字段是可选的,如果字段为空,我会得到Call to a member function isValid() on null

所以我必须使用if (request()->hasFile($field)) 检查字段是否存在,但这不适用于大文件,因为dd(request()->hasFile('picture')) 返回false

当然,我可以依赖默认的 Laravel 验证器消息,但我得到了一个虚拟的 The picture failed to upload.,它不会向用户提供任何线索。

【问题讨论】:

标签: php laravel upload


【解决方案1】:

只有当你上传的文件大小小于 php.ini 中设置的限制时,Laravel 验证才会起作用。

如果您尝试上传大于限制的文件,PHP 不会将请求转发给 Laravel,并且会立即出错。因此,Laravel 在这种情况下无法做任何事情。

解决这个问题的一种方法是在 php.ini 中设置一个更大的限制,然后在 Laravel 中验证文件大小。

【讨论】:

    【解决方案2】:

    您应该考虑使用内置的 Laravel 表单请求验证系统。有一个内置的验证规则可让您指定 max 文件大小,您可以在此处查看文档:

    https://laravel.com/docs/5.3/validation#rule-max

    你的规则看起来像这样:

    [
        'video' => 'max:256'
    ]
    

    如果上传的文件大于 256kb,这将失败。

    您提到您不喜欢 Laravel 的内置验证错误消息。没问题!您可以在 resources/lang/en/validation.php 语言文件中更改它们,这是您需要更改的行:

    https://github.com/laravel/laravel/blob/master/resources/lang/en/validation.php#L51

    【讨论】:

    • uhm...这种验证适用于高于“最大”阈值但低于 upload_max_filesize 设置的文件,不适用于高于 upload_max_filesize 设置的文件...
    • 如果您的upload_max_filesize 设置太低,任何形式的服务器端验证都将不起作用 - 您需要增加此设置。
    • 正如 OP 评论的那样,这个解决方案没有帮助,因为如果文件上传失败,则永远不会检查“max”规则。
    • @Ivan uhm.... 如果您尝试上传高于 upload_max_filesize 设置的文件,Laravel 甚至不会收到请求。 PHP 本身将彻底失败,并且请求甚至不会发送到 Web 应用程序。如果您希望 Laravel(或任何 PHP 应用程序)处理文件大小验证,您必须增加此限制。
    • 这应该可以。但是你的 PHP 配置和 Laravel 配置应该有相同的限制。
    【解决方案3】:

    My previous answer 处理了上传文件大于php.ini 中的upload_max_filesize 设置的情况。但是当文件的大小使请求大于post_max_size(另一个php.ini 设置)时失败。这种情况更难处理,因为输入($_POST 全局,如果我们处理纯 PHP)被清除。

    我认为中间件是进行这种“验证”的好地方:

    public function handle(Request $request, Closure $next)
    {
        $post_max_size = ini_get('post_max_size') * 1024 * 1024;
        $content_length = $request->server('HTTP_CONTENT_LENGTH') ?: $request->server('CONTENT_LENGTH') ?: 0;
    
        $response = $next($request);
    
        if ($content_length > $post_max_size)
        {
            return redirect()->back()->with('errors', collect([trans('validation.max.file', ['max' => 2000])]));
        }
    
        return $response;
    }
    

    (正如我所说,这不会保留输入。)

    【讨论】:

      【解决方案4】:

      服务器端代码(在控制器中):

      以下函数取自 meustrus 作者stack answer 的drupal,我以这里为例。 从 post_max_size 开始

      // Returns a file size limit in bytes based on the PHP upload_max_filesize
      // and post_max_size
      $max_size = parse_size(ini_get('post_max_size'));
      
      // If upload_max_size is less, then reduce. Except if upload_max_size is
      // zero, which indicates no limit.
      $upload_max = parse_size(ini_get('upload_max_filesize'));
      if ($upload_max > 0 && $upload_max < $max_size) {
        $max_size = $upload_max;
      }
      
      //Get max upload file size limit...
      $file_upload_max_size = $max_size;
      

      解析大小的公共函数

      public function parse_size($size) {
        $unit = preg_replace('/[^bkmgtpezy]/i', '', $size); // Remove the non-unit characters from the size.
        $size = preg_replace('/[^0-9\.]/', '', $size); // Remove the non-numeric characters from the size.
        if ($unit) {
          // Find the position of the unit in the ordered string which is the power of magnitude to multiply a kilobyte by.
          return round($size * pow(1024, stripos('bkmgtpezy', $unit[0])));
        }
        else {
          return round($size);
        }
      }
      

      设置压缩以将 'file_upload_max_size' 值发送到刀片

      return view('YOURBLADEPATH',compact('file_upload_max_size'));
      

      JS 验证(在 Blade 中):

      <script type="text/javascript">
      document.forms[0].addEventListener('submit', function( evt ) {
          var file = document.getElementById('file').files[0];
      
          if(file && file.size < '{$file_upload_max_size}') { // 10 MB (this size is in bytes)
              //Submit form        
          } else {
              //Prevent default and display error
              evt.preventDefault();
          }
      }, false);
      

      【讨论】:

        【解决方案5】:

        根据我的经验,我使用 JavaScript 来验证文件大小。

        *JavaScript

        function validate_size(elm) {
            var file = elm.files[0];
            var max_size = {{ env('UPLOAD_MAX_FILESIZE', 2097152) }}; // in bytes, e.g. 2 MB = 2097152 Bytes
            var size_name = {{ floor(env('UPLOAD_MAX_FILESIZE', 2097152) / 1024 / 1024) }}; // result: 2
        
            if (file && file.size < max_size) {
                // CONTINUE
            } else {
                // PREVENT AND DISPLAY ERROR
                alert('File size cannot be greater than ' + size_name + ' MB');
                // RESET INPUT FILE VALUE
                elm.value = '';
            }
        }
        

        *HTML/Laravel 刀片

        ...
        <input type="file" name="photo" onchange="validate_size(this)">
        ...
        

        如果用户选择的文件大小超过允许的大小,它会提醒用户。

        【讨论】:

          【解决方案6】:

          Laravel 文件验证器的默认行为是在上传不正确时拒绝文件,无论出于何种原因。然后不应用验证规则,因此“最大”规则在这里无法为您提供帮助。 在这种情况下,您显然需要针对此类错误(超出最大文件大小)的自定义消息。我认为扩展 Validator 类是一个优雅的解决方案。

          use Illuminate\Http\UploadedFile;
          use Illuminate\Validation\Validator;
          
          class UploadSizeValidator extends Validator
          {
              protected function validateAttribute($attribute, $rule)
              {
                  $value = $this->getValue($attribute);
          
                  if ($value instanceof UploadedFile && $value->getError() != UPLOAD_ERR_OK) {
                      switch ($value->getError()) {
                          case UPLOAD_ERR_INI_SIZE:
                              return $this->addFailure($attribute, 'max_file_size_exceeded', []);
                          // check additional UPLOAD_ERR_XXX constants if you want to handle other errors
                      }
                  }
          
                  return parent::validateAttribute($attribute, $rule);
              }
          }
          

          现在,您如何告诉框架使用您的验证器而不是默认验证器?您可以在 Validator Factory 上设置解析器功能:

          // do this in a 'boot' method of a ServiceProvider
          use Illuminate\Support\Facades\Validator;
          
          Validator::resolver(function($translator, $data, $rules, $messages, $customAttributes) {
              return new UploadSizeValidator($translator, $data, $rules, $messages, $customAttributes);
          });
          

          最后为validation.php lang 文件中的键“max_file_size_exceeded”设置适当的消息。

          【讨论】:

          • Uhm... 似乎有点复杂:我注意到有一个 Symfony\Component\HttpFoundation\File\UploadedFile 类包含我需要的所有方法,在任何之前没有简单的方法来使用它发生其他验证,如果文件太大,将用户重定向到上一页?
          • @Ivan 是的,这是可能的。你是如何验证的?在带有$this-&gt;validate 的控制器中,或者您是否有带有验证规则的 FormRequest 类?
          • 抱歉回复晚了,我最近 10 天不在家 :-( 是的,我使用 $this-&gt;validate:我刚刚看到你的回答:可以在不保留输入的情况下重定向,但我想避免使用中间件
          猜你喜欢
          • 2013-07-01
          • 1970-01-01
          • 1970-01-01
          • 2017-02-23
          • 1970-01-01
          • 2017-02-12
          • 1970-01-01
          • 1970-01-01
          • 2019-12-07
          相关资源
          最近更新 更多