Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 179 additions & 24 deletions user_guide_src/source/libraries/uploaded_files.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
***************************
###########################
Working with Uploaded Files
***************************
###########################

CodeIgniter makes working with files uploaded through a form much simpler and more secure than using PHP's ``$_FILES``
array directly. This extends the :doc:`File class </libraries/files>` and thus gains all of the features of that class.
Expand All @@ -12,12 +12,166 @@ array directly. This extends the :doc:`File class </libraries/files>` and thus g
:local:
:depth: 2

===============
***********
The Process
***********

Uploading a file involves the following general process:

- An upload form is displayed, allowing a user to select a file and
upload it.
- When the form is submitted, the file is uploaded to the destination
you specify.
- Along the way, the file is validated to make sure it is allowed to be
uploaded based on the preferences you set.
- Once uploaded, the user will be shown a success message.

To demonstrate this process here is brief tutorial. Afterward you'll
find reference information.

Creating the Upload Form
========================

Using a text editor, create a form called upload_form.php. In it, place
this code and save it to your **app/Views/** directory::

<!DOCTYPE html>
<html lang="en">
<head>
<title>Upload Form</title>
</head>
<body>

<?php foreach ($errors as $error): ?>
<li><?= esc($error) ?></li>
<?php endforeach ?>

<?= form_open_multipart('upload/upload') ?>

<input type="file" name="userfile" size="20" />

<br /><br />

<input type="submit" value="upload" />

</form>

</body>
</html>

You'll notice we are using a form helper to create the opening form tag.
File uploads require a multipart form, so the helper creates the proper
syntax for you. You'll also notice we have an ``$errors`` variable. This is
so we can show error messages in the event the user does something
wrong.

The Success Page
================

Using a text editor, create a form called upload_success.php. In it,
place this code and save it to your **app/Views/** directory::

<!DOCTYPE html>
<html lang="en">
<head>
<title>Upload Form</title>
</head>
<body>

<h3>Your file was successfully uploaded!</h3>

<ul>
<li>name: <?= esc($uploaded_flleinfo->getBasename()) ?></li>
<li>size: <?= esc($uploaded_flleinfo->getSizeByUnit('kb')) ?> KB</li>
<li>extension: <?= esc($uploaded_flleinfo->guessExtension()) ?></li>
</ul>

<p><?= anchor('upload', 'Upload Another File!') ?></p>

</body>
</html>

The Controller
==============

Using a text editor, create a controller called Upload.php. In it, place
this code and save it to your **app/Controllers/** directory::

<?php

namespace App\Controllers;

use CodeIgniter\Files\File;

class Upload extends BaseController
{
protected $helpers = ['form'];

public function index()
{
return view('upload_form', ['errors' => []]);
}

public function upload()
{
$validationRule = [
'userfile' => [
'label' => 'Image File',
'rules' => 'uploaded[userfile]'
. '|is_image[userfile]'
. '|mime_in[userfile,image/jpg,image/jpeg,image/gif,image/png,image/webp]'
. '|max_size[userfile,100]'
. '|max_dims[userfile,1024,768]',
],
];
if (! $this->validate($validationRule)) {
$data = ['errors' => $this->validator->getErrors()];

return view('upload_form', $data);
}

$img = $this->request->getFile('userfile');

if (! $img->hasMoved()) {
$filepath = WRITEPATH . 'uploads/' . $img->store();

$data = ['uploaded_flleinfo' => new File($filepath)];

return view('upload_success', $data);
} else {
$data = ['errors' => 'The file has already been moved.'];

return view('upload_form', $data);
}
}
}

.. note:: Since the value of a file upload HTML field doesn't exist, and is stored in the ``$_FILES`` global,
only :ref:`rules-for-file-uploads` can be used to validate upload file with :doc:`validation`.
The rule ``required`` also can't be used, so use ``uploaded`` instead.

The Upload Directory
====================

The uploaded files are stored in the **writable/uploads/** directory.

Try it!
=======

To try your form, visit your site using a URL similar to this one::

example.com/index.php/upload/

You should see an upload form. Try uploading an image file (either a
**jpg**, **gif**, **png**, or **webp**). If the path in your controller is correct it should
work.

***************
Accessing Files
===============
***************

All Files
----------
=========

When you upload files they can be accessed natively in PHP through the ``$_FILES`` superglobal. This array has some
major shortcomings when working with multiple files uploaded at once, and has potential security flaws many developers
Expand Down Expand Up @@ -72,7 +226,7 @@ In this case, the returned array of files would be more like::
]

Single File
-----------
===========

If you just need to access a single file, you can use ``getFile()`` to retrieve the file instance directly. This will return an instance of ``CodeIgniter\HTTP\Files\UploadedFile``:

Expand Down Expand Up @@ -110,14 +264,14 @@ In controller::
foreach($imagefile['images'] as $img) {
if ($img->isValid() && ! $img->hasMoved()) {
$newName = $img->getRandomName();
$img->move(WRITEPATH.'uploads', $newName);
$img->move(WRITEPATH . 'uploads', $newName);
}
}
}

where the **images** is a loop from the form field name

If there are multiple files with the same name you can use ``getFile()`` ro retrieve every file individually::
If there are multiple files with the same name you can use ``getFile()`` to retrieve every file individually::
In controller::

$file1 = $this->request->getFile('images.0');
Expand All @@ -138,29 +292,29 @@ In controller::
$file1 = $this->request->getFile('my-form.details.avatars.0');
$file2 = $this->request->getFile('my-form.details.avatars.1');

.. note:: using ``getFiles()`` is more appropriate
.. note:: Using ``getFiles()`` is more appropriate.

=====================
*********************
Working With the File
=====================
*********************

Once you've retrieved the UploadedFile instance, you can retrieve information about the file in safe ways, as well as
move the file to a new location.

Verify A File
-------------
=============

You can check that a file was actually uploaded via HTTP with no errors by calling the ``isValid()`` method::

if (! $file->isValid()) {
throw new \RuntimeException($file->getErrorString().'('.$file->getError().')');
throw new \RuntimeException($file->getErrorString() . '(' . $file->getError() . ')');
}

As seen in this example, if a file had an upload error, you can retrieve the error code (an integer) and the error
message with the ``getError()`` and ``getErrorString()`` methods. The following errors can be discovered through
this method:

* The file exceeds your upload_max_filesize ini directive.
* The file exceeds your ``upload_max_filesize`` ini directive.
* The file exceeds the upload limit defined in your form.
* The file was only partially uploaded.
* No file was uploaded.
Expand All @@ -169,7 +323,7 @@ this method:
* File upload was stopped by a PHP extension.

File Names
----------
==========

**getName()**

Expand All @@ -192,15 +346,16 @@ To get the full path of the temp file that was created during the upload, you ca
$tempfile = $file->getTempName();

Other File Info
---------------
===============

**getClientExtension()**

Returns the original file extension, based on the file name that was uploaded. This is NOT a trusted source. For a
trusted version, use ``guessExtension()`` instead::
Returns the original file extension, based on the file name that was uploaded::

$ext = $file->getClientExtension();

.. warning:: This is NOT a trusted source. For a trusted version, use ``guessExtension()`` instead.

**getClientMimeType()**

Returns the mime type (mime type) of the file as provided by the client. This is NOT a trusted value. For a trusted
Expand All @@ -211,17 +366,17 @@ version, use ``getMimeType()`` instead::
echo $type; // image/png

Moving Files
------------
============

Each file can be moved to its new location with the aptly named ``move()`` method. This takes the directory to move
the file to as the first parameter::

$file->move(WRITEPATH.'uploads');
$file->move(WRITEPATH . 'uploads');

By default, the original filename was used. You can specify a new filename by passing it as the second parameter::

$newName = $file->getRandomName();
$file->move(WRITEPATH.'uploads', $newName);
$file->move(WRITEPATH . 'uploads', $newName);

Once the file has been removed the temporary file is deleted. You can check if a file has been moved already with
the ``hasMoved()`` method, which returns a boolean::
Expand All @@ -237,15 +392,15 @@ Moving an uploaded file can fail, with an HTTPException, under several circumsta
- the file move operation fails (e.g., improper permissions)

Store Files
------------
===========

Each file can be moved to its new location with the aptly named ``store()`` method.

With the simplest usage, a single file might be submitted like::

<input type="file" name="userfile" />

By default, upload files are saved in writable/uploads directory. The YYYYMMDD folder
By default, upload files are saved in **writable/uploads** directory. The **YYYYMMDD** folder
and random file name will be created. Returns a file path::

$path = $this->request->getFile('userfile')->store();
Expand All @@ -255,7 +410,7 @@ passing it as the second parameter::

$path = $this->request->getFile('userfile')->store('head_img/', 'user_name.jpg');

Moving an uploaded file can fail, with an HTTPException, under several circumstances:
Moving an uploaded file can fail, with an ``HTTPException``, under several circumstances:

- the file has already been moved
- the file did not upload successfully
Expand Down
4 changes: 3 additions & 1 deletion user_guide_src/source/libraries/validation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -892,11 +892,13 @@ valid_cc_number Yes Verifies that the credit card number matches
HSBC Canada Card (hsbc)
======================= ========== ============================================= ===================================================

.. _rules-for-file-uploads:

Rules for File Uploads
======================

These validation rules enable you to do the basic checks you might need to verify that uploaded files meet your business needs.
Since the value of a file upload HTML field doesn't exist, and is stored in the $_FILES global, the name of the input field will
Since the value of a file upload HTML field doesn't exist, and is stored in the ``$_FILES`` global, the name of the input field will
need to be used twice. Once to specify the field name as you would for any other rule, but again as the first parameter of all
file upload related rules::

Expand Down