Aim
To create a web page that provides file uploading and file downloading using PHP.
Theory
File upload needs two cooperating pieces. The form must declare method="post" and enctype="multipart/form-data" — without that encoding the browser sends only the file's name, never its bytes. On the server, PHP exposes each uploaded file through the $_FILES superglobal: $_FILES["myfile"] is an array with five keys — name (original filename), type (browser-claimed MIME type), tmp_name (where PHP parked the bytes), error (0 = UPLOAD_ERR_OK) and size (bytes).
PHP deliberately quarantines every upload in a temporary directory and deletes it when the request ends; the script must claim it with move_uploaded_file(), which also verifies the source really is an HTTP upload. Size is capped by two php.ini directives: upload_max_filesize (one file) and post_max_size (the whole POST body, so it must be at least as large).
Downloading is the reverse trick: before any output the script sends Content-Type: application/octet-stream and Content-Disposition: attachment; filename="..." headers — the second forces the browser's Save dialog — then streams the bytes with readfile() and exits so no HTML corrupts the stream.
Requirements
- XAMPP/WAMP with Apache and PHP 8.x (or PHP CLI for the simulated run)
- Code editor (VS Code); browser (Chrome/Edge)
Procedure
- Start Apache from the XAMPP Control Panel.
- Save the snippet as
p21_upload_download.phpinC:\xampp\htdocs\wbplab. - Open
http://localhost/wbplab/p21_upload_download.php— a file chooser and an Upload button appear. - Pick a small file and submit; on success a Download Uploaded File link appears — clicking it makes the browser save the same file back.
- Run
php p21_upload_download.phpin a terminal: with no browser available, the compiler/CLI branch demonstrates the workflow using local temporary files — it writes a demo file and prints usage hints instead of rendering the form.
Explanation of the Code
$uploadDiris built fromsys_get_temp_dir()pluswbp_uploadsand created on first run withmkdir()— the system temp dir keeps the demo portable (a real site would use anuploads/folder insidehtdocs).php_sapi_name() === "cli"selects the compiler branch:file_put_contents()fakes an upload by writingsample_upload.txt, then its path and two hint lines are printed.- Upload half (web): on a POST with
$_FILES["myfile"]set,basename()strips any directory part from the client-supplied name (blocking../path traversal) andmove_uploaded_file()moves the quarantinedtmp_nameto$target. Success echoes a link whose?download=query carries the filename throughurlencode(). - Download half: when
$_GET["download"]is present the script re-appliesbasename(), checksfile_exists(), emits the two download headers, streams the file withreadfile()and callsexitimmediately. - Finally the form itself is echoed — note the
enctype='multipart/form-data'attribute, without which$_FILESwould stay empty.
Expected Output
In the browser the page first shows only a file input and an Upload button. After a successful upload it prints File uploaded successfully. followed by the Download Uploaded File link; clicking the link downloads that exact file (no page renders, because the script exits after readfile()). A failed move prints Upload failed. instead. Run from the CLI/compiler, it prints CLI Demo Upload Path: with the temp-folder path of sample_upload.txt, plus the hints about using <input type='file' name='myfile'> and the ?download=filename query string.
🎯 Viva Questions
- Why is
enctype="multipart/form-data"compulsory for uploads? It makes the browser encode the request in parts that carry the raw file bytes; the default encoding sends only the filename text. - Name the five keys of
$_FILES["myfile"].name,type,tmp_name,errorandsize. - Why does PHP put uploads in a temp directory first? As a quarantine — the file is discarded at request end unless the script explicitly accepts it with
move_uploaded_file(). - Which
php.inidirectives limit upload size?upload_max_filesizeandpost_max_size; the second covers the whole request body and must be ≥ the first. - Which header forces a download instead of inline display?
Content-Disposition: attachment; filename="...". - Why call
basename()on both the uploaded and requested names? To strip path components, preventing directory-traversal attacks like?download=../../config.php.
CO Mapping
CO1, CO2