CakePHP:文件上传时无法将类 Laminas\Dictoros\UploadedFile 的对象转换为字符串

Nil*_*ony 0 cakephp cakephp-4.x

我正在尝试在 cakephp 版本 4 中上传文件。

我正在关注这个文档

我在控制器中尝试过

if ($this->request->is('post')) {

            $image = $this->request->getData('image');
            $fileName = $image->getClientFilename();

            $targetPath = WWW_ROOT.'img'.DS.$fileName;

            $image->moveTo($targetPath);

            $user = $this->Users->patchEntity($user, $this->request->getData());   //line 58

            $user->image = $fileName;

            $this->Users->save($user);
}
Run Code Online (Sandbox Code Playgroud)

图片上传工作正常,名称也保存在数据库中。但是当发生验证错误时,我得到

Warning (4096): Object of class Laminas\Diactoros\UploadedFile could not be converted to string [CORE\src\Database\Type\StringType.php, line 97]
Run Code Online (Sandbox Code Playgroud)

日志

Cake\Database\Type\StringType::marshal() - CORE\src\Database\Type\StringType.php, line 97
Cake\ORM\Marshaller::Cake\ORM\{closure}() - CORE\src\ORM\Marshaller.php, line 78
Cake\ORM\Marshaller::merge() - CORE\src\ORM\Marshaller.php, line 558
Cake\ORM\Table::patchEntity() - CORE\src\ORM\Table.php, line 2761
App\Controller\UsersController::add() - APP/Controller\UsersController.php, line 58
Cake\Controller\Controller::invokeAction() - CORE\src\Controller\Controller.php, line 524
Cake\Controller\ControllerFactory::invoke() - CORE\src\Controller\ControllerFactory.php, line 79
Cake\Http\BaseApplication::handle() - CORE\src\Http\BaseApplication.php, line 229
Cake\Http\Runner::handle() - CORE\src\Http\Runner.php, line 77
Cake\Http\Runner::handle() - CORE\src\Http\Runner.php, line 77
Cake\Http\Middleware\CsrfProtectionMiddleware::process() - CORE\src\Http\Middleware\CsrfProtectionMiddleware.php, line 132
Cake\Http\Runner::handle() - CORE\src\Http\Runner.php, line 73
Cake\Http\Runner::run() - CORE\src\Http\Runner.php, line 58
Cake\Routing\Middleware\RoutingMiddleware::process() - CORE\src\Routing\Middleware\RoutingMiddleware.php, line 162
Cake\Http\Runner::handle() - CORE\src\Http\Runner.php, line 73
Cake\Routing\Middleware\AssetMiddleware::process() - CORE\src\Routing\Middleware\AssetMiddleware.php, line 68
Run Code Online (Sandbox Code Playgroud)

ndm*_*ndm 8

首先,您的代码示例盲目信任客户端,它接受任何文件,并允许通过传递路径作为文件名来选择任意目标位置!切勿在未验证/清理的情况下使用客户端数据,相信客户端提供的文件日期会导致各种漏洞!!!

话虽如此,通常您要么使用单独的字段进行文件上传,要么使用在编组时不转换数据的自定义数据库类型,以便您可以在之后手动转换它。目前,您似乎使用应该在数据库中保存字符串的上传字段,因此编组器将尝试将输入转换为字符串,这当然会失败。

例如,将表单控件从imageto重命名image_file(数据库中不应该存在这样的列),为 ,添加适当的验证规则image_file,类似于此,以确保上传有效,即检查类型,大小,名称等:

$validator
    ->notEmptyFile('image_file')
    ->uploadedFile('image_file', [
        'types' => ['image/png'], // only PNG image files
        'minSize' => 1024, // Min 1 KB
        'maxSize' => 1024 * 1024 // Max 1 MB
    ])
    ->add('image_file', 'minImageSize', [
        'rule' => ['imageSize', [
            // Min 10x10 pixel
            'width' => [Validation::COMPARE_GREATER_OR_EQUAL, 10],
            'height' => [Validation::COMPARE_GREATER_OR_EQUAL, 10],
        ]]
    ])
    ->add('image_file', 'maxImageSize', [
        'rule' => ['imageSize', [
            // Max 100x100 pixel
            'width' => [Validation::COMPARE_LESS_OR_EQUAL, 100],
            'height' => [Validation::COMPARE_LESS_OR_EQUAL, 100],
        ]]
    ])
    ->add('image_file', 'filename', [
        'rule' => function (UploadedFileInterface $file) {
            // filename must not be a path
            $filename = $file->getClientFilename();
            if (strcmp(basename($filename), $filename) === 0) {
                return true;
            }

            return false;
        }
    ])
    ->add('image_file', 'extension', [
        'rule' => ['extension', ['png']] // .png file extension only
    ]);
Run Code Online (Sandbox Code Playgroud)

然后修补后处理上传,即在验证发生后,只有在验证成功时才移动文件!

$user = $this->Users->patchEntity($user, $this->request->getData());
if (!$user->getErrors()) {
    // never trust anything in `$image` if you haven't properly validated it!!!
    $image = $this->request->getData('image_file');
    $fileName = $image->getClientFilename();
    // if you want to take it a step further, drop the user supplied filename
    // $fileName = \Cake\Utility\Security::hash($image->getClientFilename(), 'sha1');
    // and re-encode the image to remove possible dangerous payloads, meta data, etc
    // reEncodeImage($image->getStream());

    $image->moveTo(WWW_ROOT . 'img' . DS . $fileName);

    $user->image = $fileName;
}

if ($this->Users->save($user)) {
    // success
} else {
    // failure
}
Run Code Online (Sandbox Code Playgroud)

一旦您掌握了事情的工作原理,您可能应该考虑在您的表类或行为(beforeSave()事件/回调对于执行上传逻辑非常流行)或某种服务中移动上传处理逻辑。

您可能还想查看现有插件如何处理上传,例如参见https://github.com/FriendsOfCake/awesome-cakephp#user-content-files