1. 程式人生 > >通過Django的form及Ajax等多種方式上傳檔案

通過Django的form及Ajax等多種方式上傳檔案

如下效果:

方式一、通過Django的Form方式上傳檔案

思路:前臺html建立Form,根據格式要求完善form內標籤屬性;提交後臺後,後臺獲取前臺傳來的資料進行處理:檔案路徑儲存至資料庫,檔案內容儲存至本地資料夾中;後臺處理完成後重新整理前臺頁面。

1、建立空白Django專案,過程忽略。

2、靜態html頁面設定:

<form action="/upload.html" method="post" enctype="multipart/form-data">
    <input type="text" name="fileName">
    <input type="file" name="fileContent">
    <input type="submit" value="提交">
</form>

寫一個form表單,三個關鍵標籤:text、file、submit,分別存新的檔名、檔案內容、提交按鈕,由於是檔案上傳因此form中enctype需要設定成multipart/form-data。

3、views後臺設定:

from django.shortcuts import render,redirect
import os

def Upload(request):
   if request.method=="GET":
      
        return render(request,"upload.html")
    elif request.method=="POST":
        # 獲取普通input標籤值,即檔名
        filname=request.POST.get('fileName')
        # 獲取file型別的input標籤值,即檔案內容
        file=request.FILES.get('fileContent')

        # 獲取檔案字尾名
        postfix=file.name.split('.')[1]
        # 設定本地檔案路徑
        file_path=os.path.join('static',filname+'.'+postfix)


        # 將上傳的檔案寫入本地目錄
        f=open(file_path,"wb")
        for chunk in file.chunks():
            f.write(chunk)
        f.close()


        return redirect("upload.html")

url檔案配置:

from Upload import views

urlpatterns = [
   
    path('upload.html',views.Upload),
]

原理:

(1)、當Get請求時,即瀏覽器開啟該網頁時,顯示上傳頁面;

(2)、當POST請求時,即點選“提交”按鈕時:

①、獲取介面傳過來的新的檔名及檔案內容。

②、分塊讀取檔案內容,並寫入到本地目錄。

4、頁面上動態顯示剛剛上傳的圖片,需做如下改造:

(1)、新建資料庫表,用來儲存圖片路徑:

# 圖片類
class image(models.Model):
    # 路徑
    file_Path=models.CharField(max_length=32)

(2)、在上傳成功時將檔案路徑儲存至資料庫,即在views的檢視方法的POST中的儲存檔案後面新增程式碼:

   # 寫入成功後將路徑儲存至資料庫
        models.image.objects.create(file_Path=file_path)

(3)、頁面重新整理時展現圖片,即在檢視方法的GET中讀取資料庫中新增的圖片路徑,並將其返給html頁面:

 if request.method=="GET":
        # 獲取所有圖片
        imgs=models.image.objects.all()
        return render(request,"upload.html",{"imgs":imgs})

(4)、html頁面中增加

<div class="imgs">
    {% for obj in imgs %}
         <img src="{{ obj.file_Path }}">
    {% endfor %}
</div>

(5)、由於Django對靜態檔案瀏覽的限制,需要在配置中新增:

STATIC_URL = '/static/'

STATICFILES_DIRS = [
    os.path.join(BASE_DIR,"static"),
]

方式二、通過原生js實現

思路:html頁面,通過FormData承載圖片內容,並通過原生的XMLHttpRequest將頁面全部資訊傳輸到後臺views中;後臺views接收後處理並將結果通過json形式返回給前臺html頁面;前臺html頁面接收到字串內容後轉換為json物件;建立圖片標籤,並將接收到的圖片地址賦值。

1、url配置如下:

from Upload import views

urlpatterns = [
   
    path('uploadjs.html', views.Upload_js),

]

2、views檢視函式:

def Upload_js(request):
    if request.method == "GET":
        # 獲取所有圖片
        imgs = models.image.objects.all()
        return render(request, "upload_js.html", {"imgs": imgs})
    elif request.method == "POST":
        # 獲取普通input標籤值,即檔名
        filname = request.POST.get('fileName')
        # 獲取file型別的input標籤值,即檔案內容
        file = request.FILES.get('fileContent')

        # 獲取檔案字尾名
        postfix = file.name.split('.')[1]
        # 設定本地檔案路徑
        file_path = os.path.join('static', filname + '.' + postfix)

        # 將上傳的檔案寫入本地目錄
        f = open(file_path, "wb")
        for chunk in file.chunks():
            f.write(chunk)
        f.close()

        # 寫入成功後將路徑儲存至資料庫
        models.image.objects.create(file_Path=file_path)

        #將狀態及檔案路徑通過json形式返回至html頁面
        ret={'status':True,'path':file_path}
        return HttpResponse(json.dumps(ret))

3、前臺html頁面

<body>

    <input type="text" name="fileName" id="fileName">
    <input type="file" name="fileContent" id="fileContent">
    <input type="button" value="提交" onclick="upload();">

<div class="imgs">
    {% for obj in imgs %}
         <img src="{{ obj.file_Path }}">
    {% endfor %}
</div>

