Skip to main content
RESTful Principles
CHAPTER 22 Beginner

File Upload APIs

Updated: May 13, 2026
5 min read

# CHAPTER 22

File Upload APIs

1. Introduction

Handling text in an API is easy. Handling a 5MB image file is a different story. REST APIs frequently need to accept file uploads—such as profile pictures, PDF documents, or CSV imports. In Chapter 22, we will step away from application/json and learn how to use multipart/form-data to securely accept, validate, and store files on your PHP server via an API endpoint.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Understand the multipart/form-data Content-Type.
  • Process incoming file uploads using the PHP $_FILES superglobal.
  • Validate file types (MIME types) and file sizes securely.
  • Generate unique filenames to prevent overwriting existing files.
  • Return the URL of the newly uploaded file in the JSON response.

3. Beginner-Friendly Explanation

Imagine sending a letter (JSON) versus mailing a large package (a File). When you mail a letter, it goes in a standard envelope. But a package requires a box, special shipping labels, and a different handling process.

In HTTP, JSON is the letter. A file upload is the package. To send a package, the client must change the Content-Type header to multipart/form-data. When the PHP server receives this package, it puts it in a temporary holding room (/tmp). Your PHP script must examine the package, make sure it isn't dangerous (like a bomb/virus), and then move it to a permanent storage shelf (/uploads folder).

4. Real-World Examples

  • Instagram API: When you upload a photo, the app sends the binary image data to the server. The server saves the image to an Amazon S3 bucket and saves the *URL of the image* in the MySQL database.
  • Slack API: Uploading a PDF document to a chat channel.

5. Detailed Code Examples

Here is a secure file upload endpoint in PHP.
php
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
<?php
// Endpoint: POST /api/upload-avatar

// 1. Check if a file was actually uploaded
if (!isset($_FILES[&#039;avatar']) || $_FILES['avatar']['error'] !== UPLOAD_ERR_OK) {
    http_response_code(400);
    echo json_encode(["error" => "No file uploaded or upload error occurred."]);
    exit;
}

$file = $_FILES[&#039;avatar'];

// 2. Validate File Size (e.g., max 2MB)
if ($file[&#039;size'] > 2 * 1024 * 1024) {
    http_response_code(400);
    echo json_encode(["error" => "File too large. Maximum size is 2MB."]);
    exit;
}

// 3. Validate MIME Type (Never trust the file extension!)
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $file[&#039;tmp_name']);
finfo_close($finfo);

$allowedMimes = [&#039;image/jpeg', 'image/png', 'image/gif'];
if (!in_array($mimeType, $allowedMimes)) {
    http_response_code(400);
    echo json_encode(["error" => "Invalid file type. Only JPG, PNG, and GIF are allowed."]);
    exit;
}

// 4. Generate a unique filename and move the file
$extension = pathinfo($file[&#039;name'], PATHINFO_EXTENSION);
$newName = uniqid(&#039;avatar_', true) . '.' . $extension;
$destination = __DIR__ . &#039;/uploads/' . $newName;

if (move_uploaded_file($file[&#039;tmp_name'], $destination)) {
    // 5. Success! Return the URL
    $fileUrl = "https://api.example.com/uploads/" . $newName;
    http_response_code(201);
    echo json_encode([
        "message" => "File uploaded successfully",
        "url" => $fileUrl
    ]);
} else {
    http_response_code(500);
    echo json_encode(["error" => "Failed to save file."]);
}
?>

6. Request/Response Examples

To test this in Postman:
  1. 1. Method: POST
  1. 2. Body Tab -> Select form-data
  1. 3. Key: avatar (Change type from Text to File)
  1. 4. Value: Select an image from your computer.

Response:

json
1234
{
  "message": "File uploaded successfully",
  "url": "https://api.example.com/uploads/avatar_60a8f9b9a1.jpg"
}

7. HTTP Examples

When a client sends a file, the request body looks completely different from JSON. It uses boundaries to separate the form fields.
http
12345678910
POST /api/upload-avatar HTTP/1.1
Host: api.example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="avatar"; filename="my_photo.jpg"
Content-Type: image/jpeg

[... RAW BINARY IMAGE DATA HERE ...]
------WebKitFormBoundary7MA4YWxkTrZu0gW--

8. JSON Examples

Often, you need to send text data *alongside* a file (e.g., uploading a photo AND setting a caption). You have two choices:
  1. 1. Send everything as multipart/form-data (PHP will put the text in $POST and the file in $FILES).
  1. 2. Do it in two steps: Upload the file first (returns the URL), then send a standard JSON POST request containing the URL and the text caption. (This is the preferred modern approach).

9. Best Practices

  • Never trust the extension: A hacker can rename virus.php to virus.jpg and bypass extension checks. Always use PHP's finfofile() to read the actual binary signature (MIME type) of the file.
  • Rename files immediately: Never save the file using the user's original filename ($file['name']). It might contain special characters, SQL injections, or overwrite existing files. Always generate a unique hash (like uniqid()) for the saved filename.
  • Keep uploads outside public root (Optional but highly secure): For sensitive files (like PDFs), save them in a folder that the web server cannot access directly. Serve them via a protected PHP script that checks authentication before outputting the file.

10. Common Mistakes

  • Expecting JSON: Beginners try to send {"avatar": "C:\images\photo.jpg"} in a JSON payload. This does not work. JSON can only send text. To send a file in JSON, it must be converted to a massive Base64 text string (which is very inefficient and increases file size by 33%).
  • uploadmaxfilesize in php.ini: PHP has a strict server-level limit on upload sizes (usually 2MB by default). If a user uploads a 5MB file, $FILES will be empty and PHP will throw a silent error unless you change your php.ini configuration.

11. Mini Exercises

  1. 1. If you are uploading a CSV file, what Content-Type header must your API client use?
*(Answer: multipart/form-data)*

12. Coding Challenges

Challenge 1: Write a PHP script that checks $FILES['document']['error']. If the error code equals UPLOADERRINISIZE, return a JSON response explaining that the file exceeds the server's php.ini limits.

13. MCQs with Answers

Question 1

Which superglobal array in PHP holds the data for uploaded files?

Question 2

Why is checking the file extension (e.g., .jpg) NOT a secure way to validate an image?

Question 3

How do you return the uploaded file to the client in the API response?

14. Interview Questions

  • Q: Describe the steps required to securely handle a file upload in a PHP API.
  • Q: Explain the difference between application/json and multipart/form-data.
  • Q: How do you securely validate that an uploaded file is truly an image and not a malicious script?

15. FAQs

Q: Should I save images directly into the MySQL database as a BLOB? A: No! This is a massive anti-pattern. Storing binary files in a database makes the database huge, slow, and expensive to back up. Always save the file to the file system (or AWS S3) and save the *URL string* in the database.

16. Summary

In Chapter 22, we tackled File Uploads. We learned that file uploads require the multipart/form-data content type, which populates the $_FILES array in PHP. We emphasized extreme security: verifying MIME types using finfo, checking file sizes, and dynamically renaming files using uniqid() before moving them to permanent storage.

17. Next Chapter Recommendation

Our API is powerful, but what happens when we need to change how it works without breaking the mobile apps currently using it? Proceed to Chapter 23: API Versioning Strategies to learn how to future-proof your architecture.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·