Django2 Web 实战03-文件上传

作者:Hubery 时间:2018.10.31

接上文:接上文:Django2 Web 实战02-用户注册登录退出

视频是一种可视化媒介,因此视频数据库至少应该存储图像。让用户上传文件是个很大的隐患,因此接下来会讨论这俩话题:文件上传,安全隐患。

  • 新增一个文件上传函数,让用户给movie上传图片
  • 检查OWASP列举的前10项安全隐患

我们会检查文件上传的安全隐患。可以看下Django帮我们做了什么,以及什么地方我们应该做出谨慎的决策。

1. 文件上传

这里,我们会创建一个model,展示和管理要上传到网站上的文件;然后,创建一个form和视图来验证和处理上传过程。

1.1 准备文件上传配置项

开始着手文件上传之前,我们需要知道,文件上传取决于一系列的设置,且这些设置在开发环境和生产环境上是不同的。这些设置会影响文件的存储方式和访问方式。 Django有两套文件配置:STATIC_* 和MEDIA_*。 Static文件是我们项目的一部分,比如(CSS,JS)。 Media文件是用户上传到我们系统中的文件。Media文件不应被信任,切不能执行。 我们将会在settings.py文件中设置这两个地方:

MEDIA_URL = '/uploaded'
MEDIA_ROOT = os.path.join(BASE_DIR, '../media_root')
复制代码

MEDIA_URL, 是用来给上传的文件服务的URL。 开发环境中,这个值无关紧要,同样不会与我们视图中的URL冲突。 生产环境中,上传的文件应该给一个与我们工程中任何app不同的域URL,同时还不能是子域。 用户的浏览器被欺骗执行它从同一域(或子域)中请求来的文件,因为我们的app将信任该与我们用户cookie(包括session ID)相同的文件。 所有浏览器的默认策略是:同源策略(Same Origin Policy)。 MEDIA_ROOT是Django保存代码目录的路径。 我们应该确保该目录不在我们的工程代码目录下,这样就不会意外的将该目录加入版本控制范围,或者意外的授予该目录文件一些特定的权限,如执行。 在生产环境中,还有其他的配置项需要配置,如限制请求body等,这些会在后续的部分讨论。

接下来,创建media_root目录: 命令行至:与我们的项目最外层目录平级

mkdir media_root
ls 
复制代码

1.2 创建MovieImage模型

MovieImage模型用一个新的字段ImageField来存储文件,同时也会验证该文件是否是图片。尽管ImageField会验证该字段,但仅仅靠阻止那些制造恶意文件的用户是不够的(但会帮助意外点击.zip文件的用户,而不是.png的用户)。 Django用Pillow库来做验证,所以先添加Pillow库到环境中:

pip install Pillow
复制代码

默认在命令行中直接pip install Pillow,安装的是最新版本; 另外提供一种更优雅的命令行安装方式:

touch requirements.dev.txt //创建文件
vi requirements.dev.txt // 编辑文件
// 输入版本号 Pillow<4.4.0 然后保存
pip install -r requirements.dev.txt // 执行py库安装
复制代码

接下来开始创建model: core/models.py

def movie_directory_path_with_uuid(instance, filename):return '{}/{}'.format(instance.movie_id, uuid4())class MovieImage(models.Model):image = models.ImageField(upload_to=movie_directory_path_with_uuid)uploaded = models.DateTimeField(auto_now_add=True)movie = models.ForeignKey('Movie', on_delete=models.CASCADE)user = models.ForeignKey(settings.AUTH_PASSWORD_VALIDATORS,on_delete=models.CASCADE)
复制代码

ImageFieldFileField的一个特殊字段,用Pillow来确认一个文件是否是图片。ImageFieldFileField使用Django的文件存储API来工作(提供了一种读取文件的方式),同时可以进行文件的读写。 Django自带了FileSystemStorage,实现了存储API将文件数据存储到本地文件系统上。这对开发来说足够了,但后续我们会考虑替代方案。

我们用ImageFieldupload_to参数来指定一个方法,用来生成上传文件的名字。我们不希望用户可以在我们的系统中指定文件的名字,因为他们可能会滥用一些用户信任的名字,从而使我们难堪。鉴于此,我们使用一个函数将指定的movie的所有图片存储在同一目录中,同时用uuid4为每个文件生成一个通用的名字(这也避免了名字冲突和处理文件之间的相互覆盖问题)。

