文件上传错误请求
Bad Request on File upload
我已经按照本教程中所述使用 nginx 和 gunicorn 设置了我的 python 项目 https://djangocentral.com/deploy-django-with-nginx-gunicorn-postgresql-and-lets-encrypt-ssl-on-ubuntu/
到目前为止一切正常。
我的项目在 /root/project_folder/
我想通过管理页面上传文件,但收到错误请求错误 400。我想将文件添加到的媒体文件夹是 /var/www/my_domain/media(归 www-data 组所有)。这也已正确配置,因为当我手动将它们移动到该文件夹时我可以看到图像。
你们知道为什么会出现这个问题吗?
主要问题是上传时我需要将图像保存到媒体根目录:
/var/www/domain/media/images/dad/ProfilePicture/offline_screen.png
但是当我发送请求查看图像时,Web 服务器 return 如下:
/media/var/www/domain/media/images/dad/ProfilePicture/offline_screen.png
upload_to 路径需要是 images/dad/ProfilePicture/offline_screen.png 才能正确请求,但随后图像被上传到错误的文件夹
有什么想法吗?提前致谢!
更新:
这是我的 nginx 配置
`
server {
server_name domain.net www.domain.net ip_address;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /var/www/domain;
}
location = /media/ {
root /var/www/domain;
}
location / {
include proxy_params;
proxy_pass http://unix:/var/log/gunicorn/domain.sock;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/domain/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/domain/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = www.domain) {
return 301 https://$host$request_uri;
} # managed by Certbot
if ($host = domain) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name domain www.domain ip_address;
return 404; # managed by Certbot
}
`
这是我的媒体根目录/媒体 url:
MEDIA_ROOT = '/var/www/domain/media' #os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
url.py:
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('webpages.urls')),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL,document_root=settings.STATIC_ROOT)
最后是我如何提供和上传图片:
def get_path_of_content_image(instance, filename):
return os.path.join(settings.MEDIA_ROOT, "images", str(instance.course.topic), str(instance.course), filename)
class Topic(models.Model):
title = models.CharField(max_length=100)
description = models.TextField(max_length=445)
image = models.ImageField(upload_to=get_topic_profile_path, default="images/default.jpeg")
def __str__(self):
return self.title
def image_tag(self):
if self.image:
return mark_safe('<img src="%s" style="width: 200px; height:200px;" />' % self.image.get_image())
def get_image(self):
if self.image:
return str(self.image.url).replace('/media/var/www/domain', '') ########1
else:
return settings.STATIC_ROOT + 'webpages/img/default.jpeg'
image_tag.short_description = 'Current Image'
我写了这一行 #######1 因为将上传路径设置为完整的媒体根目录但是为了 return 正确的路径我需要删除媒体根目录前缀。而且非常重要的是,这仅在 DEBUg 设置为 TRUE 时有效。
我不确定这是否能解决您的问题,但这里有两处需要正确配置。如果 nginx 正在为您的媒体目录中的文件提供服务,那么我猜您的 nginx 配置已经可以了,您只需要编辑 settings.py,但我会包括 nginx 配置以防它帮助其他人。
更新
我不认为你的模型中的那些方法是必要的,虽然我真的不知道它们是如何使用的。这是我如何使用 ImageField
:
配置模型的示例
首先,如何上传和保存图片
(让 django 完成大部分工作)
models.py
...
class Post(models.Model):
"""Represents a single blog post"""
# model fields
title = models.CharField(max_length=150, unique=True)
content = models.TextField()
title_image = models.ImageField(upload_to='image/', blank=True, null=True)
...
forms.py
...
class PostModelForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'title_image', 'content']
widgets = {
'content': Textarea(attrs={'cols': 80, 'rows': 25}),
}
...
views.py
...
def new_post(request):
context = {}
template = 'new_post.html'
# POST
if request.method == 'POST':
form = PostModelForm(request.POST or None, request.FILES)
if form.is_valid():
new_post = form.save()
return redirect('list_post')
else:
context['form'] = form
return render(request, template, context)
# GET
else:
context['form'] = PostModelForm()
return render(request, template, context)
...
new_post.html
(注意表格的 enctype
- 如果您想上传文件,这很重要)
...
<div class="card bg-light">
<div class="card-header">
<h3>New Post</h3>
</div>
<div class="card-body">
<form action="{% url 'blog:new_post' %}" method="POST" class="w-100" enctype="multipart/form-data">
<div class="col-12">
{% csrf_token %}
{{ form.as_p }}
</div>
<div class="col-12">
<ul class="actions">
<button class="primary" type="submit">Save</button>
</ul>
</div>
</form>
</div>
</div>
...
其次,如何提供您的图像
现在您想在标记中显示图像
在views.py
...
def detail(request, id):
context = {}
template_name = 'post.html'
post = get_object_or_404(Post, id=id)
context['post'] = post
return render(request, template_name, context)
...
在 post.html
中的标记中
...
{% if post.title_image %}
<span class="img"><img src="{{ post.title_image.url }}" alt="image for {{ post.title }}" /></span>
{% endif %}
...
(1) 在settings.py
中定义MEDIA_ROOT和MEDIA_URL
在settings.py
中添加
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
注意:阅读 MEDIA_ROOT and MEDIA_URL
上的 django 文档
(2) 在 nginx 服务器块中定义媒体位置
在该教程中,nginx 配置没有为媒体定义位置。这是教程中的服务器块:
server {
listen 80;
server_name server_domain_or_IP;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /path/to/staticfiles;
}
location / {
include proxy_params;
proxy_pass http://unix:/var/log/gunicorn/project_name.sock;
}
}
我觉得应该是这样的:
server {
listen 80;
server_name server_domain_or_IP;
location = /favicon.ico { access_log off; log_not_found off; }
location /media/ {
root /path/to/media;
}
location /static/ {
root /path/to/staticfiles;
}
location / {
include proxy_params;
proxy_pass http://unix:/var/log/gunicorn/project_name.sock;
}
}
如果该配置不起作用,我在下面发布了适合我的配置
然而,根据我自己使用 django、nginx 和 gunicorn 的经验,我的 nginx 配置如下所示:
server {
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /etc/letsencrypt/live/website_name.com-0001/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/website_name.com-0001/privkey.pem; # managed by Certbot
server_name website_name.com www.website_name.com;
location = /favicon.ico { access_log off; log_not_found off; }
location /media {
alias /home/user_name/website_name/project_name/media;
}
location /static/ {
alias /home/user_name/website_name/project_name/static_root/;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
}
所以在花了很长时间调试之后,我找到了解决问题的方法:
首先出现问题的是 nginx 文件中 'location' 和“/media/”之间的“=”。确保这不是错误的根源。它应该是这样的:
location /media {
alias /home/user_name/website_name/project_name/media;
}
而不是这样:
location = /media {
alias /home/user_name/website_name/project_name/media;
}
文件上传到自定义本地目录可以这样完成:
from django.core.files.storage import FileSystemStorage
fs = FileSystemStorage(location=settings.MEDIA_ROOT)
并为模型在上传时添加文件存储选项(或之后使用更新后的路径更改图像名称):
image = models.ImageField(upload_to=get_topic_profile_path, default="images/default.jpeg", storage=fs)
感谢其他回答。通过修复这个错误,我学到了很多东西 :D
我已经按照本教程中所述使用 nginx 和 gunicorn 设置了我的 python 项目 https://djangocentral.com/deploy-django-with-nginx-gunicorn-postgresql-and-lets-encrypt-ssl-on-ubuntu/
到目前为止一切正常。 我的项目在 /root/project_folder/
我想通过管理页面上传文件,但收到错误请求错误 400。我想将文件添加到的媒体文件夹是 /var/www/my_domain/media(归 www-data 组所有)。这也已正确配置,因为当我手动将它们移动到该文件夹时我可以看到图像。
你们知道为什么会出现这个问题吗?
主要问题是上传时我需要将图像保存到媒体根目录:
/var/www/domain/media/images/dad/ProfilePicture/offline_screen.png
但是当我发送请求查看图像时,Web 服务器 return 如下:
/media/var/www/domain/media/images/dad/ProfilePicture/offline_screen.png
upload_to 路径需要是 images/dad/ProfilePicture/offline_screen.png 才能正确请求,但随后图像被上传到错误的文件夹
有什么想法吗?提前致谢!
更新:
这是我的 nginx 配置 `
server {
server_name domain.net www.domain.net ip_address;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /var/www/domain;
}
location = /media/ {
root /var/www/domain;
}
location / {
include proxy_params;
proxy_pass http://unix:/var/log/gunicorn/domain.sock;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/domain/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/domain/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = www.domain) {
return 301 https://$host$request_uri;
} # managed by Certbot
if ($host = domain) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name domain www.domain ip_address;
return 404; # managed by Certbot
}
`
这是我的媒体根目录/媒体 url:
MEDIA_ROOT = '/var/www/domain/media' #os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
url.py:
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('webpages.urls')),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL,document_root=settings.STATIC_ROOT)
最后是我如何提供和上传图片:
def get_path_of_content_image(instance, filename):
return os.path.join(settings.MEDIA_ROOT, "images", str(instance.course.topic), str(instance.course), filename)
class Topic(models.Model):
title = models.CharField(max_length=100)
description = models.TextField(max_length=445)
image = models.ImageField(upload_to=get_topic_profile_path, default="images/default.jpeg")
def __str__(self):
return self.title
def image_tag(self):
if self.image:
return mark_safe('<img src="%s" style="width: 200px; height:200px;" />' % self.image.get_image())
def get_image(self):
if self.image:
return str(self.image.url).replace('/media/var/www/domain', '') ########1
else:
return settings.STATIC_ROOT + 'webpages/img/default.jpeg'
image_tag.short_description = 'Current Image'
我写了这一行 #######1 因为将上传路径设置为完整的媒体根目录但是为了 return 正确的路径我需要删除媒体根目录前缀。而且非常重要的是,这仅在 DEBUg 设置为 TRUE 时有效。
我不确定这是否能解决您的问题,但这里有两处需要正确配置。如果 nginx 正在为您的媒体目录中的文件提供服务,那么我猜您的 nginx 配置已经可以了,您只需要编辑 settings.py,但我会包括 nginx 配置以防它帮助其他人。
更新
我不认为你的模型中的那些方法是必要的,虽然我真的不知道它们是如何使用的。这是我如何使用 ImageField
:
首先,如何上传和保存图片
(让 django 完成大部分工作)
models.py
...
class Post(models.Model):
"""Represents a single blog post"""
# model fields
title = models.CharField(max_length=150, unique=True)
content = models.TextField()
title_image = models.ImageField(upload_to='image/', blank=True, null=True)
...
forms.py
...
class PostModelForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'title_image', 'content']
widgets = {
'content': Textarea(attrs={'cols': 80, 'rows': 25}),
}
...
views.py
...
def new_post(request):
context = {}
template = 'new_post.html'
# POST
if request.method == 'POST':
form = PostModelForm(request.POST or None, request.FILES)
if form.is_valid():
new_post = form.save()
return redirect('list_post')
else:
context['form'] = form
return render(request, template, context)
# GET
else:
context['form'] = PostModelForm()
return render(request, template, context)
...
new_post.html
(注意表格的 enctype
- 如果您想上传文件,这很重要)
...
<div class="card bg-light">
<div class="card-header">
<h3>New Post</h3>
</div>
<div class="card-body">
<form action="{% url 'blog:new_post' %}" method="POST" class="w-100" enctype="multipart/form-data">
<div class="col-12">
{% csrf_token %}
{{ form.as_p }}
</div>
<div class="col-12">
<ul class="actions">
<button class="primary" type="submit">Save</button>
</ul>
</div>
</form>
</div>
</div>
...
其次,如何提供您的图像
现在您想在标记中显示图像
在views.py
...
def detail(request, id):
context = {}
template_name = 'post.html'
post = get_object_or_404(Post, id=id)
context['post'] = post
return render(request, template_name, context)
...
在 post.html
...
{% if post.title_image %}
<span class="img"><img src="{{ post.title_image.url }}" alt="image for {{ post.title }}" /></span>
{% endif %}
...
(1) 在settings.py
中定义MEDIA_ROOT和MEDIA_URL在settings.py
中添加
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
注意:阅读 MEDIA_ROOT and MEDIA_URL
上的 django 文档(2) 在 nginx 服务器块中定义媒体位置
在该教程中,nginx 配置没有为媒体定义位置。这是教程中的服务器块:
server {
listen 80;
server_name server_domain_or_IP;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /path/to/staticfiles;
}
location / {
include proxy_params;
proxy_pass http://unix:/var/log/gunicorn/project_name.sock;
}
}
我觉得应该是这样的:
server {
listen 80;
server_name server_domain_or_IP;
location = /favicon.ico { access_log off; log_not_found off; }
location /media/ {
root /path/to/media;
}
location /static/ {
root /path/to/staticfiles;
}
location / {
include proxy_params;
proxy_pass http://unix:/var/log/gunicorn/project_name.sock;
}
}
如果该配置不起作用,我在下面发布了适合我的配置 然而,根据我自己使用 django、nginx 和 gunicorn 的经验,我的 nginx 配置如下所示:
server {
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /etc/letsencrypt/live/website_name.com-0001/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/website_name.com-0001/privkey.pem; # managed by Certbot
server_name website_name.com www.website_name.com;
location = /favicon.ico { access_log off; log_not_found off; }
location /media {
alias /home/user_name/website_name/project_name/media;
}
location /static/ {
alias /home/user_name/website_name/project_name/static_root/;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
}
所以在花了很长时间调试之后,我找到了解决问题的方法:
首先出现问题的是 nginx 文件中 'location' 和“/media/”之间的“=”。确保这不是错误的根源。它应该是这样的:
location /media {
alias /home/user_name/website_name/project_name/media;
}
而不是这样:
location = /media {
alias /home/user_name/website_name/project_name/media;
}
文件上传到自定义本地目录可以这样完成:
from django.core.files.storage import FileSystemStorage
fs = FileSystemStorage(location=settings.MEDIA_ROOT)
并为模型在上传时添加文件存储选项(或之后使用更新后的路径更改图像名称):
image = models.ImageField(upload_to=get_topic_profile_path, default="images/default.jpeg", storage=fs)
感谢其他回答。通过修复这个错误,我学到了很多东西 :D