使用CakePHP和安全哈希创建文件上传

9o685dep  于 2022-11-11  发布在  PHP
关注(0)|答案(1)|浏览(115)

我有一个PageController允许添加,编辑和删除页面。对于每个页面,我们可以上传一个单一的图像,我将使用作为横幅。我正在使用蛋糕安全哈希来更改文件名,但我不确定我是否正确地使用它。一些页面可能有相同的图像,所以,使用我下面的代码,删除或更新一个页面将删除所有其他页面的图像有相同的图像...
你能检查一下我下面的代码是否有意义,或者有什么方法可以改进它吗?

public function add()
{
    $page = $this->Pages->newEmptyEntity();

    if ($this->request->is('post'))
    {
        $page = $this->Pages->patchEntity($page, $this->request->getData());

        if(!$page->getErrors()) {
            $image = $this->request->getData('image_file');
            $filename = $image->getClientFilename();
            $filename = \Cake\Utility\Security::hash($image->getClientFilename(), 'sha1');

            $image->moveTo(WWW_ROOT . 'img' . DS . 'pages' . DS . $filename);
            $page->image = $filename;
        }

        if ($this->Pages->save($page))
        {
            $this->Flash->success(__('Your page has been created.'));
            return $this->redirect(['action' => 'index']);
        }

        $this->Flash->error(__('Unable to add your page.'));
    }

    $this->set('page', $page);
}

/**
 * Edit method
 *
 * @param string|null $id Page id.
 * @return \Cake\Http\Response|null|void Redirects on successful edit, renders view otherwise.
 * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
 */
public function edit($id)
{
    $page = $this->Pages->get($id);

    if ($this->request->is(['post', 'put']))
    {
        $this->Pages->patchEntity($page, $this->request->getData());

        if(!$page->getErrors()) {
            $oldImage = $page->image;
            $image = $this->request->getData('image_file');
            $filename = $image->getClientFilename();
            $filename = \Cake\Utility\Security::hash($image->getClientFilename(), 'sha1');

            $image->moveTo(WWW_ROOT . 'img' . DS . 'pages' . DS . $filename);
            $page->image = $filename;

            unlink(WWW_ROOT . 'img' . DS . 'pages' . DS . $oldImage);
        }

        if ($this->Pages->save($page))
        {
            $this->Flash->success(__('Your page has been updated.'));
            return $this->redirect(['action' => 'index']);
        }

        $this->Flash->error(__('Unable to update your page.'));
    }

    $this->set('page', $page);
}

/**
 * Delete method
 *
 * @param string|null $id Page id.
 * @return \Cake\Http\Response|null|void Redirects to index.
 * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
 */
public function delete($id)
{
    $page = $this->Pages->get($id);

    if ($this->Pages->delete($page))
    {
        unlink(WWW_ROOT . 'img' . DS . 'pages' . DS . $page->image);
        $this->Flash->success(__('The {0} page has been deleted.', $page->title));
        return $this->redirect(['action' => 'index']);
    }
}

谢谢你的帮助。

wvmv3b1j

wvmv3b1j1#

如果你真的不希望这种“* 在页面之间共享图像 *”的行为,那么你需要想出一个不同的命名/存储方案。例如,你可以使用UUID,重复的机会非常非常非常小(假设PHP使用的伪随机数生成器是好的)。
最重要的是,在保存文件名的数据库列上放置一个唯一的索引,并且只有在记录成功保存后才上传文件,这有助于避免PHP级别的争用条件问题。
当然,为了最大限度地减少代码在第一时间存储重复项的可能性,实现某种类型的重复检查策略也没有什么坏处,例如检查名称是否已经存在,如果存在,则会出错,并且可能会尝试先多次生成一个唯一的名称。为了进一步减少冲突的可能性,可以使用名称随时间变化的子文件夹。例如使用年和月。
快速和肮脏的例子:

// group by year and month
$base = WWW_ROOT . 'img' . DS . 'pages' . DS;
$group = gmdate('Y-m') . DS;

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

// try to generate unique filename max 10 times
$uniqueFilenameFound = false;
for ($i = 0; $i < 10; $i ++) {
    $filename = \Cake\Utility\Text::uuid();
    if (!file_exists($base . $group . $filename)) {
        $uniqueFilenameFound = true;
        break;
    }
}

// set field if unique name was found, otherwise set an error
if ($uniqueFilenameFound) {
    $page->image = $group . $filename;
} else {
    $page->setError('image_file', 'Could not generate a unique filename.');
}

// ...

// move file only after successful saving that ensures the name
// is really unique
if ($this->Pages->save($page))
{
    $image->moveTo($base . $group . $filename);

    // ...
}

尽管如此,您还应该考虑将此功能移到一个服务、一个behavior或表本身中。在**afterSave事件**中处理上载将允许在移动文件失败时自动回滚(这将触发异常)!

相关问题