Repository

Looks good to me!

User Tools

Site Tools


topic:backend:php:fileupload

File upload exercise

Created a sample upload form using PHP as an exercise, and as a reliable way of transferring files. The go-to documentation page for POST file uploading can be found here.

File requests can either be individual files, or even directories (but either/or for plain PHP). The multiple keyword can be specified for several files/directories.

<form action="upload.php" method="post" enctype="multipart/form-data">
  <input type="submit" name="submit" value="Upload">
  <input type="file" multiple name="payload[]" value="Choose files" id="payload">
  # replace 'multiple' with 'webkitdirectory directory multiple' for directory upload
</form>

Of note is the PHP initialization file php.ini, which dictates the fallback temporary directory and max upload size. This can be found in /etc/php/[VERSION]/fpm/php.ini. Documentation on these variables are found here. Need to ensure sufficient memory is allocated to the PHP script, on top of manual uploading limits.

; Default values in php.ini
memory_limit = 128M
post_max_size = 8M
upload_max_filesize = 2M

See this comment on defensive coding prior to the use of move_uploaded_file.

Implementation

The upload page should first contain the required form as above, with the action field pointing along a relative path to the PHP script that processes the form data. The PHP script can also point to itself, since the POST method distinguishes it from the GET method when the upload page is first loaded.

Behind nginx, the temporary upload directory can be specified, typically /tmp. The files prompted by the user are uploaded to the temporary directory, and a status code is emitted for each upload attempt:

# Fail gracefully if got error
$error_code = $payload["error"];
if ($error_code != 0) {
    echo "\u{2717} \"" . htmlspecialchars($target_name) . "\" ";
    switch ($error_code) {
      case 1:
      case 2:
        echo nl2br("(file too large)\n");
        break;
      case 3:
        echo nl2br("(file transfer interrupted)\n");
        break;
      case 4:
        echo nl2br("(no file specified)\n");
        break;
      case 7:
        echo nl2br("(copy failed)\n");
        break;
      default:
        echo nl2br("(unknown error)\n");
        break;
    }
 
# File uploaded to /tmp without issues
} else {
    $tmp_target = $payload["tmp_name"];
    if (move_uploaded_file($tmp_target, $target_dir . $target_name)) {
        echo nl2br("\u{2713} \"" . htmlspecialchars($target_name) . "\"\n");
    }
}

A complete implementation of the upload script upload.php thus may look like this:

<!doctype html>
<html>
<head></head>
<body>
  <h1>Upload form</h1>
  <form action="upload.php" method="post" enctype="multipart/form-data">
    <input type="submit" name="submit" value="Upload">
    <input type="file" name="thefile" value="Choose files" id="payload">
  </form>
  <?php
    $target_dir = "/var/www/uploads";
    $file = $_FILES["thefile"];
 
    # Process only if is POST-submitted form
    if (isset($_POST["submit"])) {
      echo "<hr><h2>Upload result:</h2>";
      $error_code = $file["error"]; # check for errors
      $target_name = basename($file["name"]);
      $tmp_target = $file["tmp_name"];
      if (move_uploaded_file($tmp_target, $target_dir . $target_name)) {
        echo nl2br("\u{2713} \"" . htmlspecialchars($target_name) . "\"\n");
      } else {
        echo nl2br("\u{2717} \"" . htmlspecialchars($target_name) . "\" (failed to move file)\n");
      }
    }
  ?>
</body>
</html>
topic/backend/php/fileupload.txt · Last modified: 24 months ago ( 2 May 2023) by 127.0.0.1