Skip to content

Commit 0ceccef

Browse files
author
栄茉
authored
Merge pull request #6 from MayMeow/add-s3-support
Adding support for S3 storage
2 parents be26b5a + 2c5432b commit 0ceccef

17 files changed

+692
-53
lines changed

.devcontainer/devcontainer.json

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
2+
// https://github.com/microsoft/vscode-dev-containers/tree/v0.202.5/containers/docker-existing-docker-compose
3+
// If you want to run as a non-root user in the container, see .devcontainer/docker-compose.yml.
4+
{
5+
"name": "Existing Docker Compose (Extend)",
6+
7+
// Update the 'dockerComposeFile' list if you have more compose files or use different names.
8+
// The .devcontainer/docker-compose.yml file contains any overrides you need/want to make.
9+
"dockerComposeFile": [
10+
"../docker-compose-dev.yml",
11+
"docker-compose.yml"
12+
],
13+
14+
// The 'service' property is the name of the service for the container that VS Code should
15+
// use. Update this value and .devcontainer/docker-compose.yml to the real service name.
16+
"service": "app",
17+
18+
// The optional 'workspaceFolder' property is the path VS Code should open by default when
19+
// connected. This is typically a file mount in .devcontainer/docker-compose.yml
20+
"workspaceFolder": "/workspace",
21+
22+
// Set *default* container specific settings.json values on container create.
23+
"settings": {},
24+
25+
// Add the IDs of extensions you want installed when the container is created.
26+
"extensions": []
27+
28+
// Use 'forwardPorts' to make a list of ports inside the container available locally.
29+
// "forwardPorts": [],
30+
31+
// Uncomment the next line if you want start specific services in your Docker Compose config.
32+
// "runServices": [],
33+
34+
// Uncomment the next line if you want to keep your containers running after VS Code shuts down.
35+
// "shutdownAction": "none",
36+
37+
// Uncomment the next line to run commands after the container is created - for example installing curl.
38+
// "postCreateCommand": "apt-get update && apt-get install -y curl",
39+
40+
// Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root.
41+
// "remoteUser": "vscode"
42+
}

.devcontainer/docker-compose.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
version: '3'
2+
services:
3+
# Update this to the name of the service you want to work with in your docker-compose.yml file
4+
app:
5+
# If you want add a non-root user to your Dockerfile, you can use the "remoteUser"
6+
# property in devcontainer.json to cause VS Code its sub-processes (terminals, tasks,
7+
# debugging) to execute as the user. Uncomment the next line if you want the entire
8+
# container to run as this user instead. Note that, on Linux, you may need to
9+
# ensure the UID and GID of the container user you create matches your local user.
10+
# See https://aka.ms/vscode-remote/containers/non-root for details.
11+
#
12+
# user: vscode
13+
14+
# Uncomment if you want to override the service's Dockerfile to one in the .devcontainer
15+
# folder. Note that the path of the Dockerfile and context is relative to the *primary*
16+
# docker-compose.yml file (the first in the devcontainer.json "dockerComposeFile"
17+
# array). The sample below assumes your primary file is in the root of your project.
18+
#
19+
# build:
20+
# context: .
21+
# dockerfile: .devcontainer/Dockerfile
22+
23+
init: true
24+
25+
volumes:
26+
# Update this to wherever you want VS Code to mount the folder of your project
27+
- .:/workspace:cached
28+
29+
# Uncomment the next line to use Docker from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker-compose for details.
30+
# - /var/run/docker.sock:/var/run/docker.sock
31+
32+
# Uncomment the next four lines if you will use a ptrace-based debugger like C++, Go, and Rust.
33+
# cap_add:
34+
# - SYS_PTRACE
35+
# security_opt:
36+
# - seccomp:unconfined
37+
38+
# Overrides default command so things don't shut down after the process ends.
39+
command: /bin/bash -c "while sleep 1000; do :; done"
40+

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# v 1.1.0
2+
3+
Added support for S3 Compatible storage, Upload and download are now handling StorageManagers.
4+
5+
* ADD Support for S3 Compatible storage
6+
* ADD new Component for Download files from storage
7+
* ADD new `storage_type` key into configuration for components
8+
* ADD AWS SDK to requirements
9+
* ADD `FileUpload\Exceptions\FileContentException`
10+
* ADD `LocalStorageManager` and `S3StorageManager` which handling upload and download files from storage
11+
* ADD New configuration for `S3` client (for more informations check README)
12+
* ADD `StorageConfigInterface`, `StorageManagerInterface` and `StoredFileInterface`
13+
* UPDATE Return type for `getFile()` was changed to`\FileUpload\File\StoredFileInterface` from `\Psr\Http\Message\UploadedFileInterface` Which returning more informations about file
14+
* UPDATE `UploadComponent::getFile()` from now can throw `HttpException` in case there is `Not supported storage type` in configuration

