From 88a5402cbba62705ae235f963f2fe8bd9561285c Mon Sep 17 00:00:00 2001 From: Johannes Hoppe Date: Sat, 10 Mar 2018 13:08:19 +0100 Subject: [PATCH 1/3] Update test suite --- .travis.yml | 13 ++----------- tox.ini | 5 ++--- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index d54dc98..845ab1b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,28 +12,19 @@ addons: packages: - google-chrome-stable python: -- '3.5' - '3.6' env: global: - - CHROME_DRIVER_VERSION=2.32 + - CHROME_DRIVER_VERSION=2.36 matrix: - - DJANGO=18 - - DJANGO=110 - DJANGO=111 + - DJANGO=20 - DJANGO=master - TOXENV=qa matrix: fast_finish: true allow_failures: - env: DJANGO=master - exclude: - - env: DJANGO=master - python: '2.7' - - env: TOXENV=qa - python: '2.7' - - env: TOXENV=qa - python: '3.5' install: - pip install -qU pip tox codecov before_script: diff --git a/tox.ini b/tox.ini index 9547b1c..f56d69a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,14 +1,13 @@ [tox] -envlist = py{35,36}-dj{18,110,111,master},qa +envlist = py{36}-dj{111,20,master},qa [testenv] setenv= DISPLAY=:99.0 PYTHONPATH = {toxinidir} deps= -rrequirements-dev.txt - dj18: https://github.com/django/django/archive/stable/1.8.x.tar.gz#egg=django - dj110: https://github.com/django/django/archive/stable/1.10.x.tar.gz#egg=django dj111: https://github.com/django/django/archive/stable/1.11.x.tar.gz#egg=django + dj20: https://github.com/django/django/archive/stable/2.0.x.tar.gz#egg=django djmaster: https://github.com/django/django/archive/master.tar.gz#egg=django commands= coverage run --source=s3file -m 'pytest' \ From 6d14e8274a430fdf00927ec99ca90c6fce480d0a Mon Sep 17 00:00:00 2001 From: Johannes Hoppe Date: Sat, 10 Mar 2018 13:47:18 +0100 Subject: [PATCH 2/3] Drop Django 1.8 support --- s3file/middleware.py | 23 ++++++++++++----------- tests/test_middleware.py | 4 ++-- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/s3file/middleware.py b/s3file/middleware.py index c14090a..aeff945 100644 --- a/s3file/middleware.py +++ b/s3file/middleware.py @@ -2,13 +2,20 @@ from django.core.files.storage import default_storage -try: - from django.utils.deprecation import MiddlewareMixin -except ImportError: - MiddlewareMixin = object +class S3FileMiddleware: + + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + file_fields = request.POST.getlist('s3file', []) + for field_name in file_fields: + paths = request.POST.getlist(field_name, []) + request.FILES.setlist(field_name, list(self.get_files_from_storage(paths))) + + return self.get_response(request) -class S3FileMiddleware(MiddlewareMixin): @staticmethod def get_files_from_storage(paths): """Return S3 file where the name does not include the path.""" @@ -16,9 +23,3 @@ def get_files_from_storage(paths): f = default_storage.open(path) f.name = os.path.basename(path) yield f - - def process_request(self, request): - file_fields = request.POST.getlist('s3file', []) - for field_name in file_fields: - paths = request.POST.getlist(field_name, []) - request.FILES.setlist(field_name, list(self.get_files_from_storage(paths))) diff --git a/tests/test_middleware.py b/tests/test_middleware.py index 2a54faa..d24ff9a 100644 --- a/tests/test_middleware.py +++ b/tests/test_middleware.py @@ -17,12 +17,12 @@ def test_get_files_from_storage(self): def test_process_request(self, rf): uploaded_file = SimpleUploadedFile('uploaded_file.txt', b'uploaded') request = rf.post('/', data={'file': uploaded_file}) - S3FileMiddleware().process_request(request) + S3FileMiddleware(lambda x: None)(request) assert request.FILES.getlist('file') assert request.FILES.get('file').read() == b'uploaded' default_storage.save('s3_file.txt', ContentFile(b's3file')) request = rf.post('/', data={'file': 's3_file.txt', 's3file': 'file'}) - S3FileMiddleware().process_request(request) + S3FileMiddleware(lambda x: None)(request) assert request.FILES.getlist('file') assert request.FILES.get('file').read() == b's3file' From 03418d8c3b631fbb2dcf2a64e504a08ff2220d69 Mon Sep 17 00:00:00 2001 From: Johannes Hoppe Date: Sat, 10 Mar 2018 15:38:17 +0100 Subject: [PATCH 3/3] Switch readme from Markdown to reStructuredText --- README.md | 137 ----------------------------------------------------- README.rst | 110 ++++++++++++++++++++++++++++++++++++++++++ setup.cfg | 2 +- 3 files changed, 111 insertions(+), 138 deletions(-) delete mode 100644 README.md create mode 100644 README.rst diff --git a/README.md b/README.md deleted file mode 100644 index 259c9b1..0000000 --- a/README.md +++ /dev/null @@ -1,137 +0,0 @@ -# django-s3file - -A lightweight file upload input for Django and Amazon S3. - -Django-S3File allows you to upload files directly AWS S3 effectively -bypassing your application server. This allows you to avoid long running -requests from large file uploads. - -[![PyPi Version](https://img.shields.io/pypi/v/django-s3file.svg)](https://pypi.python.org/pypi/django-s3file/) -[![Build Status](https://travis-ci.org/codingjoe/django-s3file.svg?branch=master)](https://travis-ci.org/codingjoe/django-s3file) -[![Test Coverage](https://coveralls.io/repos/codingjoe/django-s3file/badge.svg?branch=master)](https://coveralls.io/r/codingjoe/django-s3file) -[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/codingjoe/django-s3file/master/LICENSE) - -## Features - -* lightweight: less 200 lines -* no JavaScript or Python dependencies (no jQuery) -* easy integration -* works just like the build-in - -## Installation - -_Make sure you have [Amazon S3 storage][boto-storage] setup correctly._ - -Just install S3file using `pip`. - -```bash -pip install django-s3file -``` - -Add the S3File app and middleware in your settings: - -```python - -INSTALLED_APPS = ( - '...', - 's3file', - '...', -) - -MIDDLEWARE = ( - '...', - 's3file.middleware.S3FileMiddleware', - '...', -) -``` - -[boto-storage]: http://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html - -## Usage - -S3File automatically replaces Django's `ClearableFileInput` widget, -you do not need to alter your code at all. - -The `ClearableFileInput` widget is only than automatically replaced when the -`DEFAULT_FILE_STORAGE` setting is set to `django-storages`' `S3Boto3Storage`. - -### Setting up the AWS S3 bucket - -### Upload folder - -S3File uploads to a single folder. Files are later moved by Django when -they are saved to the `upload_to` location. - -It is recommended to [setup expiration][aws-s3-lifecycle-rules] for that folder, to ensure that -old and unused file uploads don't add up and produce costs. - -[aws-s3-lifecycle-rules]: http://docs.aws.amazon.com/AmazonS3/latest/dev/intro-lifecycle-rules.html - -The default folder name is: `tmp/s3file` -You can change it by changing the `S3FILE_UPLOAD_PATH` setting. - -### CORS policy - -You will need to allow `POST` from all origins. -Just add the following to your CORS policy. - -```xml - - - * - POST - GET - 3000 - * - - -``` - -### Uploading multiple files - -Django does have limited [support to uploaded multiple files][uploading-multiple-files]. -S3File fully supports this feature. The custom middleware makes ensure that files -are accessible via `request.FILES`, even thogh they have been uploaded to AWS S3 directly -and not to your Django application server. - -[uploading-multiple-files]: https://docs.djangoproject.com/en/1.11/topics/http/file-uploads/#uploading-multiple-files - -### Security and Authentication - -django-s3file does not require any authentication setup. Files can only be uploaded -to AWS S3 by users who have access to the form where the file upload is requested. - -You can further limit user data using the [`accept`][att_input_accept]-attribute. -The specified MIME-Type will be enforced in the AWS S3 policy as well, for enhanced -server side protection. - -S3File uses a strict policy and signature to grant clients permission to upload -files to AWS S3. This signature expires based on Django's -[`SESSION_COOKIE_AGE`][setting-SESSION_COOKIE_AGE] setting. - -[setting-SESSION_COOKIE_AGE]: https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-SESSION_COOKIE_AGE -[att_input_accept]: https://www.w3schools.com/tags/att_input_accept.asp - -## License - -The MIT License (MIT) - -Copyright (c) 2014 Johannes Hoppe - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..05c4ff5 --- /dev/null +++ b/README.rst @@ -0,0 +1,110 @@ +django-s3file +============= + +A lightweight file upload input for Django and Amazon S3. + +Django-S3File allows you to upload files directly AWS S3 effectively +bypassing your application server. This allows you to avoid long running +requests from large file uploads. + +|PyPi Version| |Build Status| |Test Coverage| |GitHub license| + +Features +-------- + +- lightweight: less 200 lines +- no JavaScript or Python dependencies (no jQuery) +- easy integration +- works just like the build-in + +Installation +------------ + +*Make sure you have `Amazon S3 storage`_ setup correctly.* + +Just install S3file using ``pip``. + +.. code:: bash + + pip install django-s3file + +Add the S3File app and middleware in your settings: + +.. code:: python + + + INSTALLED_APPS = ( + '...', + 's3file', + '...', + ) + + MIDDLEWARE = ( + '...', + 's3file.middleware.S3FileMiddleware', + '...', + ) + +Usage +----- + +S3File automatically replaces Django’s ``ClearableFileInput`` widget, +you do not need to alter your code at all. + +The ``ClearableFileInput`` widget is only than automatically replaced +when the ``DEFAULT_FILE_STORAGE`` setting is set to +``django-storages``\ ’ ``S3Boto3Storage``. + +Setting up the AWS S3 bucket +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Upload folder +~~~~~~~~~~~~~ + +S3File uploads to a single folder. Files are later moved by Django when +they are saved to the ``upload_to`` location. + +It is recommended to `setup expiration`_ for that folder, to ensure that +old and unused file uploads don’t add up and produce costs. + +The default folder name is: ``tmp/s3file`` You can change it by changing +the ``S3FILE_UPLOAD_PATH`` setting. + +CORS policy +~~~~~~~~~~~ + +You will need to allow ``POST`` from all origins. Just add the following +to your CORS policy. + +.. code:: xml + + + + * + POST + GET + 3000 + * + + + +Uploading multiple files +~~~~~~~~~~~~~~~~~~~~~~~~ + +Django does have limited `support to uploaded multiple files`_. S3File +fully supports this feature. The custom middleware makes ensure that +files are accessible via ``request.FILES``, even thogh they have been +uploaded to AWS S3 directly and not to your Django application server. + +.. _Amazon S3 storage: http://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html +.. _setup expiration: http://docs.aws.amazon.com/AmazonS3/latest/dev/intro-lifecycle-rules.html +.. _support to uploaded multiple files: https://docs.djangop + +.. |PyPi Version| image:: https://img.shields.io/pypi/v/django-s3file.svg + :target: https://pypi.python.org/pypi/django-s3file/ +.. |Build Status| image:: https://travis-ci.org/codingjoe/django-s3file.svg?branch=master + :target: https://travis-ci.org/codingjoe/django-s3file +.. |Test Coverage| image:: https://codecov.io/gh/codingjoe/django-s3file/branch/master/graph/badge.svg + :target: https://codecov.io/gh/codingjoe/django-s3file +.. |GitHub license| image:: https://img.shields.io/badge/license-MIT-blue.svg + :target: https://raw.githubusercontent.com/codingjoe/django-s3file/master/LICENSE \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 78e1ea1..b08d0d5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,7 +3,7 @@ name = django-s3file author = Johannes Hoppe author-email = info@johanneshoppe.com summary = A lightweight file uploader input for Django and Amazon S3 -description-file = README.md +description-file = README.rst home-page = https://github.com/codingjoe/django-s3file license = MIT classifier =