如何在Django網站框架中上傳檔案

Django網站所使用的媒體檔案以及Js、CSS檔案為了效能上的考量都被以靜態檔案的型式呈現,也就是在網站被網頁伺服器載入之後就放在記憶體中,以便更有效率地被瀏覽器存取。因此,如果我們打算在網站中提供檔案上傳的功能,如果沒有另行設定的話,剛上傳的檔案是沒辦法被後來的網頁中使用的。

為了解決這個問題,在Django中另外提供了一個叫做media資料夾的功能,在settings.py中做一些設定,就可以讓讓資料夾中的檔案可以即時地被網站中的程式加以存取利用,所以,我們就可以把上傳之後的檔案放到那個資料夾中。關於media資料夾的設定,除了在BASE_DIR的目錄下建立一個叫做media的資料夾之外,在settings.py中也要加入以下的設定(以下的設定包括原有靜態檔案資料夾的設定):

STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATICFILES_DIRS = [
            BASE_DIR / "static",
]

MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / "media"

同時,在urls.py的路由檔案中,也要在urlpatterns的設定中加上一些變更,如下所示:

from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from mysite import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.index),
]+static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)

在程式碼的部份,首先要準備一個用來接受上傳檔案選擇的表單,也就是在html檔案中加上如下所示的上傳表單HTML程式碼,並在這個表單下方加上可以顯示出目前在media資料夾內所有檔案名稱的程式碼(index.html):

<form action='/' method="POST" enctype="multipart/form-data">
    {% csrf_token %}
    <input type=file name="file" required>
    <input type=submit value="開始上傳">
</form>
<hr>
以下是目前伺服器上的檔案:<br>
{% for filename in mediafiles %}
{{ filename }}<br>
{% endfor %}


在上面這段程式碼中,我們建立的表單會以file這個輸入元件輸入要上傳的檔案,並在使用者選擇了上傳之後,以POST協定呼叫根目錄,讓urls.py把執行權限導到views.py中的index程序。上述程式碼的網頁呈現如下所示(此範例已上傳了2個檔案):

那麼,當網站收到上傳檔案的請求時應該要做些什麼事呢?請參考以下的程式碼(views.py):

from django.shortcuts import render, redirect
from django.core.files.storage import FileSystemStorage
from django.conf import settings
import os

def index(request):
    if request.method=="POST":
        uploaded_file = request.FILES['file']
        fss = FileSystemStorage()
        file = fss.save(uploaded_file.name, uploaded_file)
        return redirect("/")
    mediafiles = os.listdir(settings.MEDIA_ROOT)        
    return render(request, "index.html", locals())

在這個程式中我們使用FileSystemStorage這個模組來負責上傳檔案的儲存工作。在把上傳的檔案透過request.FILES[‘file’]取得之後,先建立一個FileSystemStorage()的物件fss,然後執行fss.save()函式即可,它會幫我們把檔案儲存在media的資料夾中,並傳回該檔案物件放在file變數中,不過在這個例子中我們並沒有使用到回傳的file物件。儲存完畢即可轉址到根目錄中。

如果index不是以POST的協定執行的話,則利用os.listdir(settings.MEDIA_ROOT)取得在media資料夾中的所有檔案,然後即可在index.html中列出所有的檔案列表,就如同前述的網頁畫面所呈現的。當然,如果你想要讓列出來的檔案列表是可以下載的,只要把連結加上去就可以了,請參考修改後的index.html(修改的部份):

以下是目前伺服器上的檔案:<br>
{% for filename in mediafiles %}
<a href='/media/{{ filename }}/'>{{filename}}</a><br>
{% endfor %}

此時輸出的畫面如下所示:

使用滑鼠點選連結,就可以直接下載該檔案了。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *