Skip to content

Commit 3521373

Browse files
committed
updating README
0 parents  commit 3521373

35 files changed

+1947
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
static/
2+
images/
3+
migrations
4+
__pycache__

Dockerfile

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
FROM python:3.5.1
2+
ENV PYTHONUNBUFFERED 1
3+
4+
################################################################################
5+
# CORE
6+
7+
RUN apt-get update && apt-get install -y \
8+
pkg-config \
9+
cmake \
10+
openssl \
11+
wget \
12+
git \
13+
vim
14+
15+
ADD requirements.txt /tmp/requirements.txt
16+
RUN pip install --upgrade pip
17+
RUN pip install -r /tmp/requirements.txt
18+
19+
ADD . /code/
20+
21+
22+
################################################################################
23+
# Storage Locations
24+
25+
RUN mkdir -p /code/images && \
26+
mkdir -p /var/www/images && \
27+
chmod -R 0755 /code/images/ && \
28+
# Create hashed temporary upload locations
29+
mkdir -p /var/www/images/_upload/{0..9} && chmod 777 -R /var/www/images/_upload
30+
31+
32+
WORKDIR /code
33+
34+
################################################################################
35+
# Clean Up
36+
37+
RUN apt-get autoremove -y && \
38+
apt-get clean && \
39+
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
40+
41+
CMD /code/run_uwsgi.sh
42+
43+
EXPOSE 3031

LICENSE

