PSR-7 file uploads in Slim 3
Handling file uploads in Slim 3 is reasonably easy as it uses the PSR-7 Request object, so let’s take a look.
The easiest way to get a Slim framework project up and running is to use the Slim-Skeleton to create a project:
composer create-project slim/slim-skeleton slim3-file-uploads
and then you can cd into the directory and run the PHP built-in web server using:
php -S 0.0.0.0:8888 -t public public/index.php
Displaying the form
We can now create a simple form, firstly by setting up the / route in src/routes.php:
$app->get('/', function ($request, $response, $args) { // Render file upload form return $this->renderer->render($response, 'index.phtml', $args); });
The view script, templates/index.phtml contains the form:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Slim 3</title> <link rel="stylesheet" href="http://yegor256.github.io/tacit/tacit.min.css"> </head> <body> <h1>Upload a file</h1> <form method="POST" action="/upload" enctype="multipart/form-data"> <label>Select file to upload:</label> <input type="file" name="newfile"> <button type="submit">Upload</button> </form> </body> </html>
Handling the upload
We now need to write the route that handles the uploaded file. This goes in src/routes.php:
$app->post('/upload', function ($request, $response, $args) { $files = $request->getUploadedFiles(); if (empty($files['newfile'])) { throw new Exception('Expected a newfile'); } $newfile = $files['newfile']; // do something with $newfile });
The file upload in $_FILES is available from the $request‘s getUploadedFiles() method. This returns an array keyed by the name of the <input> element. In this case, that’s newfile.
The $newfile object is a instance of PSR-7’s UploadedFileInterface. Typical usage is to check that there is no error and then move the file to somewhere else. This is done like this:
if ($newfile->getError() === UPLOAD_ERR_OK) { $uploadFileName = $newfile->getClientFilename(); $newfile->moveTo("/path/to/$uploadFileName"); }
There’s also other useful methods such as getClientMediaType() and getSize() if you need them.
Conclusion
As you can see, dealing with file uploads within a PSR-7 request is really easy!
Just in case, someone want to upload their file to other services, let say Cloudinary, etc. They can simply use `$newFile->file´ property to get the content of `$_FILES['…']['tmpname']´
Can you describe how to I can get the content?
I used $newFile->file(); it throw error.
Thanks for example.
I think $this->file in Slim\Http\UploadedFile.php should be protected and public method e.g getUploadedPath() { return $this->file; } should be added.
Agree. $file is a dynamic property, public as such, and it's a smell.
Uploads are not that easy. I suggest using a library like this https://github.com/siriusphp/upload (disclaimer: I am the author)
Hi Rob,
Nice article, however I get the following error when the moveTo is called:
{
"message": "Slim Application Error",
"exception": [
{
"type": "RuntimeException",
"code": 0,
"message": "Could not write to stream",
"file": "\/home\/johan\/web\/skeleton\/vendor\/slim\/slim\/Slim\/Http\/Stream.php",
"line": 384,
"trace": [
"#0 \/home\/johan\/web\/skeleton\/vendor\/slim\/slim\/Slim\/Http\/Response.php(258): Slim\\Http\\Stream->write(Array)",
"#1 \/home\/johan\/web\/skeleton\/src\/routes.php(142): Slim\\Http\\Response->write(Array)",
"#2 [internal function]: Closure->{closure}(Object(Slim\\Http\\Request), Object(Slim\\Http\\Response), Array)",
"#3 \/home\/johan\/web\/skeleton\/vendor\/slim\/slim\/Slim\/Handlers\/Strategies\/RequestResponse.php(41): call_user_func(Object(Closure), Object(Slim\\Http\\Request), Object(Slim\\Http\\Response), Array)",
"#4 \/home\/johan\/web\/skeleton\/vendor\/slim\/slim\/Slim\/Route.php(325): Slim\\Handlers\\Strategies\\RequestResponse->__invoke(Object(Closure), Object(Slim\\Http\\Request), Object(Slim\\Http\\Response), Array)",
"#5 \/home\/johan\/web\/skeleton\/vendor\/slim\/slim\/Slim\/MiddlewareAwareTrait.php(116): Slim\\Route->__invoke(Object(Slim\\Http\\Request), Object(Slim\\Http\\Response))",
"#6 \/home\/johan\/web\/skeleton\/vendor\/slim\/slim\/Slim\/Route.php(297): Slim\\Route->callMiddlewareStack(Object(Slim\\Http\\Request), Object(Slim\\Http\\Response))",
"#7 \/home\/johan\/web\/skeleton\/vendor\/slim\/slim\/Slim\/App.php(434): Slim\\Route->run(Object(Slim\\Http\\Request), Object(Slim\\Http\\Response))",
"#8 \/home\/johan\/web\/skeleton\/vendor\/slim\/slim\/Slim\/MiddlewareAwareTrait.php(116): Slim\\App->__invoke(Object(Slim\\Http\\Request), Object(Slim\\Http\\Response))",
"#9 \/home\/johan\/web\/skeleton\/vendor\/slim\/slim\/Slim\/App.php(341): Slim\\App->callMiddlewareStack(Object(Slim\\Http\\Request), Object(Slim\\Http\\Response))",
"#10 \/home\/johan\/web\/skeleton\/vendor\/slim\/slim\/Slim\/App.php(302): Slim\\App->process(Object(Slim\\Http\\Request), Object(Slim\\Http\\Response))",
"#11 \/home\/johan\/web\/skeleton\/public\/index.php(30): Slim\\App->run()",
"#12 {main}"
]
}
]
}
Regards,
Johan
how can i download a file using this approach .
i want to save the file on clients disk.
I seem to be getting a an error on "call to member function getError on null" any ideas on what I am doing wrong?
How to get image width and height
use getimagesize().
how to check if the file already moved?
and UploadedFile class property "moved" is protected
hi
i can upload file in slim
but when I want to send the saved file path to the user. The path does not work properly.
How to make the right path for the use ?
hi Rob! thanks for the article.
I am using slim 3 and trying to upload more than 20 files, but I am getting just 20 files when I called the getUploadedFiles() function, how can I change that limit?
Thanks!