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.pswp.cn/news/389357.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/389357.shtml
英文地址,請注明出處:http://en.pswp.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…