• 上传文件和上传头像


    一、上传文件

    上传一个图片

    使用input type="file",来上传一个文件。注意:form表单必须添加属性enctype="multipart/form-data"

    在views.py视图函数中,获取文件对象,必须使用request.FILES.get

    新建项目upload,在项目中新建static文件夹,在文件夹里面创建upload目录,用来保存上传的文件。

    修改settings.py,定义static路径,在最后面加上以下代码。记住下面的等号后面是一个元组,所以一定要加一个英文的逗号‘,’,等号后面也可以使用列表,这个就不需要加逗号了

    STATICFILES_DIRS = (
        os.path.join(BASE_DIR, 'static'),
    )

    在upload/urls.py,增加路由

    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^uploading_img/', views.uploading_img, name='uploading_img'),
    ]

    在app01/views.py中的代码

    from django.shortcuts import render, HttpResponse, redirect
    from uploading import settings
    import os
    
    
    # Create your views here.
    def uploading_img(request):
        if request.method == 'POST':
            user = request.POST.get('user')
            avatar = request.FILES.get('avatar')
            print(user)
            print('avatar', avatar, type(avatar))
            print(avatar.__dict__)  # 查看对象的所有属性
            # 文件存储的绝对路径
            path = os.path.join(settings.BASE_DIR, 'static', 'upload', avatar.name)
            #  必须以二进制的方式打开,存储图片
            with open(path, 'wb') as f:
                for line in avatar.chunks():
                    f.write(line)
    
            return HttpResponse('上传成功')
        return render(request, 'upload_img.html')

    在templates下创建一个upload_img.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    {#上传文件必须指定enctype="multipart/form-data"#}
    <form method="post" enctype="multipart/form-data">
        {% csrf_token %}
        <lable>上传图片</lable>
        <input type="file" name="avatar"><br>
        <lable>用户名</lable>
        <input type="text" name="user">
        <input type="submit" value="提交">
    
    </form>
    </body>
    </html>
    View Code

    上面用的是原始的上传按钮要是不好看可以使用下面的

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    </head>
    <body>
    {#上传文件必须指定enctype="multipart/form-data"#}
    <form method="post" enctype="multipart/form-data">
        {% csrf_token %}
        <label for="excelFile"><span class="btn btn-success">上传</span></label>
        <input name="avatar" type="file" id="excelFile" style="display: none">
    
        <div>
            <lable>用户名:</lable>
            <input type="text" name="user">
            <input type="submit" value="提交">
        </div>
    </form>
    </body>
    </html>
    View Code

    这样就完成了一个简单的上传图片。结果我就不演示了

    上传按钮的美化

    网上所有的好看的上传按钮都是经过修饰的。

    下面就是一个修饰后的按钮,简单的修饰。可以自定义大小

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    {#上传文件必须指定enctype="multipart/form-data"#}
    <form method="post" enctype="multipart/form-data">
        {% csrf_token %}
        <div style="position: relative;display: inline-block;height: 50px;min- 300px;overflow: hidden;">
            <div style="position: absolute;top: 0;left: 0;right: 0;bottom: 0;z-index: 1000;border: 1px dotted #9d9d9d;color: #9d9d9d;line-height: 50px;padding-left: 15px;">
                <i class="fa fa-cloud-upload" aria-hidden="true"></i>
                <span>点击上传文件</span>
            </div>
            {# opacity表示设置透明度,0表示完全透明 #}
            <input name="customer_excel" type="file" id="excelFile"
                   style="position: absolute;top: 0;left: 0;right: 0;bottom: 0;background-color: #333333;z-index: 1001;opacity: 0;filter:alpha(opacity=0);">
        </div>
        <div>
            <lable>用户名:</lable>
            <input type="text" name="user">
            <input type="submit" value="提交">
        </div>
    </form>
    <script src="/static/js/jquery.min.js"></script>
    <script>
        $(function () {
            {#当元素的值发生改变时,会发生 change 事件#}
            $('#excelFile').change(function (e) {
                /* event.currentTarget 属性是在事件冒泡阶段内的当前 DOM 元素,通常等于 this
                 :file 选择器选取带有 type=file 的 input 元素
                 0表示获取第一个元素,name表示获取文件名 */
                var fileName = e.currentTarget.files[0].name;
                /*prev() 获得匹配元素集合中每个元素紧邻的前一个同胞元素,通过选择器进行筛选是可选的
                 $(this)表示上传控件,那么它前一个同胞元素为div style="position:...
                 find() 方法获得当前元素集合中每个元素的后代,通过选择器、jQuery 对象或元素来筛选。
                 text() 方法方法设置或返回被选元素的文本内容 */
                $(this).prev().find('span').text(fileName);
            })
        })
    </script>
    </body>
    </html>
    View Code

    二、上传头像

     一般做上传头像功能,会有一个预览效果。总共有4种方法:

    1. createObjectURL
    2. FileReader
    3. FormData
    4. iframe

    前2种,是在浏览器端可以做图片预览,没有上传图片到服务器!

    后2种,图片需要上传到服务器。

    兼容性效果对比: iframe > FormData > FileReader -> createObjectURL

    iframe的兼容性是最好的,即使ie6也支持。FormData对浏览器,有一定要求

    参考兼容性:

    createObjectURL

    实例:

    urls.py

    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^upload_img/', views.upload_img, name='upload_img'),
        url(r'^upload_avatar/', views.upload_avatar, name='upload_avatar'),
    ]
    View Code

    views.py的代码,在static里面传建一个upload_avatar的目录用来存储上传的头像

    from django.shortcuts import render, HttpResponse, redirect
    from uploading import settings
    import os
    
    
    # Create your views here.
    def upload_img(request):
        if request.method == 'POST':
            user = request.POST.get('user')
            avatar = request.FILES.get('avatar')
            print(user)
            print('avatar', avatar, type(avatar))
            print(avatar.__dict__)  # 查看对象的所有属性
            # 文件存储的绝对路径
            path = os.path.join(settings.BASE_DIR, 'static', 'upload', avatar.name)
            #  必须以二进制的方式打开,存储图片
            with open(path, 'wb') as f:
                for line in avatar.chunks():
                    f.write(line)
    
            return HttpResponse('上传成功')
        return render(request, 'upload_img.html')
    
    
    def upload_avatar(request):
        if request.method == "GET":
            return render(request, 'upload_avatar.html')
    
        user = request.POST.get('user')
        avatar = request.FILES.get('avatar')
        # 文件存储的绝对路径
        path = os.path.join(settings.BASE_DIR, "static", "upload_avatar", avatar.name)
        with open(path, 'wb') as f:
            for line in avatar.chunks():
                f.write(line)
        return HttpResponse('上传成功')
    View Code

    创建一个upload_avatar.html 注意:不是所有的浏览器都支持createObjectURL,在这里要注意我们放默认图标的位置要在static里面要存储。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form method="post" enctype="multipart/form-data">
        {% csrf_token %}
        <div style="height: 100px; 100px;padding: 2px;border: 1px solid #dddddd;position: relative;">
            <img style="height: 100%; 100%;border: 0;overflow: hidden;border-radius: 50%;" id="previewImg"
                 src="/static/images/default.png">
            <input style="top: 0;left: 0;right: 0;bottom: 0;opacity: 0;position: absolute;z-index: 102;" id="avatarImg"
                   name="avatar" type="file" class="img-file"/>
        </div>
        <div>点击图片更换(<a href="#">撤销</a>)</div>
    
        <lable>用户名:</lable>
        <input type="text" name="user">
        <input type="submit" value="提交">
    </form>
    <script src="/static/js/jquery.min.js"></script>
    <script>
        {#浏览器加载资源完成后#}
        $(function () {
            {#执行函数#}
            bindChangeAvatar1();
        });
    
        function bindChangeAvatar1() {
            {#绑定change事件#}
            $('#avatarImg').change(function () {
                {#$(this)表示input上传控件,0表示第一个元素#}
                {#files[0] 获取file input中的File对象#}
                var file_obj = $(this)[0].files[0];
                console.log(file_obj);
                {#通过createObjectURL读取对象,生成url#}
                var blob = window.URL.createObjectURL(file_obj);
                {#修改src的值#}
                document.getElementById('previewImg').src = blob;
                {#load() 当资源加载完成之后才会执行#}
                $('#previewImg').load(function () {
                    {#revokeObjectURL释放对象#}
                    window.URL.revokeObjectURL(blob);
                })
            })
        }
    </script>
    </body>
    </html>
    View Code

    FileReader

    实例:

    修改upload_file.html里面的js代码即可,readAsDataURL也有兼容问题

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form method="post" enctype="multipart/form-data">
        {% csrf_token %}
        <div style="height: 100px; 100px;padding: 2px;border: 1px solid #dddddd;position: relative;">
            <img style="height: 100%; 100%;border: 0;overflow: hidden;border-radius: 50%;" id="previewImg"
                 src="/static/images/default.png">
            <input style="top: 0;left: 0;right: 0;bottom: 0;opacity: 0;position: absolute;z-index: 102;" id="avatarImg"
                   name="avatar" type="file" class="img-file"/>
        </div>
        <div>点击图片更换(<a href="#">撤销</a>)</div>
    
        <lable>用户名:</lable>
        <input type="text" name="user">
        <input type="submit" value="提交">
    </form>
    <script src="/static/js/jquery.min.js"></script>
    <script>
        $(function () {
            bindChangeAvatar2();
        });
    
        function bindChangeAvatar2() {
            $('#avatarImg').change(function () {
                var file_obj = $(this)[0].files[0];
                //使用fileReader对文件对象进行操作
                var reader = new FileReader();
                //readAsDataURL 将文件读取为 DataURL
                reader.readAsDataURL(file_obj);
                //onload 事件会在页面或图像加载完成后立即发生
                reader.onload = function (e) {
                    // 修改src属性
                    $('#previewImg')[0].src = this.result;
                };
            })
        }
    </script>
    </body>
    </html>
    View Code

    FormData

    上面的保存图片方式有问题,因为用户上传的图片,可能会重名。为了解决这个问题,需要使用uuid模块。

    uuid

    UUID是128位的全局唯一标识符,通常由32字节的字符串表示。
    它可以保证时间和空间的唯一性,也称为GUID,全称为:
    UUID —— Universally Unique IDentifier。Python 中叫 UUID

    它通过MAC地址、时间戳、命名空间、随机数、伪随机数来保证生成ID的唯一性。

    UUID主要有五个算法,也就是五种方法来实现:

    1、uuid1()——基于时间戳
    
           由MAC地址、当前时间戳、随机数生成。可以保证全球范围内的唯一性,
           但MAC的使用同时带来安全性问题,局域网中可以使用IP来代替MAC。
    
    2、uuid2()——基于分布式计算环境DCE(Python中没有这个函数)
    
            算法与uuid1相同,不同的是把时间戳的前4位置换为POSIX的UID。
            实际中很少用到该方法。
    
    3、uuid3()——基于名字的MD5散列值
    
            通过计算名字和命名空间的MD5散列值得到,保证了同一命名空间中不同名字的唯一性,
            和不同命名空间的唯一性,但同一命名空间的同一名字生成相同的uuid。    
    
    4、uuid4()——基于随机数
    
            由伪随机数得到,有一定的重复概率,该概率可以计算出来。
    
    5、uuid5()——基于名字的SHA-1散列值
    
            算法与uuid3相同,不同的是使用 Secure Hash Algorithm 1 算法

    使用方面:
    首先,Python中没有基于DCE的,所以uuid2可以忽略;
    其次,uuid4存在概率性重复,由无映射性,最好不用;
    再次,若在Global的分布式计算环境下,最好用uuid1;
    最后,若有名字的唯一性要求,最好用uuid3或uuid5。

    如何使用uuid
    import uuid
    
    name = "test_name"
    print(uuid.uuid1())  # 带参的方法参见Python Doc
    print(uuid.uuid3(uuid.NAMESPACE_DNS, name))
    print(uuid.uuid4())
    print(uuid.uuid5(uuid.NAMESPACE_DNS, name))

    修改upload_file视图函数,使用uuid,完整代码如下:

    from django.shortcuts import render, HttpResponse, redirect
    from uploading import settings
    import os
    import uuid
    import json
    
    
    # Create your views here.
    def upload_img(request):
        if request.method == 'POST':
            user = request.POST.get('user')
            avatar = request.FILES.get('avatar')
            print(user)
            print('avatar', avatar, type(avatar))
            print(avatar.__dict__)  # 查看对象的所有属性
            # 文件存储的绝对路径
            path = os.path.join(settings.BASE_DIR, 'static', 'upload', avatar.name)
            #  必须以二进制的方式打开,存储图片
            with open(path, 'wb') as f:
                for line in avatar.chunks():
                    f.write(line)
    
            return HttpResponse('上传成功')
        return render(request, 'upload_img.html')
    
    
    def upload_avatar(request):
        if request.method == "GET":
            return render(request, 'upload_avatar.html')
    
        user = request.POST.get('user')
        avatar = request.FILES.get('avatar')
        # 文件存储的绝对路径
        path = os.path.join(settings.BASE_DIR, "static", "upload_avatar", avatar.name)
        with open(path, 'wb') as f:
            for line in avatar.chunks():
                f.write(line)
        return HttpResponse('上传成功')
    
    
    def form_data_upload(request):
        """
        ajax上传文件
        :param request:
        :return:
        """
        img_upload = request.FILES.get('img_upload')  # 获取文件对象
        # 生成随机文件名
        file_name = str(uuid.uuid4()) + "." + img_upload.name.rsplit('.', maxsplit=1)[1]
        # 文件保存到static下的images目录
        img_file_path = os.path.join('static', 'images', file_name)
        with open(img_file_path, 'wb') as f:  # 写入问题
            for line in img_upload.chunks():
                f.write(line)
        # 因为ajax发送的图片路径的static前面没有带/,所以这里要拼接一下
        return HttpResponse(os.path.join("/", img_file_path))
    View Code

    当我们访问http://127.0.0.1:8000/upload_avatar/,点击更换图片的时候换了一个图片就会把换了的图片传到后台并保存。

    查看/static/images下多了一个fc2c2de5-41b7-4d30-ba07-ebeaee1d53af.jpg的文件

    注意:form_data_upload和upload_file虽然都处理了POST数据。但是它们接收的数据是不一样的!

    用户点击上传时,走form_data_upload视图函数,将图片上传到服务器

    用户点击提交时,将服务器的图片url地址和用户名提交给upload_file视图函数。

    为什么呢?因为用户点击上传时,已经将图片上传到服务器了。所以点击提交按钮时,需要再将图片上传一次。

    只需要将图片的url地址传给服务器即可!

    iframe

    所有浏览器都支持 <iframe> 标签,它是兼容性最好的一种方式

    iframe 元素会创建包含另外一个文档的内联框架(即行内框架)

    举例:内嵌汽车之家

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <iframe style=" 960px;height: 800px;" src="https://www.autohome.com.cn/beijing/" frameborder="0"></iframe>
    </body>
    </html>
    View Code

    用浏览器打开就会直接打开汽车之家

    举例2:输入什么地址,就跳转什么地址

    修改iframe.html,增加一个输入框,加入一段js代码。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <input type="text" id="addr">
    <input type="submit" onclick="changeUrl()" value="访问">
    <iframe id="ifr" style=" 960px;height: 800px;" src="https://www.autohome.com.cn/beijing/" frameborder="0"></iframe>
    <script>
        function changeUrl() {
            //获取输入框的值
            var addr = document.getElementById("addr").value;
            //修改iframe的src的值
            document.getElementById("ifr").src = addr;
        }
    </script>
    </body>
    </html>

    会在汽车之家上面出现一个访问框只要输入想要的网址就会跳到对应的网站

    注意:整体页面并没有刷新,只是iframe里面刷新了!

    它有2个应用场景:

    1. iframe标签
    可以修改src,且页面不刷新
    2. form表单
    提交表单数据,但刷新数据

    使用在上传头像:

    urls.py,增加2条路由

    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^upload_img/$', views.upload_img, name='upload_img'),
        url(r'^upload_avatar/$', views.upload_avatar, name='upload_avatar'),
        url(r'^form_data_upload/$', views.form_data_upload, name='form_data_upload'),
        url(r'^iframe_upload_img/$', views.iframe_upload_img, name='iframe_upload_img'),
        url(r'^upload_iframe/$', views.upload_iframe, name='upload_iframe'),
    ]
    View Code

    修改views.py,增加视图函数,在最后增加2个视图

    from django.shortcuts import render, HttpResponse, redirect
    from uploading import settings
    import os
    import uuid
    import json
    
    
    # Create your views here.
    def upload_img(request):
        if request.method == 'POST':
            user = request.POST.get('user')
            avatar = request.FILES.get('avatar')
            print(user)
            print('avatar', avatar, type(avatar))
            print(avatar.__dict__)  # 查看对象的所有属性
            # 文件存储的绝对路径
            path = os.path.join(settings.BASE_DIR, 'static', 'upload', avatar.name)
            #  必须以二进制的方式打开,存储图片
            with open(path, 'wb') as f:
                for line in avatar.chunks():
                    f.write(line)
    
            return HttpResponse('上传成功')
        return render(request, 'upload_img.html')
    
    
    def upload_avatar(request):
        if request.method == "GET":
            return render(request, 'upload_avatar.html')
    
        user = request.POST.get('user')
        avatar = request.FILES.get('avatar')
        # 文件存储的绝对路径
        path = os.path.join(settings.BASE_DIR, "static", "upload_avatar", avatar.name)
        with open(path, 'wb') as f:
            for line in avatar.chunks():
                f.write(line)
        return HttpResponse('上传成功')
    
    
    def form_data_upload(request):
        """
        ajax上传文件
        :param request:
        :return:
        """
        img_upload = request.FILES.get('img_upload')  # 获取文件对象
        # 生成随机文件名
        file_name = str(uuid.uuid4()) + "." + img_upload.name.rsplit('.', maxsplit=1)[1]
        # 文件保存到static下的images目录
        img_file_path = os.path.join('static', 'images', file_name)
        with open(img_file_path, 'wb') as f:  # 写入问题
            for line in img_upload.chunks():
                f.write(line)
        # 因为ajax发送的图片路径的static前面没有带/,所以这里要拼接一下
        return HttpResponse(os.path.join("/", img_file_path))
    
    
    def iframe_upload_img(request):
        if request.method == "GET":
            return render(request, 'iframe_upload_img.html')
    
        USER_LIST = []  # 空列表
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        avatar = request.POST.get('avatar')
        # 最加到列表中
        USER_LIST.append(
            {
                'user': user,
                'pwd': pwd,
                'avatar': avatar
            }
        )
        return HttpResponse("上传成功")
    
    
    def upload_iframe(request):  # iframe post提交
        ret = {'status': True, 'data': None}
        try:
            avatar = request.FILES.get('avatar')
            file_name = str(uuid.uuid4()) + "." + avatar.name.rsplit('.', maxsplit=1)[1]
            img_file_path = os.path.join('static', 'upload', file_name)
            with open(img_file_path, 'wb') as f:
                for line in avatar.chunks():
                    f.write(line)
            ret['data'] = os.path.join("/", img_file_path)
    
        except Exception as e:
            ret['status'] = False
            ret['error'] = '上传失败'
    
        return HttpResponse(json.dumps(ret))
    View Code

    增加iframe_upload_img.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
         <div style="height: 100px; 100px;padding: 2px;border: 1px solid #dddddd;position: relative;">
                <iframe style="display: none;" id="ifr" name="fffff"></iframe>
                <form method="POST" action="/upload_iframe/" enctype="multipart/form-data" target="fffff">
                    {% csrf_token %}
                    <img style="height: 100px; 100px;border: 0;overflow: hidden;border-radius: 50%;" id="prevImg"
                         src="/static/images/default.png">
                    <input style="top: 0;left: 0;right: 0;bottom: 0;opacity: 0;position: absolute;z-index: 102;"
                           id="avatar"
                           name="avatar" type="file" class="img-file"/>
                </form>
            </div>
    
         <form method="post" action="/iframe_upload_img/">
             {% csrf_token %}
             <input type="text" name="avatar" id="formAvatar" style="display: none">
             <input type="text" name="user" placeholder="请输入用户名">
             <input type="submit" value="提交">
         </form>
    
        <script src="/static/js/jquery.min.js"></script>
        <script>
            $(function () {
                bindChangeAvatar4();
            });
    
             function bindChangeAvatar4() {
                $('#avatar').change(function () {
                    //parent该变量指的是包含当前分割窗口的父窗口
                    $(this).parent().submit();
                    //onload 事件会在页面或图像加载完成后立即发生
                    $('#ifr')[0].onload = function (){
                        //获取post返回值,比如{"status": true, "data": "/static\upload\bc72823e-b274-4a76-8ec2-af844a738959.jpg"}
                        var iframeContents = $('#ifr')[0].contentWindow.document.body.innerText;
                        console.log(iframeContents);
                        //反向序列化数据
                        iframeContents = JSON.parse(iframeContents);
                        if (iframeContents.status) {
                            //修改图片的src属性
                            $('#prevImg').attr('src', iframeContents.data);
                            //修改隐藏输入框的值
                            $('#formAvatar').val(iframeContents.data);
                        }
                    }
    
                })
            }
    
        </script>
    </body>
    </html>
    View Code

    访问url:  http://127.0.0.1:8000/iframe_upload_img/

    当我们换一个头像的时候,点击提交我们会在upload下面看见新添加的图片。

    总结:

    1. 在浏览器端可以做图片预览
        - createObjectURL
        - FileReader
        
    2. 使用Ajax上传文件:
        - FormData对象
    
    3. 伪ajax上传文件:
        - iframe 
        - form 
        
    4. 图片预览 
        本质就是修改img的src数据
        
    5. 使用场景:
        a. 文件上传 
            - FormData对象
            - iframe 
            
        b. 图片预览
            - createObjectURL、FileReader
            - iframe

    文件上传场景:

    假如是政府,或者传统企业,使用最后一种

    假如是互联网企业,使用第3种!

  • 相关阅读:
    剑指21.栈的压入、弹出序列
    剑指20.包含min函数的栈
    剑指19.顺时针打印矩阵
    Java--使用反编译工具,打开jar包,查看源码
    没想到 Unicode 字符还能这样玩?
    angularjs中响应回车事件
    两个很好的angular调试工具-——batarang(stable)和ng-inspector
    基于 ThinkPHP5 的 cltphp 被搜索劫持,篡改首页的解决过程记录
    国内外CDN服务商CNAME特征串调研
    开源中国/码云 README.md上传图片的爬坑记录
  • 原文地址:https://www.cnblogs.com/yang-China/p/9771764.html
Copyright © 2020-2023  润新知