README.md

Lines changed: 99 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# 🆙 FileUpload plugin for CakePHP
22

3+
Cakephp plugin to upload files to storage and download them. Currently are supported:
4+
5+
- Local storage
6+
- S3 Compatible storage
7+
8+
And supported actions are upload and download.
9+
310
## Installation
411

512
You can install this plugin into your CakePHP application using [composer](https://getcomposer.org).
@@ -12,20 +19,6 @@ The recommended way to install composer packages is:
1219
composer require maymeow/file-upload
1320
```
1421

15-
## 🛡 From my server
16-
17-
Add to `composer.json` following repository
18-
19-
```json
20-
{"type":"composer","url":"https://git.cloud.hsoww.net/api/v4/group/121/-/packages/composer/packages.json"}
21-
```
22-
23-
then run
24-
25-
```
26-
composer require maymeow/file-upload
27-
```
28-
2922
## Usage
3023

3124
To load plugin addd to `Application.php`
@@ -41,10 +34,17 @@ public function initialize(): void
4134
{
4235
parent::initialize();
4336
$this->loadComponent('FileUpload.Upload');
37+
$this->loadComponent('FileUpload.Dowmload'); // in case you wan to download files
4438
}
4539
```
4640

47-
Or you can change them
41+
## Using local storage
42+
43+
Commands above are loaded with default configuration:
44+
- Using Local filesystem: type is set to `local`
45+
- Data are stored in `sotrage` folder in root of your application - This folder is not public and cannot be served directly from webserver. You need to use `DownloadComponent` to get files
46+
- Field name for uploading files is `uploaded_file`
47+
- Allowed are all file types
4848

4949
```php
5050
public function initialize(): void
@@ -60,40 +60,95 @@ public function initialize(): void
6060

6161
For `allowedFileTypes` use `'allowedFileTypes' => '*'` for all file types or `'allowedFileTypes' => ['type1', 'type2']` for your expected file types. If file have not allowed type Component will throw `Cake\Http\Exception\HttpException`.
6262

63-
Usage in function
63+
Using local storage (default option) you can use default config but do not forge to change allowed file types and form field nam from which you uploading file to server.
64+
65+
## Using S3 storage
66+
67+
For S3 storage configuration is pretty same as configuration above, but you have to change type to `s3` instead of `local` which is default as follows
68+
69+
```php
70+
public function initialize(): void
71+
{
72+
parent::initialize();
73+
$this->loadComponent('FileUpload.Upload', [
74+
'fieldName' => 'your_form_file_field',
75+
'storagePath' => 'bucket_name',
76+
'storage_type' => 's3'
77+
]);
78+
}
79+
```
80+
81+
:exclamation: Dont forget to change configuration for `DownloadComponent` too if you planing using it.
82+
83+
Next add configuration for s3 server to your config file `app_local.php` for example as follows:
6484

6585
```php
66-
public function add()
86+
'S3' => [
87+
'version' => 'latest',
88+
'region' => 'us-east-1',
89+
'endpoint' => 'http://cake_minio:9000',
90+
'use_path_style_endpoint' => true,
91+
'credentials' => [
92+
'key' => 'minioadmin',
93+
'secret' => 'minioadmin',
94+
],
95+
]
96+
```
97+
98+
Config above is for using minio with my [CakePHP starte kit](https://github.com/MayMeow/cakephp-starter-kit). Change it for your needs.
99+
100+
101+
## Uploading files
102+
103+
104+
```php
105+
/**
106+
* @throws \HttpException
107+
* @return \Cake\Http\Response|null|void
108+
*/
109+
public function upload()
67110
{
68-
$file = $this->Files->newEmptyEntity();
111+
$uploadForm = new UploadForm();
69112

70113
if ($this->request->is('post')) {
71-
72-
// you can use try catch to show Flash instead of error page
73-
try {
74-
// Get Uploaded file details
75-
$fileObject = $this->Upload->getFile($this->request);
76-
77-
$file = $this->Files->patchEntity($file, $this->request->getData());
78-
79-
// Update Model and write it
80-
$file->name = $fileObject->getClientFilename();
81-
$file->size = $fileObject->getSize();
82-
$file->path = $fileObject->getClientFilename();
83-
$file->updated = Date::now();
84-
85-
if ($this->Files->save($file)) {
86-
$this->Flash->success(__('The file has been saved.'));
87-
88-
return $this->redirect(['action' => 'index']);
89-
}
90-
$this->Flash->error(__('The file could not be saved. Please, try again.'));
91-
92-
} catch (HttpException $e) {
93-
$this->Flash->error($e->getMessage());
94-
}
114+
$uploadedFile = $this->Upload->getFile($this->request);
115+
116+
// Create new Entity and store info about uploaded file
117+
$file = $this->Files->newEmptyEntity();
118+
119+
$file->name = $uploadedFile->getFileName();
120+
$file->path = $uploadedFile->getPath();
121+
122+
$this->Files->save($file);
123+
124+
return $this->redirect($this->referer());
95125
}
96-
$this->set(compact('file'));
126+
127+
$this->set(compact('uploadForm'));
128+
}
129+
```
130+
131+
## Downloading files
132+
133+
134+
```php
135+
/**
136+
* @param string $fileName
137+
* @return \Cake\Http\Response|void
138+
*/
139+
public function download($fileName)
140+
{
141+
$downloadedFile = $this->Download->getFile($fileName);
142+
143+
$response = $this->response;
144+
$response = $response->withStringBody($downloadedFile->getFileContent());
145+
$response = $response->withType($downloadedFile->getFileType());
146+
147+
if ($this->request->getParam('?.download') == true) {
148+
$response = $response->withDownload($fileName);
149+
}
150+
151+
return $response;
97152
}
98153
```
99154

@@ -102,9 +157,9 @@ public function add()
102157
* [x] Configurable field name
103158
* [x] Configurable path to storage
104159
* [x] Allowed file types
160+
* [x] Add S3 Support
105161
* [ ] Multiple file upload
106162
* [ ] File Size
107-
* [ ] Factory with most common file types
108163

109164
💡 If you have more ideas then you can post issue on porect's GitHub page.
110165

VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1.0.2

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"license": "MIT",
66
"require": {
77
"php": ">=7.2",
8+
"aws/aws-sdk-php": "^3.199",
89
"cakephp/cakephp": "~4.1.0"
910
},
1011
"require-dev": {

docker/devcontainer/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
FROM ghcr.io/maymeow/php-ci-cd/php-ci-cd:7.4.16-cs-1
22

3+
# RUN apt update && apt install -y git
4+
35
# arguments in docker-compose file
46
ARG user=vscode
57
ARG uid=1000
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace FileUpload\Controller\Component;
5+
6+
use Cake\Controller\Component;
7+
use FileUpload\File\StoredFileInterface;
8+
use FileUpload\Storage\LocalStorageManager;
9+
use FileUpload\Storage\S3StorageManager;
10+
use FileUpload\Storage\StorageConfigInterface;
11+
12+
/**
13+
* Download component
14+
*/
15+
class DownloadComponent extends Component implements StorageConfigInterface
16+
{
17+
/**
18+
* Default configuration.
19+
*
20+
* @var array
21+
*/
22+
protected $_defaultConfig = [
23+
'fieldName' => 'uploaded_file',
24+
'storagePath' => ROOT . DS . 'storage' . DS ,
25+
'allowedFileTypes' => '*',
26+
'storage_type' => 'local',
27+
];
28+
29+
/**
30+
* Returns stored file info and content
31+
*
32+
* @param string $fileName Name of file without path or slashes
33+
* @return \FileUpload\File\StoredFileInterface Stored file object
34+
*/
35+
public function getFile(string $fileName): StoredFileInterface
36+
{
37+
/** check if storage is s3. Local storage is default one */
38+
if ($this->getConfig('storage_type') == 's3') {
39+
$sm = new S3StorageManager($this);
40+
} else {
41+
$sm = new LocalStorageManager($this); // default one
42+
}
43+
44+
return $sm->pull($fileName);
45+
}
46+
}

0 commit comments

Comments
 (0)