Ci4 / 图像上传和操作错误 / finfo_file(/tmp/phpSrVWUZ): 无法打开流:没有这样的文件或目录

Ci4 / Image Upload and Manipulate Error / finfo_file(/tmp/phpSrVWUZ): failed to open stream: No such file or directory

我正在尝试 (1) 上传图像文件 (2) 调整大小并居中 (3) 更改文件名 (4) 将其移至 /public 目录。

这是我的部分代码;

$path = $this->request->getFile('dh_site_logo')->store();
            
$temp_file_path = WRITEPATH.'uploads/' . $path;

service('image')
    ->withFile($temp_file_path)  // must include full path
    ->fit(250,150,'center')
    ->save($temp_file_path);
            
$public_file_path = ROOTPATH.'public';
$file_to_upload = 'site_logo.'.$update_post->guessExtension();;
$overwrite = true;      
        
$update_post->move($public_file_path, $file_to_upload, $overwrite);

我想我必须在移动到 /public

之前在 writable/uploads 目录中进行图像处理

我似乎无法锁定已使用新的随机名称上传的文件,操作然后移动。

这个也试过了

            // example of return $path = "20220130/1643544458_5a528551d1fe83c88e02.gif"
            $path = $this->request->getFile('dh_site_logo')->store('site_logo', 'site_logo.gif');
            
            $temp_file_path = WRITEPATH.'uploads/' . $path;

             service('image')
                ->withFile($temp_file_path)  // must include full path
                ->fit(250,150,'center')
                ->save($temp_file_path);
            
            $public_file_path = ROOTPATH.'public';
            $new_file_name = 'site_logo.gif';
            $overwrite = true;      
        
            $update_post->move($public_file_path, $new_file_name, $overwrite);

以上报错,The uploaded file has already been moved

Ci4 / Image Upload and Manipulate Error / finfo_file(/tmp/phpSrVWUZ): failed to open stream: No such file or directory

问题 1:

您收到上述错误是因为以下代码行:

$file_to_upload = 'site_logo.'.$update_post->guessExtension();;

解释 1:

在下面的第一行代码中:

$path = $this->request->getFile('dh_site_logo')->store();

您正在调用 CodeIgniter\HTTP\Files\UploadedFile::store(?string $folderName = null, ?string $fileName = null) 方法将上传的文件 (/tmp/phpSrVWUZ) 保存到新位置。

即:store(...)方法将上传的文件server's default temporary directory/tmp)移动到项目的上传目录(WRITEPATH . 'uploads/').

然后您尝试调用 $update_post->guessExtension() 忘记 UploadedFile 实例 $update_post 仍然引用旧的 non-existent 路径(/tmp/phpSrVWUZ),因此出现错误。

更具体地说,导致错误的方法CodeIgniter\HTTP\Files\UploadedFile::guessExtension() calls another method CodeIgniter\Files\File::getMimeType(). The method getMimeType(...) tries to retrieve the mime type using the code snippet below on a non-existent file

finfo_file(finfo_open(FILEINFO_MIME_TYPE), "/tmp/phpSrVWUZ");

// PHP Warning:  finfo_file(/tmp/phpSrVWUZ): Failed to open stream: No such file or directory in ...

=========

问题 2:

Tried this, too

...

The above give me an error, The uploaded file has already been moved

解释二:

您通常会收到此错误,因为您多次尝试将上传的文件移动到新位置。

当您多次调用 CodeIgniter\HTTP\Files\UploadedFile::store(...) or CodeIgniter\HTTP\Files\UploadedFile::move(...) 方法时会发生这种情况。

Excerpt from CI 4.x源代码。

/**
 * Move the uploaded file to a new location.
 * ...
 * If this method is called more than once, any subsequent calls MUST raise
 * an exception.
 * ...
 */
public function move(...): bool {
// ...
        if ($this->hasMoved) {
            throw HTTPException::forAlreadyMoved();
        }
// ...

}

