File uploads
A post cover, an avatar, a document — files from visitors arrive together with a form. In CodeIgniter an uploaded file is an object: you get it from the request, check it with rules and move it to a storage folder. Public files go to public/uploads, private ones to writable/uploads.
The form
<!-- app/Views/posts/cover.php -->
<?= form_open_multipart('posts/cover') ?>
<input type="file" name="cover">
<button type="submit">Upload</button>
<?= form_close() ?>form_open_multipart() prints the form tag with the enctype="multipart/form-data" attribute. Without it the file never reaches the server.
Routes
// app/Config/Routes.php
$routes->get('posts/cover', 'Posts::cover');
$routes->post('posts/cover', 'Posts::upload');The cover() method only outputs the view: return view('posts/cover');.
Receiving and saving
// app/Controllers/Posts.php
public function upload()
{
$file = $this->request->getFile('cover');
if (! $file->isValid()) {
return redirect()->back()->with('message', $file->getErrorString());
}
$name = $file->getRandomName();
$file->move(FCPATH . 'uploads', $name);
return redirect()->back()->with('message', "File saved: $name");
}getFile('cover') returns the file object by the field name. getRandomName() creates a random name with the original extension, move() moves the file. FCPATH is the path to the public folder, so the file lands in public/uploads. Choose an image and submit the form — a file like 1709371200_a3f9c2.jpg appears in the folder.
Validation
Before saving, the file is checked with rules like ordinary fields. Add this to the beginning of the upload() method:
$rules = [
'cover' => 'uploaded[cover]|is_image[cover]|max_size[cover,2048]|ext_in[cover,jpg,jpeg,png,webp]',
];
if (! $this->validate($rules)) {
return redirect()->back()->withInput();
}Now a text file or an image larger than two megabytes is rejected.
| Rule | What it checks |
|---|---|
uploaded[cover] | The file was really uploaded |
max_size[cover,2048] | The size is at most 2048 KB |
is_image[cover] | The file is an image |
ext_in[cover,jpg,png] | The extension is in the list |
mime_in[cover,image/png,image/jpeg] | The content type is in the list |
Showing the image
The file name is saved in the post record, and the view builds the address with base_url():
<img src="<?= base_url('uploads/' . esc($post['cover'], 'url')) ?>" alt="Cover">File methods
| Method | What it returns |
|---|---|
getClientName() | The file name on the visitor's computer |
getRandomName() | A random name with the extension |
getMimeType() | The content type |
getSizeByUnit('kb') | The size in kilobytes |
isValid() | The upload finished without errors |
store('invoices') | Saves the file to writable/uploads/invoices |
Files in writable/uploads cannot be reached by a direct address. A controller serves them after checking permissions: return $this->response->download($path, null);.
Common mistake
Saving a file under the visitor's name — getClientName() — is dangerous: the name may be shell.php or contain ../. A file with the .php extension in the public folder is executed by the server as a program. Create the name with getRandomName(), limit the extensions with the ext_in rule and check the type by content with is_image or mime_in.