takeaway

Today, I used ThinkPHP image batch upload and found that only two files were stored when three files were uploaded at the same time and one file was missing. Debugging found that the same file name was generated when the file name was generated, resulting in the file being overwritten.

Official document example – Multiple file upload

  • view

    <form action="/index/index/upload" enctype="multipart/form-data" method="post"> <input type="file" name="image[]" /> <br> <input type="file" name="image[]" /> <br> <input type="file" name="image[]" /> <br> <input type="submit" value=" upload"  /> </form>Copy the code
  • The controller

    Public function upload(){$files = request()->file('image'); Foreach ($files as $file){$info = $file->move('.. /uploads'); Echo $info->getExtension(); echo $info->getExtension(); / / output 42 a79759f284b767dfcb2a0197904287. JPG echo $info - > getFilename (); Echo $file->getError(); }}Copy the code

    }

This code generates the file name directly during move

  • / thinkphp/library/think/File. The method is autoBuildName PHP automatically generated File name

      protected function autoBuildName()
      {
          if ($this->rule instanceof \Closure) {
              $savename = call_user_func_array($this->rule, [$this]);
          } else {
              switch ($this->rule) {
                  case 'date':
                      $savename = date('Ymd') . DIRECTORY_SEPARATOR . md5(microtime(true));
                      break;
                  default:
                      if (in_array($this->rule, hash_algos())) {
                          $hash     = $this->hash($this->rule);
                          $savename = substr($hash, 0, 2) . DIRECTORY_SEPARATOR . substr($hash, 2);
                      } elseif (is_callable($this->rule)) {
                          $savename = call_user_func($this->rule);
                      } else {
                          $savename = date('Ymd') . DIRECTORY_SEPARATOR . md5(microtime(true));
                      }
              }
          }
    
          return $savename;
      }
    Copy the code

$this->rule ($this->rule

 $savename = date('Ymd') . DIRECTORY_SEPARATOR . md5(microtime(true));
Copy the code

Md5 Encrypts the current microsecond floating point number

The method is called at the same time during the loop so the same file name is generated

How to solve

The rule that generates the file name can pass in the callback and implement a generated callback function of its own

Create a public function in the common.php file at the root of your project

if (! Function_exists ('autoBuild')) {/** * /** Custom generated file name generation rules * thinkPHP framework uses microsecond timestamp generation to generate duplicate names when batch uploads ** @param $rule encryption method * @return string */ function autoBuild($rule='sha256'){ if (! in_array($rule, hash_algos())) { $rule='md5'; } $uuid=guid(); $hash = hash ($rule, time () mt_rand (1000999). $uuid); $savename = date('Ymd'). DIRECTORY_SEPARATOR. Substr ($hash, 2,36); return $savename; }} /** * generate unique ID */ if (! function_exists('guid')) { function guid() { if (function_exists('com_create_guid')) { return com_create_guid(); } else { mt_srand((double)microtime() * 10000); //optional for PHP 4.2.0 and up. $charid = strtoupper(md5(uniqid(rand(), true))); $hyphen = chr(45); // "-" $uuid = chr(123)// "{" . substr($charid, 0, 8) . $hyphen . substr($charid, 8, 4) . $hyphen . substr($charid, 12, 4) . $hyphen . substr($charid, 16, 4) . $hyphen . substr($charid, 20, 12) . chr(125); // "}" return $uuid; }}}Copy the code

Modify the call to the controller

Public function upload(){$files = request()->file('image'); Foreach ($files as $file){$info = $file->rule('autoBuild')->move('.. /uploads'); Echo $info->getExtension(); echo $info->getExtension(); / / output 42 a79759f284b767dfcb2a0197904287. JPG echo $info - > getFilename (); Echo $file->getError(); }}}Copy the code

The end of the