Skip to content

Commit 64d98c7

Browse files
committed
README.md
1 parent 65fc5d9 commit 64d98c7

1 file changed

Lines changed: 54 additions & 116 deletions

File tree

README.md

Lines changed: 54 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,26 @@
99
[![PyPI version](https://img.shields.io/pypi/pyversions/ellar-storage.svg)](https://pypi.python.org/pypi/ellar-storage)
1010

1111
## Introduction
12-
EllarStorage Module adds support for cloud and local file storage
13-
management using [apache `libcloud`](https://github.com/apache/libcloud) package to your Ellar application.
12+
The EllarStorage Module enriches your Ellar application with robust support for managing both cloud and
13+
local file storage.
14+
Leveraging the capabilities of the [Apache `libcloud`](https://github.com/apache/libcloud) package
15+
to simplify file storage operations within your Ellar-powered projects.
1416

1517
## Installation
1618
```shell
1719
$(venv) pip install ellar-storage
1820
```
1921

20-
This library was inspired by [sqlalchemy-file](https://github.com/jowilf/sqlalchemy-file)
22+
This library drew inspiration from [sqlalchemy-file](https://github.com/jowilf/sqlalchemy-file).
2123

22-
23-
## **Usage**
24-
Follow Ellar project scaffold here, then you configure your module.
24+
## Usage
25+
To integrate EllarStorage into your project, follow the standard Ellar project structure and then configure the module as follows:
2526

2627
### StorageModule
27-
Just like every other ellar `Module`s, `StorageModule`
28-
can be configured directly in where its used or through application config.
29-
30-
### **StorageModule.setup**
31-
Quick example using `StorageModule.setup` method.
28+
Similar to other Ellar modules, the `StorageModule` can be configured directly where it's used or through the application configuration.
3229

33-
Pattern of configuring Storages are in key-word patterns
34-
where the `key` is `Folder name/Container` and value is `StorageDriver` init properties.
35-
Example is shown below:
30+
### StorageModule.setup
31+
You can set up the `StorageModule` using the `setup` method. Here's a quick example:
3632

3733
```python
3834
import os
@@ -64,23 +60,11 @@ class ApplicationModule(ModuleBase):
6460
pass
6561
```
6662

67-
In the above illustration, When application initialization is complete,
68-
`files`, `images` and `documents` will be created in `os.path.join(BASE_DIRS, "media")`.
69-
Each is configured to be managed by Local Storage Driver.
70-
See other supported [storage drivers](https://libcloud.readthedocs.io/en/stable/storage/supported_providers.html#provider-matrix)
71-
72-
Each storage required `key` and some other parameters for object instantiation,
73-
so those should be provided in the `options` as a key-value pair.
63+
In this example, after application initialization, folders for `files`, `images`, and `documents` will be created in the specified directory. Each folder is configured to be managed by a local storage driver. You can explore other supported [storage drivers](https://libcloud.readthedocs.io/en/stable/storage/supported_providers.html#provider-matrix).
7464

75-
Also, `default` parameter defines container/folder of choice when saving/retrieving
76-
a file if storage container was specified.
77-
It is important to note that if `default` is not set,
78-
it will default to the first storage container which in this can is `files`.
65+
### StorageModule.register_setup
66+
Alternatively, you can move the storage configuration to the application config:
7967

80-
81-
### **StorageModule.register_setup**
82-
Alternatively, we can move the storage configuration to application Config and everything will still work fine.
83-
For example:
8468
```python
8569
## project_name/root_module.py
8670

@@ -93,7 +77,7 @@ class ApplicationModule(ModuleBase):
9377
pass
9478
```
9579

96-
Then in `config.py` add the following code:
80+
Then, in `config.py`, you can define the storage configurations:
9781

9882
```python
9983
import os
@@ -103,7 +87,6 @@ from ellar_storage import get_driver, Provider
10387

10488
BASE_DIRS = Path(__file__).parent
10589

106-
10790
class DevelopmentConfig(ConfigDefaultTypesMixin):
10891
DEBUG = True
10992

@@ -127,10 +110,7 @@ class DevelopmentConfig(ConfigDefaultTypesMixin):
127110
```
128111

129112
### StorageService
130-
131-
At the end of `StorageModule` setup, `StorageService` is registered into an Ellar DI system.
132-
A quick way to test this would be through application instance.
133-
For example:
113+
At the end of the `StorageModule` setup, `StorageService` is registered into the Ellar DI system. Here's a quick example of how to use it:
134114

135115
```python
136116
## project_name/server.py
@@ -141,7 +121,6 @@ from ellar.common import datastructures, constants
141121
from ellar.core import LazyModuleImport as lazyLoad
142122
from ellar_storage import StorageService
143123

144-
145124
application = AppFactory.create_from_app_module(
146125
lazyLoad("project_name.root_module:ApplicationModule"),
147126
config_module=os.environ.get(
@@ -150,20 +129,22 @@ application = AppFactory.create_from_app_module(
150129
)
151130

152131
storage_service: StorageService = application.injector.get(StorageService)
153-
# save a file in files folder
132+
# Example: save a file in the 'files' folder
154133
storage_service.save(
155-
file=datastructures.ContentFile(b"We can now save files in files folder", name="file.txt"), upload_storage='files')
156-
# save a file in images folder
134+
file=datastructures.ContentFile(b"We can now save files in the 'files' folder", name="file.txt"), upload_storage='files')
135+
# Example: save a file in the 'images' folder
157136
storage_service.save(
158-
file=datastructures.ContentFile(b"We can now save files in images folder", name="image.txt"), upload_storage='images')
159-
# save a file in document folder
137+
file=datastructures.ContentFile(b"We can now save files in the 'images' folder", name="image.txt"), upload_storage='images')
138+
# Example: save a file in the 'documents' folder
160139
storage_service.save(
161-
file=datastructures.ContentFile(b"We can now save files in documents folder", name="docs.txt"), upload_storage='documents')
140+
file=datastructures.ContentFile(b"We can now save files in the 'documents' folder", name="docs.txt"), upload_storage='documents')
162141
```
163-
### StorageService in Route functions
164-
You can inject `StorageService` into your controller or route functions. For example:
142+
143+
### StorageService in Route Functions
144+
You can inject `StorageService` into your controllers or route functions. For instance:
165145

166146
In Controller:
147+
167148
```python
168149
from ellar.common import ControllerBase, Controller
169150
from ellar_storage import StorageService
@@ -175,6 +156,7 @@ class FileManagerController(ControllerBase):
175156
```
176157

177158
In Route Function:
159+
178160
```python
179161
from ellar.common import UploadFile, Inject, post
180162
from ellar_storage import StorageService
@@ -184,83 +166,39 @@ def upload_file(self, file: UploadFile, storage_service: Inject[StorageService])
184166
pass
185167
```
186168

187-
Here is a quick example of a controller to manage files. This is just to illustrate how to use `StorageService`.
169+
See [Sample Project](https://github.com/python-ellar/ellar-storage/tree/master/samples)
188170

189-
```python
190-
from ellar.common import (
191-
Controller,
192-
ControllerBase,
193-
File,
194-
Form,
195-
Inject,
196-
Query,
197-
UploadFile,
198-
delete,
199-
file,
200-
get,
201-
post,
202-
)
171+
## API Reference
203172

204-
from ellar_storage import StorageService
205-
206-
207-
@Controller('/upload')
208-
class FileManagerController(ControllerBase):
209-
def __init__(self, storage_service: StorageService):
210-
self._storage_service = storage_service
211-
212-
@post("/", response=str)
213-
def upload_file(
214-
self,
215-
upload: File[UploadFile],
216-
storage_service: Inject[StorageService],
217-
upload_storage: Form[str]
218-
):
219-
assert self._storage_service == storage_service
220-
res = storage_service.save(file=upload, upload_storage=upload_storage)
221-
return res.filename
222-
223-
@get("/")
224-
@file(media_type="application/octet-stream", streaming=True)
225-
def download_file(self, path: Query[str]):
226-
res = self._storage_service.get(path)
227-
return {"media_type": res.content_type, "content": res.as_stream()}
228-
229-
@get("/download_as_attachment")
230-
@file(media_type="application/octet-stream")
231-
def download_as_attachment(self, path: Query[str]):
232-
res = self._storage_service.get(path)
233-
return {
234-
"path": res.get_cdn_url(), # since we are using a local storage, this will return a path to the file
235-
"filename": res.filename,
236-
'media_type': res.content_type
237-
}
238-
239-
@delete("/", response=dict)
240-
def delete_file(self, path: Query[str]):
241-
self._storage_service.delete(path)
242-
return ""
243-
```
244-
245-
See [Sample Project]()
173+
### StorageService
246174

175+
- **_save(self, file: UploadFile, upload_storage: Optional[str] = None) -> StoredFile_**: Saves a file from an `UploadFile` object.
176+
- **_save_async(self, file: UploadFile, upload_storage: Optional[str] = None) -> StoredFile_**: Asynchronously saves a file from an `UploadFile` object.
177+
- **_save_content(self, **kwargs) -> StoredFile_**: Saves a file from content/bytes or through a file path.
178+
- **_save_content_async(self, **kwargs) -> StoredFile_**: Asynchronously saves a file from content/bytes or through a file path.
179+
- **_get(self, path: str) -> StoredFile_**: Retrieves a saved file if the specified `path` exists. The `path` can be in the format `container/filename.extension` or `filename.extension`.
180+
- **_get_async(self, path: str) -> StoredFile_**: Asynchronously retrieves a saved file if the specified `path` exists.
181+
- **_delete(self, path: str) -> bool_**: Deletes a saved file if the specified `path` exists.
182+
- **_delete_async(self, path: str) -> bool_**: Asynchronously deletes a saved file if the specified `path` exists.
183+
- **_get_container(self, name: Optional[str] = None) -> Container_**: Gets a `libcloud.storage.base.Container` instance for a configured storage setup.
247184

248185
### StoredFile
249-
`StoredFile` is file-like object returned from saving and retrieving saved files.
250-
Its also extends some `libcloud` Object methods
251-
and has reference to the `libcloud` Object retrieved from the `libcloud` storage container.
252-
253-
Some important attributes:
254-
255-
- **name**: File name
256-
- **size**: File Size
257-
- **filename**: File name
258-
- **content_type**: File Content Type
259-
- **object**: `libcloud` Object reference
260-
- **read(self, n: int = -1, chunk_size: t.Optional[int] = None) -> bytes**: Reads file content
261-
- **get_cdn_url(self) -> t.Optional[str]**: gets file cdn url
262-
- **as_stream(self, chunk_size: t.Optional[int] = None) -> t.Iterator[bytes]**: create a file stream
263-
- **delete(self) -> bool**: deletes file from container
186+
187+
`StoredFile` is a file-like object returned from saving and retrieving files.
188+
It extends some `libcloud` Object methods and has a reference to the
189+
`libcloud` Object retrieved from the storage container.
190+
191+
Key attributes include:
192+
193+
- **_name_**: File name
194+
- **_size_**: File size
195+
- **_filename_**: File name
196+
- **_content_type_**: File content type
197+
- **_object_**: `libcloud` Object reference
198+
- **_read(self, n: int = -1, chunk_size: Optional[int] = None) -> bytes_**: Reads file content
199+
- **_get_cdn_url(self) -> Optional[str]_**: Gets file CDN URL
200+
- **_as_stream(self, chunk_size: Optional[int] = None) -> Iterator[bytes]_**: Creates a file stream
201+
- **_delete(self) -> bool_**: Deletes the file from the container
264202

265203
## License
266204
Ellar is [MIT licensed](LICENSE).

0 commit comments

Comments
 (0)