通过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,至于为什么要这么设置,详见:

[#1] 浅谈contentType = false 
[#2] 原生ajax请求 怎么设置processData这个参数? 

 


方式四、通过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>

 参考:JS获取/设置iframe内对象元素、文档的几种方法

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>

参考:Js/Jquery获取iframe中的元素

=======

总结:

通过Django的Form方式上传文件,简单,但无法局部刷新实现ajax效果;

通过js或jQuery方式,需要使用FormData对象,仍有些浏览器不兼容;

通过Form+iframe方式上传文件,兼容性最好。

 

完整源代码请见:https://download.****.net/download/baobao267/10718732