Pragmatism in the real world

File uploads with Slim 4, tested with curl

Unsurprisingly, uploading files with Slim 4 is pretty much the same as for Slim 3 as they are both use PSR-7 for Requests.

Recently, Matthew asked a question about why he was getting an error, so I looked into it. One thing that’s really nice about Slim is that you can write a complete application in a single file (+ the vendor directory) which is helpful for testing things. Here’s the entire application:

public/index.php:

<?php

declare(strict_types=1);

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\Factory\AppFactory;

require __DIR__ . '/../vendor/autoload.php';

// Create app
$app = AppFactory::create();

// Register middleware
$app->addRoutingMiddleware();
$app->addBodyParsingMiddleware();
$app->addErrorMiddleware(true, true, true);

$app->post('/upload', function (
    ServerRequestInterface $request,
    ResponseInterface $response
): ResponseInterface {
    $uploadedFiles = $request->getUploadedFiles();
    $uploadedFile = $uploadedFiles['file'];

    if ($uploadedFile->getError() === UPLOAD_ERR_OK) {
        $filename = $uploadedFile->getClientFilename();
        $uploadedFile->moveTo(__DIR__ . "/../uploads/$filename");
        $response->getBody()->write("uploaded $filename");
    } else {
        $response->getBody()->write('error: ' . $uploadedFile->getError());
    }

    return $response;
});

// Run app
$app->run();

I created the project using composer create-project akrabat/slim4-empty test-uploads which sets up the composer.json for me along with creating an initial public/index.php that I tweaked to make the above file.

To run the app, I used the built-in PHP web server: php -d -S 0.0.0.0:8888 -t public/

Uploading files with curl

To test this, I just used curl! To upload a file in curl, you use the --form/-F parameter to upload as multipart/form-data and then prepend the data value with @ character before the filename to upload the file:

 curl -i --form "file=@car1.jpg" http://localhost:8888/upload -H "Accept: application/json"

Note that I set the Accept header to accept JSON as Slim’s error handling will output HTML unless you tell it that you can read JSON or XML. As I’m on the command line, JSON is much easier to read than HTML!

All is good:

$ curl -i --form "file=@car1.jpg" http://localhost:8888/upload -H "Accept: application/json"
HTTP/1.1 200 OK
Host: localhost:8888
Date: Mon, 12 Aug 2024 09:16:44 GMT
Connection: close
X-Powered-By: PHP/8.3.9
Content-type: text/html; charset=UTF-8

uploaded car1.jpg

Then I tested a larger file:

$ curl -i --form "file=@IMG_1151.jpeg" http://localhost:8888/upload -H "Accept: application/json"
HTTP/1.1 200 OK
Host: localhost:8888
Date: Mon, 12 Aug 2024 09:18:00 GMT
Connection: close
X-Powered-By: PHP/8.3.9
Content-type: text/html; charset=UTF-8

error: 1

Error 1 means that the file is too large, so I need to set upload_max_filesize and post_max_size. These are INI_SYSTEM scoped configuration settings for PHP, so we can’t use ini_set(), so we either set in php.ini, or on the command line:

php -d upload_max_filesize=10M -d post_max_size=10M  -S 0.0.0.0:8888 -t public/

Now it works.