Lines changed: 661 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Django Nginx Upload
2+
3+
The [nginx upload](https://www.nginx.com/resources/wiki/modules/upload/) module offers a nice way to bypass the Django application for file uploads. This is an example Django application that
4+
demonstrates a basic setup with a custom nginx image and a webserver, both as Docker images for
5+
deployment with docker compose.
6+
7+
![img/upload.png](img/upload.png)
8+
9+
10+
## Rationale
11+
I am creating this toy example after much frustration trying to implement (something) that would be able to handle uploads of [Singularity](https://singularityware.github.io) containers. Specifically,
12+
when you set up an nginx web server and you have file uploads, you usually need to set a maximum body
13+
size so the server doesn't poop out:
14+
15+
```bash
16+
client_max_body_size 8000M;
17+
client_body_buffer_size 8000M;
18+
client_body_timeout 120;
19+
```
20+
21+
It's a game we all play - when you first create the application, you forget to set this
22+
entirely. At some point you get a suite of errors related to "this file is too big," you do a google
23+
search, find that you can define the max body (and buffer and timeout) and add to your configuration.
24+
But if you have REALLY big files, you can still have the configuration above, and have the upload fail.
25+
Then you start looking for other solutions, and here we are!
26+
27+
There were several issues ([#108](https://github.com/singularityhub/sregistry/issues/108), [#109](https://github.com/singularityhub/sregistry/issues/109), [#123](https://github.com/singularityhub/sregistry/issues/123)) with discussion that ranged from using [chunked upload](https://github.com/juliomalegria/django-chunked-upload), and then this nginx module, and in retrospect you can read the issues to
28+
see how I stumbled trying to find the best approach. I was so determined to make this easier for all future dinosaur programmers that I started working on it early one Saturday morning, and had an entire new thing (this repository) about 8 hours later. This is my dinosaur programmer superpower!
29+
30+
31+
### Chunked Upload
32+
The idea of a chunked upload speaks for itself - you break a file into chunks, and upload them separately instead of the entire thing in one go. This module to implement chunked uploading for Django worked beautifully in the browser, and if you need a good "out of the box" module this is a good approach. It was very easy for me to plug the uploader into a pretty progress bar, and my result looked like this:
33+
34+
![img/progress.png](img/progress.png)
35+
36+
The author was also very wise to provide an [example](https://github.com/juliomalegria/django-chunked-upload-demo). But getting the same functionality from the command line would be a series of hacks. I [posted an issue](https://github.com/juliomalegria/django-chunked-upload/issues/41) but did not get any response, even after many weeks. I think this solution could be very good if the work is extended to include an integration with django restful (or similar) for interaction from the command line.
37+
38+
### Chunk Upload (Restful)
39+
Another user created a [django restful framework chunked upload](https://github.com/jkeifer/drf-chunked-upload) but did not provide substantial documentation to get it working. We [came very close](https://github.com/jkeifer/drf-chunked-upload/issues/6)
40+
but I wasn't able to guess what the PUT or POST calls should look like, and I sensed we weren't going to progress very quickly.
41+
42+
### Nginx Module
43+
I at first didn't like the idea of having a file uploaded directly to my server, but actually
44+
you can integrate authentication at several levels so my concern was assuaged. For this demo I won't
45+
implement any kind of authentication, but I'll point you to resources to figure it out (I would
46+
imagine the authentication schema / details would vary based on the use case).
47+
48+
49+
# Usage
50+
51+
I've provided the application Dockerized so you don't need to install dependencies, beyond using Docker.
52+
To bring up the server:
53+
54+
```bash
55+
docker-compose up -d
56+
```
57+
58+
Then to upload, you can use either the web interface or a command line utility [push.py](push.py)
59+
60+
## Web Interface
61+
62+
Then you can navigate to [http://127.0.0.1](http://127.0.0.1) to see the portal. It's the massive
63+
purpely/pink box you see in the picture above! You can click or drop to add a file to upload.
64+
65+
When the file finishes uploading, you will see the table! You can always navigate back to the
66+
main page by clicking the home icon in the upper left.
67+
68+
![img/table.png](img/table.png)
69+
70+
## Command Line
71+
72+
I wrote you a little client to push an image to your server! Here is the usage:
73+
74+
```bash
75+
usage: push.py [-h] [--host HOST] [--port PORT] [--schema SCHEMA] file
76+
77+
Dinosaur Nginx Upload Example
78+
79+
positional arguments:
80+
file full path to file to push
81+
82+
optional arguments:
83+
-h, --help show this help message and exit
84+
--host HOST the host where the server is running
85+
--port PORT, -p PORT the port where the server is running
86+
--schema SCHEMA, -s SCHEMA
87+
http:// or https://
88+
usage: push.py [-h] [--host HOST] [--port PORT] [--schema SCHEMA] file
89+
push.py: error: the following arguments are required: file
90+
```
91+
92+
You shouldn't need to change the host or port or schema given running the Docker containers
93+
locally, but I've added those arguments in case you want to extend the script to some other
94+
server.
95+
96+
And here is an example to upload a container. Note that you will need requests and requests tool belt instlled.
97+
98+
```bash
99+
pip install requests
100+
pip install requests-toolbelt
101+
```
102+
```bash
103+
./push.py /home/vanessa/dfnworks.simg
104+
PUSH /home/vanessa/dfnworks.simg
105+
[3682 of 3682 MB]
106+
[Return status 200 Upload Complete]
107+
```
108+
109+
## Deployment
110+
If you deploy this, there are some customizations you need to take into account!
111+
112+
- **Authentication** is removed for this demo. You should add it back. You can add this at the level of the nginx module, or via a view for the application.
113+
- **https** of course this should be served with https. The server block is largely the same, but you would have another for port 443.
114+
115+
If you have any questions or need help, please don't be afraid to [reach out](https://www.github.com/vsoch/nginx-upload-module/issues). I hope that this is helpful for you!

docker-compose.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
db:
2+
image: postgres
3+
4+
uwsgi:
5+
restart: always
6+
build: .
7+
volumes:
8+
- .:/code
9+
- ./static:/var/www/static
10+
- ./images:/var/www/images
11+
links:
12+
- db
13+
14+
nginx:
15+
restart: always
16+
build: nginx
17+
ports:
18+
- "80:80"
19+
volumes:
20+
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
21+
- ./uwsgi_params.par:/etc/nginx/uwsgi_params.par:ro
22+
volumes_from:
23+
- uwsgi
24+
links:
25+
- uwsgi
26+
- db

img/progress.png

26.1 KB
Loading

img/table.png

59 KB
Loading

img/upload.png

19.8 KB
Loading

manage.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/usr/bin/env python
2+
import os
3+
import sys
4+
5+
if __name__ == "__main__":
6+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "upload.settings")
7+
try:
8+
from django.core.management import execute_from_command_line
9+
except ImportError as exc:
10+
raise ImportError(
11+
"Couldn't import Django. Are you sure it's installed and "
12+
"available on your PYTHONPATH environment variable? Did you "
13+
"forget to activate a virtual environment?"
14+
) from exc
15+
execute_from_command_line(sys.argv)

nginx.conf

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
server {
2+
listen *:80;
3+
server_name localhost;
4+
5+
client_max_body_size 8000M;
6+
client_body_buffer_size 8000M;
7+
client_body_timeout 120;
8+
9+
add_header X-Clacks-Overhead "GNU Terry Pratchett";
10+
add_header X-Clacks-Overhead "GNU Terry Pratchet";
11+
add_header Access-Control-Allow-Origin *;
12+
add_header 'Access-Control-Allow-Credentials' 'true';
13+
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
14+
add_header 'Access-Control-Allow-Headers' 'Authorization,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
15+
16+
location /images {
17+
alias /var/www/images;
18+
}
19+
20+
location / {
21+
include /etc/nginx/uwsgi_params.par;
22+
uwsgi_pass uwsgi:3031;
23+
uwsgi_max_temp_file_size 10024m;
24+
}
25+
26+
location /static {
27+
alias /var/www/static;
28+
}
29+
30+
# Upload form should be submitted to this location
31+
location /upload {
32+
33+
# After upload, pass altered request body to this django view
34+
upload_pass /complete/;
35+
36+
# Store files to this directory
37+
# The directory is hashed, subdirectories 0 1 2 3 4 5 6 7 8 9 should exist
38+
upload_store /var/www/images/_upload 1;
39+
upload_store_access user:rw group:rw all:rw;
40+
41+
# Set specified fields in request body
42+
upload_set_form_field $upload_field_name.name "$upload_file_name";
43+
upload_set_form_field $upload_field_name.content_type "$upload_content_type";
44+
upload_set_form_field $upload_field_name.path "$upload_tmp_path";
45+
46+
# Inform backend about hash and size of a file
47+
upload_aggregate_form_field "$upload_field_name.md5" "$upload_file_md5";
48+
upload_aggregate_form_field "$upload_field_name.size" "$upload_file_size";
49+
50+
# Here is where you define additional fields to pass through to upload_complete
51+
upload_pass_form_field "^submit$|^description$";
52+
upload_pass_form_field "^name$";
53+
upload_pass_form_field "^terminal$";
54+
upload_cleanup 400-599;
55+
56+
57+
}
58+
59+
}

nginx/Dockerfile

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
FROM alpine:3.4
2+
ENV NGINX_VERSION 1.13.6
3+
4+
COPY install.sh /usr/src/
5+
COPY nginx.key /usr/src/
6+
7+
RUN sh -x /usr/src/install.sh
8+
9+
COPY nginx.conf /etc/nginx/nginx.conf
10+
COPY nginx.vh.default.conf /etc/nginx/conf.d/default.conf
11+
12+
# Create hashed temporary upload locations
13+
RUN mkdir -p /var/www/images/_upload/0 && \
14+
mkdir -p /var/www/images/_upload/1 && \
15+
mkdir -p /var/www/images/_upload/2 && \
16+
mkdir -p /var/www/images/_upload/3 && \
17+
mkdir -p /var/www/images/_upload/4 && \
18+
mkdir -p /var/www/images/_upload/5 && \
19+
mkdir -p /var/www/images/_upload/6 && \
20+
mkdir -p /var/www/images/_upload/7 && \
21+
mkdir -p /var/www/images/_upload/8 && \
22+
mkdir -p /var/www/images/_upload/9 && \
23+
chmod 777 -R /var/www/images/_upload
24+
25+
EXPOSE 80 443
26+
27+
CMD ["nginx", "-g", "daemon off;"]

0 commit comments

Comments
 (0)