This plugin offers an implementation of an object storage for CakePHP applications.
This plugin provides:
- migration for creating the
files
table - entity and table classes for
Files
model - REST controller
- default routing
- service provider for the storage service using CakePHP's service container
- event listener to inject the service container in table instance on initialization
- modeless forms to handle file operations
- shell command to cleanup incomplete multipart uploads
You can install this plugin using composer:
composer install chialab/cakephp-object-storage
To use AWS S3 as a backend storage, the SDK is also needed:
composer install aws/aws-sdk-php
Add the plugin in your Application.php
bootstrap:
public function bootstrap(): void
{
// ...
$this->addPlugin('Chialab/CakeObjectStorage');
// ...
}
Run the migration to create the files
table:
bin/cake migrations migrate --plugin Chialab/CakeObjectStorage
Add the configuration for the backend storage in your app.php
:
// ...
'Storage' => [
'className' => FilesystemAdapter::class,
'args' => [
WWW_ROOT . 'files' . DS,
TMP . 'incomplete-uploads' . DS,
'/files/',
0007,
],
],
// ...
See chialab/php-object-storage
library's README for more information on the adapters.
Use ['routes' => false]
when adding the plugin to implement your own routes. You can still use Chialab/CakeObjectStorage.Files
as controller
if you only want to change the paths, or implement your own controller.
Use ['bootstrap' => false]
when adding the plugin to disable automatically adding the event listener.
If you're using the plugin's FilesTable
, you are required to set the DI container to its instances, or implement your own service handling.
Use ['services' => false]
when adding the plugin to disable automatically adding the storage service provider.
You are required to provide a MultipartUploadInterface
implementation to FilesTable
instances, or implement your own service handling.
See File::getMultipartChunkSize()
for the threshold between small file and multipart upload.
The default controller accepts a request like the following:
POST /files
{
"filename": "example.jpg",
"mime_type": "image/jpg",
"size": 20480 // 20 MiB
}
The response is like the following:
{
"file": {
"filename": "example.jpg",
"mime_type": "image/jpg",
"size": 20480, // 20 MiB
"created": "2023-05-08T15:58:27+00:00",
"id": "076e11d0-4ba6-4680-8796-ee232eeca090",
"is_multipart": true,
"is_finalized": false,
"url": null
},
"chunk_size": 10485760, // 10 MiB, present only if `is_multipart === true`
"upload": "http://ossma.localhost/api/v1/files/076e11d0-4ba6-4680-8796-ee232eeca090/upload",
"finalize": "http://ossma.localhost/api/v1/files/076e11d0-4ba6-4680-8796-ee232eeca090/finalize" // present only if `is_multipart === true`
}
The file entity in the response has a property is_multipart
to let the client know if a multipart upload is required.
In this case, the response will also contain a chunk_size
property with the maximum file part size, and a finalize
URL
to call after all parts have been uploaded.
To upload the file, make a request like the following with the file as body:
POST /files/{file_id}/upload
To upload a part of a multipart upload, add the part
query parameter to the request:
POST /files/{file_id}/upload?part=1
The parameter is an incremental number starting from 1, which represent the "index" of the part currently uploading.
The endpoint will respond with a hash of the uploaded part that needs to be stored to finalize the upload later:
{
"part": "1",
"hash": "86507bea6d332c2814df1d244abdd3696169607f5f42ac3f6782dd69883f6b0d"
}
To finalize the upload:
POST /files/{file_id}/finalize
{
"hashes": [
{ "part": 1, hash: "86507bea6d332c2814df1d244abdd3696169607f5f42ac3f6782dd69883f6b0d" },
{ "part": 2, hash: "df7c1db235bac334dab81d0a25824055bdb738bf54fd2857698d4a9fd88af2c6" },
]
}
To abort a multipart upload:
DELETE /files/{file_id}/abort