Django 亮點:處理靜態資產和媒體文件(第 4 部分)
已發表: 2022-03-10Django 網站涉及很多文件。 它不僅僅是配置、模型、視圖和模板的源代碼,還包括靜態資產:CSS 和 JavaScript、圖像、圖標。 好像這還不夠,有時用戶會出現並希望將自己的文件上傳到您的網站。 這足以讓任何開發人員難以置信。 文件無處不在!
這就是我希望我可以說的地方(沒有警告):“別擔心,Django 支持你!” 但不幸的是,在處理靜態資產和媒體文件時,需要處理很多注意事項。
今天,我們將討論為單服務器和可擴展部署存儲和提供文件,同時考慮壓縮、緩存和可用性等因素。 我們還將討論 CDN 和專用文件存儲解決方案的成本和收益。
注意:這不是關於如何將 Django 站點部署到任何特定平台的教程。 相反,與 Django Highlights 系列中的其他文章(見下文)一樣,它旨在作為前端開發人員和設計人員了解創建 Web 應用程序過程的其他部分的指南。 今天,我們將重點關注在您剛剛完成的樣式修補程序或精美圖形被推送到大師之後會發生什麼。 我們將一起開發一種直覺,了解 Django 開發人員可用的策略,以便以安全、高性能和經濟高效的方式向全球用戶提供這些文件。
該系列的先前部分:
- 第 1 部分:用戶模型和身份驗證
- 第 2 部分:模板保存行
- 第 3 部分:模型、管理和利用關係數據庫
定義
這些術語中的大多數都非常簡單,但值得花點時間為本次討論建立一個共享的詞彙表。
實時 Django 應用程序中的三種文件類型是:
- 源代碼
使用 Django 框架創建的 Python 和 HTML 文件。 這些文件是應用程序的核心。 源代碼文件通常很小,以千字節為單位。 - 靜態文件
這些文件也稱為“靜態資產”,包括由應用程序開發人員和第三方庫編寫的 CSS 和 JavaScript,以及 PDF、軟件安裝程序、圖像、音樂、視頻和圖標。 這些文件僅在客戶端使用。 靜態文件的範圍從幾千字節的 CSS 到幾千兆字節的視頻。 - 媒體文件
用戶上傳的任何文件,從個人資料圖片到個人文檔,都稱為媒體文件。 需要為用戶安全可靠地存儲和檢索這些文件。 媒體文件可以是任意大小,用戶可以將幾千字節的明文上傳到幾千兆字節的視頻中。 如果您處於此規模的後期,您可能需要比本文準備提供的更專業的建議。
兩種類型的 Django 部署是:
- 單服務器
單服務器 Django 部署正是它聽起來的樣子:一切都存在於單個服務器上。 這種策略非常簡單,與開發環境非常相似,但無法有效處理大量或不一致的流量。 單服務器方法僅適用於學習或演示項目,不適用於需要可靠正常運行時間的實際應用程序。 - 可擴展
部署 Django 項目的方法有很多種,可以擴展以滿足用戶需求。 這些策略通常涉及啟動和關閉大量服務器以及使用負載平衡器和託管數據庫等工具。 幸運的是,為了本文的目的,我們可以有效地將比單服務器部署更複雜的所有內容歸為此類。
選項 1:默認 Django
小型項目受益於簡單的架構。 Django 對靜態資源和媒體文件的默認處理就是:簡單。 對於每一個,您都有一個根文件夾,用於存儲文件並位於服務器上的源代碼旁邊。 簡單的。 這些根文件夾主要通過yourproject/settings.py配置生成和管理。
靜態資產
在 Django 中處理靜態文件時要了解的最重要的事情是python manage.py collectstatic
命令。 此命令遍歷 Django 項目中每個應用程序的靜態文件夾,並將所有靜態資產複製到根文件夾。 運行此命令是部署 Django 項目的重要部分。 考慮以下目錄結構:
- project - project - settings.py - urls.py - ... - app1 - static/ - app1 - style.css - script.js - img.jpg - templates/ - views.py - ... - app2 - static/ - app2 - style.css - image.png - templates/ - views.py - ...
還假設project/settings.py中的以下設置:
STATIC_URL = "/static/" STATIC_ROOT = "/path/on/server/to/djangoproject/static"
運行python manage.py collectstatic
命令將在服務器上創建以下文件夾:
- /path/on/server/to/djangoproject/static - app1 - style.css - script.js - img.jpg - app2 - style.css - image.png
請注意,在每個靜態文件夾中,都有另一個帶有應用程序名稱的文件夾。 這是為了防止收集靜態文件後的命名空間衝突; 正如您在上面的文件結構中看到的那樣,這使app1/style.css和app2/style.css保持不同。 從這裡開始,應用程序將在生產期間在STATIC_ROOT
的此結構中查找靜態文件。 因此,在app1/templates/的模板中引用靜態文件如下:
{% load static %} <link rel="stylesheet" type="text/css" href="{% static "app1/style.css" %}">
Django 會自動找出在開發過程中從何處獲取靜態文件來建模此行為,您無需在開發過程中運行collectstatic
。
有關更多詳細信息,請參閱 Django 文檔。
媒體文件
想像一個具有用戶數據庫的專業網站。 這些用戶中的每一個都會有一個關聯的個人資料,其中可能包含頭像圖像和簡歷文檔等。 這是該信息的簡短示例模型:
from django.db import models from django.contrib.auth.models import User def avatar_path(instance, filename): return "avatar_{}_{}".format(instance.user.id, filename) class Profile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) resume = models.FileField(upload_to="path/string") avatar = models.ImageField(upload_to=avatar_path)
為此,您需要project/settings.py中的以下選項,例如靜態資產:
MEDIA_URL = "/media/" MEDIA_ROOT = "/path/on/server/to/media"
ImageField
繼承自FileField
,因此它共享相同的參數和功能。 這兩個字段都有一個可選的upload_to
參數,它接受一個作為路徑的字符串並將其附加到MEDIA_ROOT
以存儲文件,然後可以通過MEDIA_URL
頂部的相同路徑訪問該文件。 upload_to
參數也可以採用返回字符串的函數,如avatar_path
函數所示。
確保從版本控制中省略媒體文件目錄及其內容。 當兩個開發人員在不同的機器上測試同一個應用程序時,它的內容可能會發生衝突,並且與靜態資產不同,它不是可部署的 Django 應用程序的一部分。
選項 2:帶有服務的 Django
我的指導理念是使用工具做他們最擅長的事情。 Django 是一個了不起的框架,它為用戶身份驗證、服務器端渲染、處理模型和表單、管理功能以及構建 Web 應用程序的許多其他基本方面提供了開箱即用的出色工具。 但是,在我看來,它用於處理靜態資產和媒體文件的工具並不適合在可擴展站點上進行製作。 Django 核心開發人員認識到,許多人選擇替代方法來處理生產中的這些文件; 當您這樣做時,該框架非常擅長擺脫困境。 大多數用於一般用途的 Django 站點都希望合併靜態資產並使用這些非 Django 特定的方法處理媒體文件。
CDN 上的靜態資產
雖然中小型項目沒有一個也可以逃脫,但 CDN(內容交付網絡)易於使用並提高了任何規模的應用程序的性能。 CDN 是一個服務器網絡,通常在全球範圍內分發和提供 Web 內容,主要是靜態資產。 流行的 CDN 包括 Cloudflare CDN、Amazon CloudFront 和 Fastly。 要使用 CDN,請上傳靜態文件,然後在應用程序中按如下方式引用它們:
<link rel="stylesheet" type="text/css" href="https://cdn.example.com/path/to/your/files/app1/style.css">
這個過程很容易與您的 Django 部署腳本集成。 運行python manage.py collectstatic
命令後,將生成的目錄複製到您的 CDN(一個根據您使用的服務而有很大差異的過程),然後從 Django 部署包中刪除靜態資產。
在開發中,您需要訪問與生產中不同的靜態資產副本。 這樣,您可以在本地進行更改而不會影響生產站點。 您可以使用本地資產或運行 CDN 的第二個實例來交付文件。 使用一些自定義變量(例如CDN_URL
)配置yourproject/settings.py ,並在模板中使用該值以確保您在開發和生產中使用正確版本的資產。
最後一點是,許多 CSS 和 JavaScript 庫都有大多數網站可以使用的免費 CDN。 如果您正在加載,例如,Bootstrap 4 或 underscore.js,您可以跳過在開發中使用自己的副本的麻煩,以及通過使用這些公共 CDN 在生產中提供自己的副本的費用。
具有專用文件存儲的媒體文件
任何生產 Django 站點都不應該將用戶文件存儲在運行站點的服務器上某個簡單的/media/文件夾中。 以下是為什麼不這樣做的眾多原因中的三個:
- 如果您需要通過添加多個服務器來擴展站點,則需要某種方式在這些服務器之間複製和同步上傳的文件。
- 如果服務器崩潰,源代碼會在您的版本控制系統中備份,但默認情況下不會備份媒體文件,除非您將服務器配置為這樣做,但為此您最好使用專用的文件存儲。
- 如果發生惡意活動,最好將用戶上傳的文件保存在與運行應用程序的服務器不同的服務器上,儘管這絕不會消除驗證用戶上傳文件的要求。
集成第三方來存儲用戶上傳的文件非常簡單。 您不需要更改代碼中的任何內容,除了可能刪除或修改模型中FileField
的upload_to
值以及配置一些設置。 例如,如果您計劃將文件存儲在 AWS S3 中,則需要執行以下操作,這與使用 Google Cloud、Azure、Backblaze 或類似競爭服務存儲文件的過程非常相似。
首先,您需要安裝庫boto3
和django-storages
storages 。 然後,您需要在 AWS 上設置存儲桶和 IAM 角色,這超出了本文的範圍,但您可以在此處查看說明。 配置完所有這些後,您需要在project/settings.py中添加三個變量:
DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage" AWS_STORAGE_BUCKET_NAME = "BUCKET_NAME" AWS_S3_REGION_NAME = "us-east-2"
此外,您需要設置對 AWS 存儲桶的憑證訪問。 一些教程將演示如何將 ID 和密鑰添加到您的設置文件或作為環境變量,但這些都是不安全的做法。 相反,使用django-storages
和 AWS CLI 來配置密鑰,如此處所述。 您可能還對django-storages
文檔感興趣。
您不希望開發或測試媒體文件與實際用戶上傳的內容混淆。 避免這種情況非常簡單:設置多個存儲桶,一個用於開發(或每個開發人員一個),一個用於測試,一個用於生產。 然後,您只需更改每個環境的AWS_STORAGE_BUCKET_NAME
設置,一切順利。
性能和可用性
有許多因素會影響您網站的性能和可靠性。 在考慮無論您採用哪種方法管理它們都很重要的靜態和媒體文件時,這裡有一些重要的。
成本
向用戶提供文件需要花錢有兩個原因:存儲和帶寬。 您必須向託管服務提供商付費才能為您存儲文件,但您也必須付費才能提供文件。 帶寬比存儲貴得多(例如,在撰寫本文時,AWS S3 的存儲費用為每 GB 2.3 美分,而傳輸到 Internet 的數據為每 GB 9 美分)。 像 S3 或 CDN 這樣的文件存儲的經濟性與像 Digital Ocean 水滴這樣的通用主機的經濟性不同。 通過將昂貴的文件轉移到專為他們設計的服務來利用專業化和規模經濟。 此外,許多文件存儲和 CDN 提供免費計劃,因此可能小到不使用它們就可以逃脫的站點可以這樣做並獲得收益,而無需任何額外的基礎設施成本。
壓縮和轉碼
由照片和視頻等靜態資產引起的大多數問題是因為它們是大文件。 自然,開發人員通過嘗試使這些文件更小來解決這個問題。 有許多方法可以在兩大類中混合使用壓縮和轉碼:無損和有損。 無損壓縮保留了資產的原始質量,但文件大小相對較小。 有損壓縮,或轉碼為有損格式,允許更小的文件大小,但會損失一些原始工件的質量。 這方面的一個例子是將視頻轉碼為較低的比特率。 有關詳細信息,請查看這篇關於優化視頻傳輸的文章。 通過 Web 提供大型文件時,帶寬速度通常要求您提供高度壓縮的工件,這需要有損壓縮。
除非您是 YouTube,否則壓縮和轉碼不會即時進行。 靜態資產應在部署前進行適當格式化,並且您可以對用戶上傳的文件強制執行基本文件類型和文件大小限制,以確保用戶的媒體文件有足夠的壓縮和適當的格式。
縮小
雖然 JavaScript 和 CSS 文件通常不像圖像那麼大,但它們通常可以被壓縮以壓縮成更少的字節。 這個過程稱為縮小。 縮小不會改變文件的編碼,它們仍然是文本,並且縮小的文件仍然需要是其原始語言的有效代碼。 縮小文件保留其原始擴展名。
在縮小文件中刪除的主要內容是不必要的空格,從計算機的角度來看,CSS 和 JavaScript 中幾乎所有的空格都是不必要的。 縮小方案還會縮短變量名稱並刪除註釋。
默認情況下縮小混淆代碼; 作為開發人員,您應該專門使用非縮小文件。 部署過程中的一些自動步驟應在存儲和提供文件之前縮小文件。 如果您使用的是第三方 CDN 提供的庫,請確保您使用的是該庫的縮小版本(如果可用)。 HTML 文件可以縮小,但由於 Django 使用服務器端渲染,因此動態執行此操作的處理成本很可能超過頁面大小的小幅減小。
全球可用性
就像向鄰居發送一封信比在全國范圍內發送一封信所花費的時間更少一樣,在附近傳輸數據所花費的時間也比在世界各地傳輸數據所花費的時間更少。 CDN 提高頁面性能的方法之一是將資產複製到世界各地的服務器上。 然後,當客戶端發出請求時,它們會從最近的服務器(通常稱為邊緣節點)接收靜態資產,從而減少加載時間。 將 CDN 與 Django 站點一起使用的優點之一是將靜態資產的全球分佈與代碼的全球分佈分離。
客戶端緩存
有什麼比在用戶附近的服務器上擁有靜態文件更好的呢? 已將靜態文件存儲在用戶的設備上! 緩存是存儲計算或請求結果的過程,以便可以更快地重複訪問它們。 就像 CSS 樣式表可以在 CDN 中緩存在世界各地一樣,它可以在客戶端第一次從您的站點加載頁面時緩存在客戶端的瀏覽器中。 然後,樣式表在後續請求中可以在設備本身上使用,因此客戶端發出的請求更少,頁面加載時間縮短,帶寬使用減少。
瀏覽器執行它們自己的緩存操作,但是如果您的站點擁有大量流量,您可以使用 Django 的緩存框架優化您的客戶端緩存行為。
綜上所述
同樣,我的指導理念是使用工具來做他們最擅長的事情。 單服務器項目和僅具有輕量級靜態資產的小型可擴展部署可以使用 Django 的內置靜態資產管理,但大多數應用程序應該分離出要通過 CDN 提供服務的資產。
如果您的項目旨在用於任何類型的實際使用,請不要使用 Django 的默認方法存儲媒體文件,而是使用服務。 有了足夠的流量,“足夠的流量”在互聯網規模上是一個相對較小的數字,架構、開發過程和部署的額外複雜性對於使用分別為靜態和媒體文件提供單獨的 CDN 和文件存儲解決方案。
推薦閱讀
- 第 1 部分:用戶模型和身份驗證
- 第 2 部分:模板保存行
- 第 3 部分:模型、管理和利用關係數據庫