我正在尝试将我的应用程序迁移到docker容器,但遇到了使用django表单上传文件的问题。一切正常,但是当我尝试从我的应用程序中的表单上传任何文件时,我什么都没得到,例如multipart/form-data
没有在html表单标签中设置。但如果我直接安装应用程序而没有泊坞工程,它就会设置并且每件事都有效。无论如何这是我的配置,我希望有人可以帮助我。
这是我的docker-compose.yml
version: '2'
services:
db_postgres:
build:
context: .
dockerfile: dockerfiles/docker-postgres/Dockerfile
args:
- db_user=username
- db_name=databasename
- db_pass=password
environment:
LC_ALL: C.UTF-8
app:
restart: always
build:
context: .
dockerfile: dockerfiles/docker-app/Dockerfile
links:
- db_postgres:db_postgres
nginx:
restart: always
build: dockerfiles/docker-nginx
volumes_from:
- app
ports:
- "80:80"
- "443:443"
links:
- app:applink
这是应用程序Dockerfile:
FROM ubuntu:16.04
RUN \
apt-get update && \
apt-get install -y python-pip python-dev build-essential python-virtualenv && \
apt-get install -y libjpeg8 libjpeg62-dev libfreetype6 libfreetype6-dev && \
apt-get install -y libpq-dev libffi-dev && \
apt-get install -y libssl-dev git
RUN mkdir app
COPY requrements.txt /app
RUN pip install --upgrade pip
RUN pip install -r /app/requrements.txt
ADD . /app
WORKDIR /app
VOLUME ["/app/staticfiles/", "/app/media/", "/app/protected/"]
# I tried this but it seams no effect at all
# this is media folders where users can upload their files
# nginx can read from this folders with no problem
# I tried to docker exec and nginx can even write there
# anyways I tried to start nginx as root
RUN chown www-data:www-data -R /app/media/
RUN chown www-data:www-data -R /app/protected/
ADD dockerfiles/docker-app/django_entrypoint.sh .
RUN chmod +x django_entrypoint.sh
CMD ["./django_entrypoint.sh"]
EXPOSE 8000
这是django_entrypoint.sh
#!/bin/bash
NAME=my_app_name
USER=www-data
GROUP=www-data
NUM_WORKERS=8
DJANGO_WSGI_MODULE=my_application.wsgi
python manage.py makemigrations
python manage.py migrate
echo "Collenting staticfiles..."
python manage.py collectstatic --noinput > /dev/null
python manage.py initadmin
python manage.py init_default_settings
exec gunicorn ${DJANGO_WSGI_MODULE}:application \
--name $NAME \
--workers $NUM_WORKERS \
--user=$USER --group=$GROUP \
--bind=:8000 \
--log-level=debug \
--capture-output
这里是nginx Dockerfile:
FROM ubuntu:16.04
# Install Nginx.
RUN apt-get update && \
apt-get install -y nginx && \
rm -rf /var/lib/apt/lists/* && \
echo "\ndaemon off;" >> /etc/nginx/nginx.conf && \
chown -R www-data:www-data /var/lib/nginx
# Define mountable directories.
VOLUME ["/etc/nginx/sites-available", "/etc/nginx/certs", "/etc/nginx/conf.d"]
ADD confgfile /etc/nginx/sites-available/
RUN rm /etc/nginx/sites-enabled/default && rm /etc/nginx/sites-available/default
RUN ln -s /etc/nginx/sites-available/ctrd /etc/nginx/sites-enabled/ctrd
# Define default command.
CMD ["nginx"]
# Expose ports.
EXPOSE 80
EXPOSE 443
这是nginx的配置文件:
server {
listen 80;
# I pasted my server ip in sever name
server_name 175.116.110.231;
client_max_body_size 300M;
error_log stdout debug;
location /static/ {
alias /app/staticfiles/;
}
location /media/ {
alias /app/media/;
}
location / {
try_files $uri @proxy;
}
location @proxy {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-FILE $request_body_file;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://applink:8000;
}
location /private-uploads/ {
internal;
alias /app/protected/records/;
}
}
该应用程序运行正常,但是当我想上传任何文件时,它什么都不做,没有文件,错误日志中没有错误。如果filefield不能为空(在django表单中requred = True),我得到了关于它的错误。 根据broswer工具的要求:
Request URL:http://175.116.110.231/edit-avatar/2
Request Method:POST
Status Code:302 Found
Remote Address:175.116.110.231:80
Response Headers
view source
Connection:keep-alive
Content-Language:ru
Content-Type:text/html; charset=utf-8
Date:Mon, 19 Sep 2016 13:26:59 GMT
Location:/profile/
Server:nginx/1.10.0 (Ubuntu)
Transfer-Encoding:chunked
Vary:Accept-Language, Cookie
X-Frame-Options:SAMEORIGIN
Request Headers
view source
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate
Accept-Language:ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4
Cache-Control:max-age=0
Connection:keep-alive
Content-Length:30316
Content-Type:multipart/form-data; boundary=----WebKitFormBoundarySCXVqlqPsCHyaHtU
Cookie:JSESSIONID=dummy; sessionid=htnbd9coal4ws2ansxzze8bhtu4fq6do; csrftoken=tCD5cVrR0IUGkjkkbJKDsxdRrtyLIUGbOIHkjHKjhkjhmnVJhvKUGkBDjZ
DNT:1
Host:175.116.110.231
Origin:http://175.116.110.231
Referer:http://175.116.110.231/edit-avatar/2
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.90 Safari/537.36 Vivaldi/1.4.589.11
Request Payload
------WebKitFormBoundarySCXVqlqPsCHyaHtU
Content-Disposition: form-data; name="csrfmiddlewaretoken"
zaAvb1KoCCovbuCbik261UDeZgQbJdumzcvpcqOHqTKIrRN826lEoeb5AvU7SrG6
------WebKitFormBoundarySCXVqlqPsCHyaHtU
Content-Disposition: form-data; name="avatar"; filename="ava2.jpg"
Content-Type: image/jpeg
------WebKitFormBoundarySCXVqlqPsCHyaHtU--
启动时更新gunicorn调试输出:
ctrd_app_1 | [2016-09-19 16:33:47 +0000] [1] [DEBUG] Current configuration:
ctrd_app_1 | secure_scheme_headers: {'X-FORWARDED-PROTOCOL': 'ssl', 'X-FORWARDED-PROTO': 'https', 'X-FORWARDED-SSL': 'on'}
ctrd_app_1 | proxy_protocol: False
ctrd_app_1 | worker_connections: 1000
ctrd_app_1 | statsd_host: None
ctrd_app_1 | max_requests_jitter: 0
ctrd_app_1 | post_fork: <function post_fork at 0x7fb7e74ee230>
ctrd_app_1 | pythonpath: None
ctrd_app_1 | enable_stdio_inheritance: False
ctrd_app_1 | worker_class: sync
ctrd_app_1 | ssl_version: 3
ctrd_app_1 | suppress_ragged_eofs: True
ctrd_app_1 | syslog: False
ctrd_app_1 | syslog_facility: user
ctrd_app_1 | when_ready: <function when_ready at 0x7fb7e74e5ed8>
ctrd_app_1 | pre_fork: <function pre_fork at 0x7fb7e74ee0c8>
ctrd_app_1 | cert_reqs: 0
ctrd_app_1 | preload_app: False
ctrd_app_1 | keepalive: 2
ctrd_app_1 | accesslog: None
ctrd_app_1 | group: 33
ctrd_app_1 | graceful_timeout: 30
ctrd_app_1 | do_handshake_on_connect: False
ctrd_app_1 | spew: False
ctrd_app_1 | workers: 8
ctrd_app_1 | proc_name: django_ctrd_app
ctrd_app_1 | sendfile: None
ctrd_app_1 | pidfile: None
ctrd_app_1 | umask: 0
ctrd_app_1 | on_reload: <function on_reload at 0x7fb7e74e5d70>
ctrd_app_1 | pre_exec: <function pre_exec at 0x7fb7e74ee7d0>
ctrd_app_1 | worker_tmp_dir: None
ctrd_app_1 | post_worker_init: <function post_worker_init at 0x7fb7e74ee398>
ctrd_app_1 | limit_request_fields: 100
ctrd_app_1 | on_exit: <function on_exit at 0x7fb7e74eee60>
ctrd_app_1 | config: None
ctrd_app_1 | logconfig: None
ctrd_app_1 | check_config: False
ctrd_app_1 | statsd_prefix:
ctrd_app_1 | proxy_allow_ips: ['127.0.0.1']
ctrd_app_1 | pre_request: <function pre_request at 0x7fb7e74ee938>
ctrd_app_1 | post_request: <function post_request at 0x7fb7e74eea28>
ctrd_app_1 | user: 33
ctrd_app_1 | forwarded_allow_ips: ['127.0.0.1']
ctrd_app_1 | worker_int: <function worker_int at 0x7fb7e74ee500>
ctrd_app_1 | threads: 1
ctrd_app_1 | max_requests: 0
ctrd_app_1 | limit_request_line: 4094
ctrd_app_1 | access_log_format: %(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"
ctrd_app_1 | certfile: None
ctrd_app_1 | worker_exit: <function worker_exit at 0x7fb7e74eeb90>
ctrd_app_1 | chdir: /app
ctrd_app_1 | paste: None
ctrd_app_1 | default_proc_name: calltrade.wsgi:application
ctrd_app_1 | errorlog: -
ctrd_app_1 | loglevel: debug
ctrd_app_1 | capture_output: True
ctrd_app_1 | syslog_addr: udp://localhost:514
ctrd_app_1 | syslog_prefix: None
ctrd_app_1 | daemon: False
ctrd_app_1 | ciphers: TLSv1
ctrd_app_1 | on_starting: <function on_starting at 0x7fb7e74e5c08>
ctrd_app_1 | worker_abort: <function worker_abort at 0x7fb7e74ee668>
ctrd_app_1 | bind: [':8000']
ctrd_app_1 | raw_env: []
ctrd_app_1 | reload: False
ctrd_app_1 | limit_request_field_size: 8190
ctrd_app_1 | nworkers_changed: <function nworkers_changed at 0x7fb7e74eecf8>
ctrd_app_1 | timeout: 30
ctrd_app_1 | ca_certs: None
ctrd_app_1 | django_settings: None
ctrd_app_1 | tmp_upload_dir: None
ctrd_app_1 | keyfile: None
ctrd_app_1 | backlog: 2048
ctrd_app_1 | logger_class: gunicorn.glogging.Logger
ctrd_app_1 | [2016-09-19 16:33:47 +0000] [1] [INFO] Starting gunicorn 19.6.0
ctrd_app_1 | [2016-09-19 16:33:47 +0000] [1] [DEBUG] Arbiter booted
ctrd_app_1 | [2016-09-19 16:33:47 +0000] [1] [INFO] Listening at: http://0.0.0.0:8000 (1)
ctrd_app_1 | [2016-09-19 16:33:47 +0000] [1] [INFO] Using worker: sync
ctrd_app_1 | [2016-09-19 16:33:47 +0000] [38] [INFO] Booting worker with pid: 38
ctrd_app_1 | [2016-09-19 16:33:47 +0000] [39] [INFO] Booting worker with pid: 39
ctrd_app_1 | [2016-09-19 16:33:47 +0000] [44] [INFO] Booting worker with pid: 44
ctrd_app_1 | [2016-09-19 16:33:47 +0000] [47] [INFO] Booting worker with pid: 47
ctrd_app_1 | [2016-09-19 16:33:47 +0000] [48] [INFO] Booting worker with pid: 48
ctrd_app_1 | [2016-09-19 16:33:47 +0000] [51] [INFO] Booting worker with pid: 51
ctrd_app_1 | [2016-09-19 16:33:47 +0000] [54] [INFO] Booting worker with pid: 54
ctrd_app_1 | [2016-09-19 16:33:48 +0000] [59] [INFO] Booting worker with pid: 59
ctrd_app_1 | [2016-09-19 16:33:48 +0000] [1] [DEBUG] 8 workers
我也试过8000:8000端口暴露在gunicorn容器上它也无法保存文件,所以原因不是nginx,但我的docker卷配置错误可能。有人可以解释码头卷吗?请检查我的卷配置,我一定不明白它应该如何工作。 请帮忙。
以下是媒体投放视图:
@login_required
def private_media_response(request, username, filename):
"""
In the nginx setting we can use something like here:
location /private-uploads/ {
internal;
alias /place/to/private/media/;
}
"""
user = request.user
if user.username == username:
response = HttpResponse()
url = '/private-uploads/{0}/{1}'.format(username, filename)
response.status_code = 200
response['X-Accel-Redirect'] = url.encode('utf-8')
response['X-Accel-Buffering'] = 'yes'
return response
else:
return HttpResponseForbidden("Restricted Access")
问题不在于获取文件,我无法使用表单保存。 我保存文件的所有观点都看起来像模型更新CBV或这个标准视图:
@login_requred
def file_save_view(request, **kwags):
if request.POST:
form = MyForm(request.POST, request.FILES)
if form.is_valid()
form.save()
else:
form = myForm();
return render(request, 'some_template.html', {'form':form})
Standart模型在媒体中保存文件数据,对于我创建的受保护文件FileSystemStorage
location = os.path.join(settings.BASE_DIR, 'protected')
fs = FileSystemStorage(location=location)
然后在模型中使用它:
def get_upload_path(instance, filename):
user = instance.user
return 'documents/{0}/{1}'.format(user, filename)
class MyModel(models.Model):
date = models.DateTimeField(auto_now=True, blank=False)
user = models.ForeignKey(User)
document = models.FileField(
upload_to=get_upload_path,
storage=fs,
)
pass
这段代码有点抽象,但它适用于标准部署方式。我想,卷一定是错的。
答案 0 :(得分:2)
这个问题对我来说非常奇怪。
有型号:
use-kss-for-demo
如果我在这里删除class Document(Model):
name = CharField()
file = ImageField(
upload_to='documents/%Y/%m/',
default='default.doc'
)
,它就可以了!但是默认的版本在docker之外工作正常。
答案 1 :(得分:1)
所以,你似乎在某种程度上误解了音量(虽然我没有看到任何可能导致你的问题的东西)。
卷来自Docker用于其图像的“写入时复制”文件系统。每个图层仅包含自上次使用以来未更改的新信息 - 这使您可以高效地构建图像,而无需为从该图像开始的每个容器复制大量数据。
卷正在说“不要在此目录的写文件系统上使用该副本”。所以VOLUME ["/etc/nginx/sites-available", "/etc/nginx/certs", "/etc/nginx/conf.d"]
实际上告诉Docker将这些目录留在你构建的图像中。这不是你想要的...我看到你将配置文件添加到你的nginx配置后将配置目录标记为卷...这些文件更改不会传播到你正在构建的图像中。
关于你的实际问题 - 我不知道那是什么。没有任何部署配置与“保存文件”有关。唯一可能的地方就是你试图从django保存它然后用nginx备份它...但是你说文件没有被保存,这意味着nginx与它无关。我会向你的应用程序本身添加一些调试输出,以试图弄清楚发生了什么。如果它返回200而没有按照它说的那样(将一些文件保存到磁盘)......那就是我要开始的地方。