Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

File uploads

A file upload is a form with a file field plus code that moves the submitted file to a permanent folder. In CakePHP an uploaded file arrives in the action as an object with methods for its name, size and moving.

Preparation

Create the webroot/uploads folder by hand; everything inside webroot is reachable by the site address. The file name is stored in the cover column of the articles table:

bin/cake bake migration AddCoverToArticles cover:string
bin/cake migrations migrate

The form

<div>
    <?= $this->Form->create($article, ['type' => 'file']) ?>
    <?= $this->Form->control('title') ?>
    <?= $this->Form->control('cover_file', ['type' => 'file', 'label' => 'Cover']) ?>
    <?= $this->Form->button('Save') ?>
    <?= $this->Form->end() ?>
</div>

The 'type' => 'file' option in create() is required: without it the browser sends only the file name. The field is called cover_file, not cover, so that the file object does not end up in the entity instead of a string.

Getting the file

// src/Controller/ArticlesController.php
$file = $this->request->getUploadedFile('cover_file');
 
if ($file !== null && $file->getError() === UPLOAD_ERR_OK) {
    $name = bin2hex(random_bytes(16)) . '.jpg';
 
    $file->moveTo(WWW_ROOT . 'uploads' . DS . $name);
 
    $article->cover = $name;
}

This code goes into the add() action after patchEntity() and before save(). getUploadedFile() returns the file object, moveTo() moves it to webroot/uploads, and the name is written to the cover column. The WWW_ROOT constant is the path to the webroot folder.

File methods

MethodWhat it returns
getClientFilename()The file name on the visitor's computer
getSize()The size in bytes
getClientMediaType()The type reported by the browser
getError()An error code, UPLOAD_ERR_OK on success
moveTo($path)Moves the file

Checking

Before saving a file, check its extension and size:

$extension = strtolower(pathinfo($file->getClientFilename(), PATHINFO_EXTENSION));
 
if (!in_array($extension, ['jpg', 'jpeg', 'png'], true)) {
    $this->Flash->error('Upload a JPG or PNG image.');
} elseif ($file->getSize() > 2 * 1024 * 1024) {
    $this->Flash->error('The file is larger than 2 MB.');
} else {
    $name = bin2hex(random_bytes(16)) . '.' . $extension;
    $file->moveTo(WWW_ROOT . 'uploads' . DS . $name);
    $article->cover = $name;
}

A file with another extension or one that is too large is not saved, and the visitor sees a message.

Showing the image

<div>
    <?php if ($article->cover): ?>
        <?= $this->Html->image('/uploads/' . $article->cover, ['alt' => $article->title]) ?>
    <?php endif; ?>
</div>

A path that starts with a slash is counted by the Html helper from the webroot folder. The cover appears on the article page.

Common mistake

Saving a file under the name sent by the visitor is dangerous: the name may contain ../ or the .php extension, and someone else's executable code ends up on the server. Always generate the name yourself as a random string and take only a checked extension from the allowed list.

Thanks for reading!

Donate to the site — we work hard for you.