<script>
    function upload() {
        //建立FormData用於儲存檔案內容
        var dict=new FormData();
        dict.append("fileName",document.getElementById('fileName').value);
        dict.append("fileContent",document.getElementById('fileContent').files[0]);


        var xml=new XMLHttpRequest();
        //uploadjs.html即url.py中配置的後臺views
        xml.open("post",'/uploadjs.html',true);
        xml.onreadystatechange=function(){
            if (xml.readyState==4 && xml.status==200) {
                //傳輸成功時將檢視返回回來的內容通過json接收
                var obj=JSON.parse(xml.responseText);
                if(obj.status){
                    //建立圖片標籤,並將後臺傳回來的圖片地址加上,顯示在頁面中
                    var img=document.createElement('img');
                    img.src='/'+obj.path;
                    document.getElementsByClassName('imgs')[0].appendChild(img);
                }
            }
        };

        xml.send(dict)
    }
</script>
</body>

方式三、通過JQuery實現

原理:將方式二中原生js部分改為JQuery形式,其他無變化。

html頁面程式碼如下:

<body>

    <input type="text" name="fileName" id="fileName">
    <input type="file" name="fileContent" id="fileContent">
    <input type="button" value="提交" onclick="upload();">

<div class="imgs">
    {% for obj in imgs %}
         <img src="{{ obj.file_Path }}">
    {% endfor %}
</div>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
    function upload() {
        //建立FormData用於儲存檔案內容
        var dict=new FormData();
        dict.append("fileName",document.getElementById('fileName').value);
        dict.append("fileContent",document.getElementById('fileContent').files[0]);


      $.ajax({
          url:'/uploadjq.html',
          type:'post',
          data:dict,
          processData: false,// 告訴jQuery不要去處理髮送的資料(必須設定)
      contentType: false, // 告訴jQuery不要去設定Content-Type請求頭(必須設定)
          dataType:'JSON',
          success:function (arg) {
            if(arg.status){
               //建立圖片標籤,並將後臺傳回來的圖片地址加上,顯示在頁面中
                    var img=document.createElement('img');
                    img.src='/'+arg.path;
                    $('.imgs')[0].appendChild(img);
            }
          }
      })
    }
</script>
</body>

注意:由於使用的是FormData物件,需要將processData、contentType設定成false,至於為什麼要這麼設定,詳見:

方式四、通過iframe及form表單,views後臺程式碼與方式二一致

原理:將views後臺返回的資料傳給iframe頁面標籤,觸發iframe的onload事件時實現img圖片動態載入。

1、原生js時,獲取iframe物件內容通過document.getElementById('iframe的ID').contentWindow.document.getElementById('元素的ID');contentWindow代表frameiframe內部的視窗物件。html前臺頁面如下:

<body>
<form action="/uploadiframe.html" method="post" enctype="multipart/form-data" target="img_igrame">
    <iframe id="img_igrame" name="img_igrame" onload="loadiframe(this)"  ></iframe>
    <input type="text" name="fileName">
    <input type="file" name="fileContent">
    <input type="submit" value="提交">
</form>
<div class="imgs">
    {% for obj in imgs %}
         <img src="{{ obj.file_Path }}">
    {% endfor %}
</div>

<script>

    function loadiframe(self) {

            // 獲取iframe內部的內容
           var str_json=self.contentWindow.document.getElementsByTagName('body')[0].innerText;
            var obj = JSON.parse(str_json);
            if (obj.status){
                var img = document.createElement('img');
                img.src = "/" + obj.path;
                document.getElementsByClassName('imgs')[0].appendChild(img);
               
            }

    }
</script>

</body>

2、jQuery時,通過.contents() 方法;.contents() 方法允許我們檢索 DOM 樹中的這些元素的直接子節點,並用匹配元素構造新的 jQuery 物件。.contents() 和 .children() 方法類似,不同的是前者在結果 jQuery 物件中包含了文字節點以及 HTML 元素。.contents() 方法也可以用於獲得 iframe 的內容文件,前提是該 iframe 與主頁面在同一個域。程式碼如下:

<body>
<form action="/uploadiframe.html" method="post" enctype="multipart/form-data" target="img_igrame">
    <iframe id="img_igrame" name="img_igrame" onload="loadiframe(this)"  ></iframe>
    <input type="text" name="fileName">
    <input type="file" name="fileContent">
    <input type="submit" value="提交">
</form>
<div class="imgs">
    {% for obj in imgs %}
         <img src="{{ obj.file_Path }}">
    {% endfor %}
</div>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>

    function loadiframe(self) {

            // 獲取iframe內部的內容
            var str_json = $('#img_igrame').contents().find('body').text();

    
            var obj = JSON.parse(str_json);
            if (obj.status){
                var img = document.createElement('img');
                img.src = "/" + obj.path;
                $('#imgs').append(img);
             
            }

    }
</script>

</body>

=======

總結:

通過Django的Form方式上傳檔案,簡單,但無法區域性重新整理實現ajax效果;

通過js或jQuery方式,需要使用FormData物件,仍有些瀏覽器不相容;

通過Form+iframe方式上傳檔案,相容性最好。