美文网首页python web
第22天,文件上传

第22天,文件上传

作者: CaiGuangyin | 来源:发表于2017-12-13 18:39 被阅读27次

    目录

    1. 利用form表单上传文件
    2. ajax上传文件
    3. 上传头像时预览头像
    4. 头像存储至数据库
        头像存储的路径
        访问上传的图片
        models.py中avatar字段方法
    

    urls.py加入url(r'^fileput/', views.fileput),

    1. 利用form表单上传文件

    模版文件fileput.html:

    {#                                        传文件必须用form-data这种类型#}
    <form action="/fileput/" method="post" enctype="multipart/form-data">
        {% csrf_token %}
        <p><input type="text" name="username"></p>
        <p><input type="file" name="myfile"></p>
        <input type="submit">
    </form>
    

    注意:form标签默认enctype="application/x-www-form-urlencoded",而上传文件时,必须设定enctype="multipart/form-data",否则服务端取不到文件对象。input标签的type='file'

    视图文件views.py

    def fileput(req):
            if request.method == 'POST':
            print(request.POST)  #<QueryDict: {'username': ['caigy'], 'csrfmiddlewaretoken': ['kUK5RON4eMj3PPeIYTvW7XtqOAzrKDiIXVfI93zizMPJ6kQQOku4PHch3uSGRnfK']}>
            print(request.FILES) #文件对象 <MultiValueDict: {'myfile': [<InMemoryUploadedFile: meinv.jpg (image/jpeg)>]}>
            file_obj = request.FILES.get('myfile')
            print(file_obj.name)   # 打印文件名
            with open(file_obj.name,'wb') as f:    #默认写在项目根目录下
                for line in file_obj:
                    f.write(line)
    
        return  render(req,'fileput.html')
    

    注意: request.FILES是一个字典类型的数据,key是前端input标签的name值,值就是前端上传的文件对象。

    • file_obj = request.FILES.get('myfile') //获取文件对象
    • file_name = file_obj.name //获取文件名称
      文件是以二进制的方式传递到后端的,所以将上传的文件写入到服务端本地时,直接用wb模式

    2. ajax上传文件

    模版文件fileput.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    {#                                        传文件必须用form-data这种类型#}
    <form action="/fileput/" method="post" enctype="multipart/form-data">
        {% csrf_token %}
        <p><input type="text" name="username"></p>
        <p><input type="file" name="myfile"></p>
    {#    <input type="submit">#}
    </form>
    
    <button>sendAjax</button>
    
    <script src="/static/dist/js/jquery-3.2.1.js"></script>
    <script>
        $('button').click(function () {
            var formdata = new FormData();  //JS语法,实例化出来的对象是一个类似python字典一样的容器,可以传大文件;
            formdata.append('username',$('[name="username"]').val());
            formdata.append('imgFile',$('[name="myfile"]')[0].files[0]);
            formdata.append('csrfmiddlewaretoken',$('[name="csrfmiddlewaretoken"]').val());
            
            $.ajax('/fileput/',{
                type:'post',
                processData:false,    //不做预处理,默认为true
                contentType:false,    //关闭默认数据类型,默认值为x-www-form-urlencoded
                data: formdata,
                success:function (data) {
                    console.log(data)
                }
            })
        })
    </script>
    </body>
    </html>
    

    注意:

    • var formdata = new FormData(); 是JS语法,实例化出来的对象是一个类似python字典一样的容器,可以传大文件;
    • $('[name="myfile"]')[0].files[0])  可以取到存储在<input type="file" name="myfile">中的文件对象,文件对象中包含以下图中的内容:
      文件对象中包含的内容
    • processData默认值是truecontentType默认值是x-www-form-urlencoded,这个配置的意义是,当processData的值为true时,就将数据预处理成contentType设定的类型,contentType:"x-www-form-urlencoded"会将数据处理成这样“?a=1&b=2”发送至后端,当没有写processData和contentType时,浏览器默认processData为ture,contentType为'x-www-form-urlencoded'。

    特别注意:利用ajax上传文件时,必须将processData设置为falsecontentType设置为false。也就是不对数据做预处理。否则前端jquery会报错。

    视图文件views.py:

    def fileput(request):
        if request.is_ajax():     #判断是不是ajax请求
            print(request.POST)
            file_obj = request.FILES.get('imgFile')
            imgpath = r'./blog/static/images/'
            print(file_obj.name)
            filepath = os.path.join(imgpath,file_obj.name)
            with open(filepath,'wb') as f:     #将上传的文件写到指定路径
                for line in file_obj:
                    f.write(line)
            return HttpResponse('上传成功')
    
        return  render(request,'fileput.html')
    

    3. 上传头像时预览头像

    模版html文件上传头像的部分代码内容如下:

    <!-- 头像预览部分的CSS样式 -->
    <style>
        #avatar{
          position: relative;
           width: 60px;
          height: 60px;
        }
    
        #avatar_img,#file{
          width: 60px;
          height: 60px;
          position: absolute;
          left: 20px;
          top:0;
          border: 1px solid white;
          -webkit-border-radius: 8px;
          -moz-border-radius: 8px;
          border-radius: 8px;
        }
        #file{
          opacity: 0;
        }
    </style>
    
    <form class="form-horizontal" id="f1" novalidate method="post">
       <div class="form-group has-feedback" >
            <label for="id_password_confirm" class="col-md-2 control-label">头像</label>
            <div class="col-md-7" id="avatar">
                <p><img src="/static/images/default.png" alt="" id="avatar_img"></p>
                <p><input type="file" id="file"></p>
            </div>
        </div>
    </form>
    
    <script>
        // 头像预览功能,就是将img标签的src路径换成要上传的本地文件路径,此时还不会上传至服务端。
        $("#file").change(function () {
            //必须用onchange事件,当标签发生改变时触发执行。
              var choose_file=$("#file")[0].files[0];
    
              var reader=new FileReader();   //文件阅读器对像
    
             reader.readAsDataURL(choose_file);    //可以读出指定文件的url路径
    
             reader.onload=function () {
                $("#avatar_img").attr("src",this.result)
                 //this就是当前事件对象,this.result就是reader.result,获取的值是choose_file文件对象的url路径
            }
        })
    </script>
    

    注意:头像预览功能原理时,点击上传文件后选择了图片后,就将img标签的src路径换成被选择的图片本地路径,不能使用点击事件(onclick),应该使用onchange事件(jquery中是change事件),当标签发生改变时才触发。

    4. 头像存储至数据库

    头像存储的路径

    models.py中的UserInfo类中的avatar字段设置:

    class UserInfo(Form):
        ...
        avatar = models.FileField(verbose_name='头像', upload_to='avatar/', default="avatar/default.png")
        ...其他字段省略
    

    数据库中存储头像的字段avatar,实际存储的是这个头像文件的相对路径,是通过UserInfo表中avatar字段的upload_to参数的值+文件名:即avatar/a.png。django会在项目根目录下创建一个名为avatar的目录,将文件存至avatar目录下。

    当settings.py中配置了

    # 用户上传的文件路径配置
    MEDIA_URL = '/media/'     # 是MEDIA_ROOT的别名
    MEDIA_ROOT = os.path.join(BASE_DIR,'blog','media')    #在blog应用下创建一个media目录
    

    django就会自动在MEDIA_ROOT目录下创建一个avatar目录,然后将文件存至此目录下,真实文件存储的绝对路径:MEDIA_ROOT+upload_to+文件名。假如项目名称为myblog,那文件存储的真实路径为:./myblog/blog/media/avatar/a.png

    访问上传的图片

    假设用户上传了一个图片:a.jpg
    如果settings.py中设置了MEDIA_URL="/media/",访问时应该写的路径为:/media/avatar/a.jpg

    要想在前端页面直接访问用户上传的文件,还需要配置一条url,urls.py添加如下:

    # 必须先导入如下模块
    from django.views.static import serve
    from myblog import settings
    urlpatterns = [
        #... 省略部分配置 ...
        # 添加media 配置
        url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
    ]
    

    前端上传头像后,后端视图函数views.py存入数据库:

    def register(req):
        form = RegistForm()
        if req.method == 'POST':
            response = {'status': True, 'msg': None, 'query': None}
            form = RegistForm(req.POST)
            if form.is_valid():
                valid_code = form.cleaned_data['valid']
                if valid_code.upper() == req.session['valid_code'].upper():
    
                    form.cleaned_data.pop('password_confirm')
                    form.cleaned_data.pop('valid')       # 验证码和确认密码不需要存入数据库,所以需要从干净数据中去除掉。
                    avatar = req.FILES.get('avatar')     # 从前端获取上传的文件对象
                    form.cleaned_data['avatar'] = avatar
                        # 将文件对象加入到干净数据中,以便写入到数据库中;\
                        # django会根据models.py中的UserInfo类下的avatar字段的upload_to参数设定的目录名,创建一个目录,\
                        # 然后把这个文件对象存至这个目录下,数据库中存的是这个文件的相对路径
                    user = UserInfo.objects.create_user(**form.cleaned_data)
    
                    if user:
                        response['username'] = form.cleaned_data['username']
                else:
                    response['status'] = False
                    response['query'] = '验证码错误'
            else:
                response['status'] = False
                response['msg'] = form.errors
            return HttpResponse(json.dumps(response))
        return render(req,'register.html',locals())
    

    models.py中avatar字段方法

    user = UserInfo.objects.get(nid=1)
    print('avatar_type:',user.avatar,type(user.avatar))
    print('URL:',user.avatar.url)
    print('PATH:',user.avatar.path)
    print('NAME:',user.avatar.name)
    print('SIZE:',user.avatar.size)
    

    以上输出的结果:

    avatar_type: avatar/meinv.jpg <class 'django.db.models.fields.files.FieldFile'>
    URL: /media/avatar/meinv.jpg
    PATH: E:\python\python_study\day22\myblog\blog\media\avatar\meinv.jpg
    NAME: avatar/meinv.jpg
    SIZE: 5241
    

    相关文章

      网友评论

        本文标题:第22天,文件上传

        本文链接:https://www.haomeiwen.com/subject/nxdzixtx.html