├── .gitignore ├── MANIFEST.in ├── README.md ├── django_minio ├── __init__.py └── storage.py ├── setup.cfg └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Example user template template 3 | ### Example user template 4 | 5 | # IntelliJ project files 6 | .idea 7 | *.iml 8 | out 9 | gen### Python template 10 | # Byte-compiled / optimized / DLL files 11 | __pycache__/ 12 | *.py[cod] 13 | *$py.class 14 | 15 | # C extensions 16 | *.so 17 | 18 | # Distribution / packaging 19 | .Python 20 | env/ 21 | build/ 22 | develop-eggs/ 23 | dist/ 24 | downloads/ 25 | eggs/ 26 | .eggs/ 27 | lib/ 28 | lib64/ 29 | parts/ 30 | sdist/ 31 | var/ 32 | *.egg-info/ 33 | .installed.cfg 34 | *.egg 35 | 36 | # PyInstaller 37 | # Usually these files are written by a python script from a template 38 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 39 | *.manifest 40 | *.spec 41 | 42 | # Installer logs 43 | pip-log.txt 44 | pip-delete-this-directory.txt 45 | 46 | # Unit test / coverage reports 47 | htmlcov/ 48 | .tox/ 49 | .coverage 50 | .coverage.* 51 | .cache 52 | nosetests.xml 53 | coverage.xml 54 | *,cover 55 | .hypothesis/ 56 | 57 | # Translations 58 | *.mo 59 | *.pot 60 | 61 | # Django stuff: 62 | *.log 63 | local_settings.py 64 | 65 | # Flask instance folder 66 | instance/ 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # IPython Notebook 78 | .ipynb_checkpoints 79 | 80 | # pyenv 81 | .python-version 82 | 83 | # celery beat schedule file 84 | celerybeat-schedule 85 | 86 | # dotenv 87 | .env 88 | 89 | # virtualenv 90 | venv/ 91 | ENV/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | 96 | # Rope project settings 97 | .ropeproject 98 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.md 3 | recursive-include django_minio/* 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Django Minio Storage 2 | ==================== 3 | Django app to use [Minio Server](https://github.com/minio/minio) as file storage. 4 | 5 | Intallation 6 | ----------- 7 | At first, you need to have working minio server. How to do that you can found at [Minio Quickstart Guide](http://docs.minio.io/docs/minio-quickstart-guide). 8 | 9 | Install Django Minio Storage from pip: 10 | ``` 11 | pip install django-minio 12 | ``` 13 | 14 | Add following keys to your projects settings file: 15 | ``` 16 | MINIO_SERVER = 'your_minio_server_address' 17 | MINIO_ACCESSKEY = 'your_minio_server_access_key' 18 | MINIO_SECRET = 'your_minio_server_secret_key' 19 | MINIO_BUCKET = 'my_bucket' 20 | MINIO_SECURE = True 21 | DEFAULT_FILE_STORAGE = 'django_minio.storage.MinioStorage' 22 | ``` 23 | Demo minio server and it's credentials can be found at [Python Client Quickstart Guide](https://docs.minio.io/docs/python-client-api-reference). 24 | 25 | If you want to use this module only at production server, include above settings only in production settings. 26 | So at local developer machine you will use django's default file storage. 27 | 28 | More information about file storages can be found at [Django Docs](https://docs.djangoproject.com/en/1.8/ref/files/storage/). 29 | 30 | Currently tested only at Django 2.0. 31 | -------------------------------------------------------------------------------- /django_minio/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maddevsio/django_minio/27d0b6d8c997337220256f8f4ee3388572daab5d/django_minio/__init__.py -------------------------------------------------------------------------------- /django_minio/storage.py: -------------------------------------------------------------------------------- 1 | import mimetypes 2 | import os 3 | 4 | from django.conf import settings 5 | from django.core.files.storage import Storage 6 | from django.utils.deconstruct import deconstructible 7 | from minio import Minio 8 | from minio.error import InvalidXMLError, InvalidEndpointError, NoSuchKey, NoSuchBucket 9 | from urllib3.exceptions import MaxRetryError 10 | 11 | 12 | def setting(name, default=None): 13 | """ 14 | Helper function to get a Django setting by name or (optionally) return 15 | a default (or else ``None``). 16 | """ 17 | return getattr(settings, name, default) 18 | 19 | 20 | @deconstructible 21 | class MinioStorage(Storage): 22 | # TODO: Log errors caused by exceptions 23 | server = setting('MINIO_SERVER') 24 | access_key = setting('MINIO_ACCESSKEY') 25 | secret_key = setting('MINIO_SECRET') 26 | bucket = setting('MINIO_BUCKET') 27 | secure = setting('MINIO_SECURE') 28 | 29 | def __init__(self, *args, **kwargs): 30 | super(MinioStorage, self).__init__(*args, **kwargs) 31 | self._connection = None 32 | 33 | @property 34 | def connection(self): 35 | if not self._connection: 36 | try: 37 | self._connection = Minio( 38 | self.server, self.access_key, self.secret_key, self.secure) 39 | except InvalidEndpointError: 40 | self._connection = None 41 | return self._connection 42 | 43 | def _save(self, name, content): 44 | pathname, ext = os.path.splitext(name) 45 | dir_path, _ = os.path.split(pathname) 46 | hashed_name = f'{dir_path}/{hash(content)}{ext}' 47 | 48 | content_type = content.content_type if hasattr(content, 'content_type') else mimetypes.guess_type(name)[0] 49 | 50 | if self.connection: 51 | if not self.connection.bucket_exists(self.bucket): 52 | self.connection.make_bucket(self.bucket) 53 | try: 54 | self.connection.put_object( 55 | self.bucket, 56 | hashed_name, 57 | content, 58 | content.size, 59 | content_type=content_type, 60 | ) 61 | except InvalidXMLError: 62 | pass 63 | except MaxRetryError: 64 | pass 65 | return hashed_name # TODO: Do not return name if saving was unsuccessful 66 | 67 | def url(self, name): 68 | if self.connection: 69 | try: 70 | if self.connection.bucket_exists(self.bucket): 71 | return self.connection.presigned_get_object(self.bucket, name) 72 | else: 73 | return 'image_not_found' # TODO: Find a better way of returning errors 74 | except MaxRetryError: 75 | return 'image_not_found' 76 | return 'could_not_establish_connection' 77 | 78 | def exists(self, name): 79 | try: 80 | self.connection.stat_object(self.bucket, name) 81 | except (NoSuchKey, NoSuchBucket): 82 | return False 83 | except Exception as err: 84 | raise IOError(f'Could not stat file {name} {err}') 85 | else: 86 | return True 87 | 88 | def size(self, name): 89 | return self.connection.stat_object(self.bucket, name).size 90 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maddevsio/django_minio/27d0b6d8c997337220256f8f4ee3388572daab5d/setup.cfg -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | import os 3 | 4 | 5 | with open(os.path.join(os.path.dirname(__file__), 'README.md')) as readme: 6 | README = readme.read() 7 | 8 | os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) 9 | 10 | setup( 11 | name='django_minio', 12 | version='2.0', 13 | packages=find_packages(), 14 | include_package_data=True, 15 | license='MIT', 16 | description='Django app to use Minio Server as file storage.', 17 | long_description=README, 18 | url='https://github.com/shapoglyk/django_minio', 19 | author='Belek Abylov', 20 | author_email='abylov.belek@gmail.com', 21 | classifiers=[ 22 | 'Environment :: Web Environment', 23 | 'Framework :: Django :: 2.0', 24 | 'Intended Audience :: Developers', 25 | 'License :: OSI Approved :: BSD License', 26 | 'Operating System :: OS Independent', 27 | 'Programming Language :: Python :: 3.6', 28 | 'Topic :: Software Development :: Libraries :: Python Modules', 29 | ], 30 | keywords='minio storage files', 31 | install_requires=[ 32 | 'Django >= 2.0', 33 | 'minio == 2.2.2', 34 | ], 35 | extras_require={ 36 | 'dev': [ 37 | 'wheel', 38 | 'twine' 39 | ], 40 | }, 41 | ) 42 | --------------------------------------------------------------------------------