更具体地说,每个 UploadedFile 实例都有一个名为 protected $hasMoved = false; 的 属性,一旦上传的文件已更新为 true已成功从服务器的默认临时目录移动:

Excerpt from CI 4.x源代码。

    /**
     * Returns whether the file has been moved or not. If it has,
     * the move() method will not work and certain properties, like
     * the tempName, will no longer be available.
     */
    public function hasMoved(): bool;

解决方案 A:

如果您不关心原始上传的文件并且您对最终的transformed/resized文件感兴趣 驻留在 'public' 路径中。


public function createThumbnail(\CodeIgniter\HTTP\Files\UploadedFile $uploadedFile, ?string $newThumbnailFileName = null, int $width = 250, int $height = 150, string $position = "center"): ?\CodeIgniter\Files\File
{
    if (!$uploadedFile->isValid()) {
        return null;
    }

    $newThumbnailFileName = $newThumbnailFileName
        ? ((($point = strrpos($newThumbnailFileName, ".")) === false) ? $newThumbnailFileName : substr($newThumbnailFileName, 0, $point)) . $uploadedFile->guessExtension()
        : $uploadedFile->getRandomName();

    $targetPath = ROOTPATH . 'public' . DIRECTORY_SEPARATOR . $newThumbnailFileName;

    \Config\Services::image()
        ->withFile($uploadedFile->getRealPath() ?: $uploadedFile->__toString())
        ->fit($width, $height, $position)
        ->save($targetPath);

    return new \CodeIgniter\Files\File($targetPath, true);
}

上面的函数基本上将原始上传的文件留在服务器的默认临时目录不变并处理一个新图像,该图像保存在项目的'public' 路径.

解决方案 A 的用法:

上面的函数 returns 一个 CodeIgniter\Files\File 实例表示新转换的图像。即:

$requestFileName = "dh_site_logo";
$uploadedFile = $this->request->getFile($requestFileName);

// Generates a thumbnail in the 'public' path with a uniquely generated filename.
$thumbnail = $this->createThumbnail(
    uploadedFile: $uploadedFile
);

// OR

// Generates a thumbnail in the 'public' path with a custom filename ('site_logo').
$thumbnail = $this->createThumbnail(
    uploadedFile: $uploadedFile,
    newThumbnailFileName: "site_logo"
);

// OR

// You can modify the default parameters as well.
$thumbnail = $this->createThumbnail(
    uploadedFile: $uploadedFile,
    width: 100,
    height: 150,
    position: "left"
);

解决方案 B:

如果由于某种原因你想 transform/resize 并将修改后的图像保存在 'public' 路径中 相似 解决方案A 并且仍然 persist/keep 或将原始上传的文件从服务器的临时目录移动到项目的 writable/uploads 文件夹以供将来参考或使用。

TIP: The file will be deleted from the temporary directory at the end of the request if it has not been moved away or renamed. - Excerpt From PHP Doc: Example #2 Validating file uploads

步骤:

  1. 根据需要生成任意数量的缩略图(转换后的文件)。
  2. 最后,将上传的文件从服务器的默认临时目录移动到项目的writable/uploads文件夹中。

$requestFileName = "dh_site_logo";
$uploadedFile = $this->request->getFile($requestFileName);

// 1. Generate as many thumbnails (transformed files) as you need.

// Generates a thumbnail in the 'public' path with
// a uniquely generated filename.
$thumbnail = $this->createThumbnail(
    uploadedFile: $uploadedFile
);

// 2. Lastly, move the uploaded file from the server's default temporary
// directory to the project's 'writable/uploads' folder (I.e: $uploadedFile->store()).

if (!$uploadedFile->hasMoved()) {
    // The moved uploaded file.
    $file = new \CodeIgniter\Files\File(WRITEPATH . 'uploads/' . $uploadedFile->store(), true);
}