我们同时会记录是谁上传的文件,这样如果我们发现一个坏的文件,相当于提供了一种如何找到其他坏文件的线索。

模型创建完,更新数据库:

python manage.py makemigrations core
复制代码

有了模型,就可以创建其他部分,如表单和视图。

1.3 创建和使用MovieImageForm

MovieImageForm和之前的VoteForm相似,它会隐藏和禁用模型所需的movie和user字段,这很难取得客户的信任。

编辑core/forms.py

# 添加文件上传form
class MovieImageForm(forms.ModelForm):movie = forms.ModelChoiceField(widget=forms.HiddenInput,queryset=Movie.objects.all(),disabled=True,)user = forms.ModelChoiceField(widget=forms.HiddenInput,queryset=get_user_model().objects.all(),disabled=True,)class Meta:model = MovieImagefields = ('image', 'user', 'movie')
复制代码

表单ModelForm中,我们没有重写MovieImage的image字段,因为ModelForm会自动提供一正确的文件选择框:<input type="file">。

现在我们在视图MovieDetail中使用这个表单, core/views.py:

# movie详情 视图
class MovieDetail(DetailView):queryset = Movie.objects.all_with_related_persons_and_score()def get_context_data(self, **kwargs):ctx = super().get_context_data(**kwargs)# 配置图片上传表单ctx['image_form'] = self.movie_image_form()# 其他 略# 添加图片上传表单def movie_image_form(self):if self.request.user.is_authenticated:return MovieImageForm()return None
复制代码

这里的上传代码比较简单,只能上传新图片,没有其他操作,一只提供一个空表单。然而通过这种方式我们不能显示错误信息。实践中,丢失error信息不是很好的做法。

1.4 更新模版movie_detail.html显示和上传图片

我们需要对movie_detail.html模版进行两次更新。

  • 需要更新main模版的block新增一个图片列表。
  • 需要更新sidebar模版的block包含我们新建的上传表单。

编辑core/templates/core/movie_detail.html

{% extends 'base.html' %}{% block title %}{{ object.title }} - {{ block.super }}
{% endblock %}{% block main %}<h1>{{ object }}</h1><p class="lead">{{ object.plot }}</p>{# 展示电影图片列表 #}<div class="col"><h1>{{ object }}</h1><p class="lead"> {{ object.plot }}</p></div><ul>{% for i in object.movieimage_set.all %}<li class="list-inline-item"><img src="{{ i.image.url }}"></li>{% endfor %}</ul><p>由 {{ object.director }} 执导。</p>
{% endblock %}{% block sidebar %}{# 电影排名部分 #}<div>这个电影排名:<span class="badge badge-primary">{{ object.get_rating_display }}</span></div><div><h2>该片得分:{{ object.score|default_if_none:"TBD-暂无得分" }}</h2></div>{# 文件上传部分 #}{% if image_form %}<div><h2>上传新图片</h2><form method="post"enctype="multipart/form-data"action="{% url 'core:MovieImageUpload' movie_id=object.id %}">{% csrf_token %}{{ image_form.as_p }}<p><button class="but btn-primary">上传</button></p></form></div>{% endif %}{# 投票部分 #}{% if vote_form %}<form method="post" action="{{ vote_form_url }}">{% csrf_token %}{{ vote_form.as_p }}<button class="btn btn-primary">投票</button></form>{% else %}<p> 先登录,再给此电影投票</p>{% endif %}
{% endblock %}
复制代码

更新movie_detail.html的main和sidebar部分。 main block中,用image字段的url属性,返回MEDIA_URL中设置的URL,再与计算的名字相拼接,然后我们可以通过tag找到正确的图片。 sidebar block中,form tag中一定要引入enctype属性,以便可以让上传的文件与请求的属性相关联。

模版升级完成,可以开始创建保存上传文件的视图了:MovieImageUpload。

1.5 创建MovieImageUpload视图

编辑core/views.py文件

# 创建图片上传视图
class MovieImageUpload(LoginRequiredMixin, CreateView):form_class = MovieImageFormdef get_initial(self):initial = super().get_initial()initial['user'] = self.request.user.idinitial['movie'] = self.kwargs['movie_id']return initialdef render_to_response(self, context, **response_kwargs):movie_id = self.kwargs['movie_id']movie_detail_url = reverse('core:MovieDetail',kwargs={'pk': movie_id})return redirect(to=movie_detail_url)def get_success_url(self):movie_id = self.kwargs['movie_id']movie_detail_url = reverse('core:MovieDetail', kwargs={'pk': movie_id})return movie_detail_url
复制代码

视图再一次做了验证和保存模型的所有工作。我们从请求的user属性中获取user.id属性,从URL中获取movie ID,当MovieImageForm的user和movie字段不可用时(忽略请求body体中的参数值),将user和movie ID当作初始参数传给form。 Django的ImageField会对文件改名和存储。

1.6 将请求关联到视图和文件上

将文件上传视图MovieImageUpload关联到URLConf中。 编辑core/urls.py

from django.conf.urls import url
from django.urls import pathfrom core import viewsapp_name = 'core'urlpatterns = [# 省略其他路径# 配置path('movie/<int:movie_id>/image/upload',views.MovieImageUpload.as_view(),name='MovieImageUpload'),
]
复制代码

像往常一样,我们添加一个path()函数,确保传入一个movie_id参数。 现在Django就知道如何找到我们新增的文件上传视图,只是它还不知道如何对外提供这个上传的文件。 在开发环境中,为了对外提供该上传的文件,更新下urls.py文件: MyMovie/urls.py

from django.conf import settings
from django.conf.urls.static import staticfrom django.contrib import admin
from django.urls import path, includeimport core.urls
import user.urlsMEDIA_FILE_PATHS = static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)urlpatterns = [path('admin/', admin.site.urls),path('user/', include(user.urls, namespace='user')),path('', include(core.urls, namespace='core')),
] + MEDIA_FILE_PATHS
复制代码

Django提供了static()函数,返回一个包含单路径对象的列表,该对象将以字符串MEDIA_URL开头的任何请求路由到document_root中的文件。 开发环境中,这给我们提供了一种上传图片文件的方式。这种方式不适合生产环境,如果settings.DEBUGFalsestatic()函数将返回一个空列表。

天星技术团QQ:557247785

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/389357.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BZOJ.2738.矩阵乘法(整体二分 二维树状数组)

题目链接 BZOJ洛谷 整体二分。把求序列第K小的树状数组改成二维树状数组就行了。 初始答案区间有点大&#xff0c;离散化一下。 因为这题是一开始给点&#xff0c;之后询问&#xff0c;so可以先处理该区间值在l~mid的修改&#xff0c;再处理询问。即二分标准可以直接用点的标号…

从数据库里读值往TEXT文本里写

/// <summary> /// 把预定内容导入到Text文档 /// </summary> private void ChangeDbToText() { this.RecNum.Visibletrue; //建立文件&#xff0c;并打开 string oneLine ""; string filename "Storage Card/YD" DateTime.Now.…

DengAI —如何应对数据科学竞赛? (EDA)

了解机器学习 (Understanding ML) This article is based on my entry into DengAI competition on the DrivenData platform. I’ve managed to score within 0.2% (14/9069 as on 02 Jun 2020). Some of the ideas presented here are strictly designed for competitions li…

Pytorch模型层简单介绍

模型层layers 深度学习模型一般由各种模型层组合而成。 torch.nn中内置了非常丰富的各种模型层。它们都属于nn.Module的子类&#xff0c;具备参数管理功能。 例如&#xff1a; nn.Linear, nn.Flatten, nn.Dropout, nn.BatchNorm2d nn.Conv2d,nn.AvgPool2d,nn.Conv1d,nn.Co…

有效沟通的技能有哪些_如何有效地展示您的数据科学或软件工程技能

有效沟通的技能有哪些What is the most important thing to do after you got your skills to be a data scientist? It has to be to show off your skills. Otherwise, there is no use of your skills. If you want to get a job or freelance or start a start-up, you ha…

java.net.SocketException: Software caused connection abort: socket write erro

场景&#xff1a;接口测试 编辑器&#xff1a;eclipse 版本&#xff1a;Version: 2018-09 (4.9.0) testng版本&#xff1a;TestNG version 6.14.0 执行testng.xml时报错信息&#xff1a; 出现此报错原因之一&#xff1a;网上有人说是testng版本与eclipse版本不一致造成的&#…

[博客..配置?]博客园美化

博客园搞定时间 -> 18年6月27日 [让我歇会儿 搞这个费脑子 代码一个都看不懂] 转载于:https://www.cnblogs.com/Steinway/p/9235437.html

使用K-Means对美因河畔法兰克福的社区进行聚类

介绍 (Introduction) This blog post summarizes the results of the Capstone Project in the IBM Data Science Specialization on Coursera. Within the project, the districts of Frankfurt am Main in Germany shall be clustered according to their venue data using t…

Pytorch损失函数losses简介

一般来说&#xff0c;监督学习的目标函数由损失函数和正则化项组成。(Objective Loss Regularization) Pytorch中的损失函数一般在训练模型时候指定。 注意Pytorch中内置的损失函数的参数和tensorflow不同&#xff0c;是y_pred在前&#xff0c;y_true在后&#xff0c;而Ten…

读取Mc1000的 唯一 ID 机器号

先引用Symbol.ResourceCoordination 然后引用命名空间 using System;using System.Security.Cryptography;using System.IO; 以下为类程序 /// <summary> /// 获取设备id /// </summary> /// <returns></returns> public static string GetDevi…

样本均值的抽样分布_抽样分布样本均值

样本均值的抽样分布One of the most important concepts discussed in the context of inferential data analysis is the idea of sampling distributions. Understanding sampling distributions helps us better comprehend and interpret results from our descriptive as …

玩转ceph性能测试---对象存储(一)

笔者最近在工作中需要测试ceph的rgw&#xff0c;于是边测试边学习。首先工具采用的intel的一个开源工具cosbench&#xff0c;这也是业界主流的对象存储测试工具。 1、cosbench的安装&#xff0c;启动下载最新的cosbench包wget https://github.com/intel-cloud/cosbench/release…

[BZOJ 4300]绝世好题

Description 题库链接 给定一个长度为 \(n\) 的数列 \(a_i\) &#xff0c;求 \(a_i\) 的子序列 \(b_i\) 的最长长度&#xff0c;满足 \(b_i\wedge b_{i-1}\neq 0\) &#xff08; \(\wedge\) 表示按位与&#xff09; \(1\leq n\leq 100000\) Solution 令 \(f_i\) 为二进制第 \(i…

因果关系和相关关系 大数据_数据科学中的相关性与因果关系

因果关系和相关关系 大数据Let’s jump into it right away.让我们马上进入。 相关性 (Correlation) Correlation means relationship and association to another variable. For example, a movement in one variable associates with the movement in another variable. For…

Pytorch构建模型的3种方法

这个地方一直是我思考的地方&#xff01;因为学的代码太多了&#xff0c;构建的模型各有不同&#xff0c;这里记录一下&#xff01; 可以使用以下3种方式构建模型&#xff1a; 1&#xff0c;继承nn.Module基类构建自定义模型。 2&#xff0c;使用nn.Sequential按层顺序构建模…

vue取数据第一个数据_我作为数据科学家的第一个月

vue取数据第一个数据A lot.很多。 I landed my first job as a Data Scientist at the beginning of August, and like any new job, there’s a lot of information to take in at once.我于8月初找到了数据科学家的第一份工作&#xff0c;并且像任何新工作一样&#xff0c;一…

Flask-SocketIO 简单使用指南

Flask-SocketIO 使 Flask 应用程序能够访问客户端和服务器之间的低延迟双向通信。客户端应用程序可以使用 Javascript&#xff0c;C &#xff0c;Java 和 Swift 中的任何 SocketIO 官方客户端库或任何兼容的客户端来建立与服务器的永久连接。 安装 直接使用 pip 来安装&#xf…

STL-开篇

基本概念 STL&#xff1a; Standard Template Library&#xff0c;标准模板库 定义&#xff1a; c引入的一个标准类库 特点&#xff1a;1&#xff09;数据结构和算法的 c实现&#xff08; 采用模板类和模板函数&#xff09;2&#xff09;数据的存储和算法的分离3&#xff09;高…

Symbol Mc1000 声音的设置以及播放

首先引用Symbol.Audio 加一命名空间using Symbol.Audio; /声音设备的设置 //Select Device from device list Symbol.Audio.Device MyDevice (Symbol.Audio.Device)Symbol.StandardForms.SelectDevice.Select( Symbol.Audio.Controller.Title, Symbol.Audio.Devic…

/bin/bash^M: 坏的解释器: 没有那个文件或目录

在win下编辑的时候&#xff0c;换行结尾是\n\r &#xff0c; 而在linux下 是\n&#xff0c;所以会多出来一个\r&#xff0c;这样会出现错误 此时执行 sed -i s/\r$// file.sh 将file.sh中的\r都替换为空白&#xff0c;问题解决转载于:https://www.cnblogs.com/zzdbullet/p/9890…