Django项目实战之用户头像上传与访问

1 将文件保存到服务器本地

upload.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">

</head>
<body>

<form action="" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    <div>用户名:<input type="text" name="username"></div>
    <div>头像<input type="file" name="avatar"></div>
    <input type="submit" value="提交">
</form>

</body>
</html>

urls.py

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^upload',views.upload)
]

views.py

from django.shortcuts import render,HttpResponse

def upload(request):
    if request.method == 'POST':
        name = request.POST.get('username')
        avatar = request.FILES.get('avatar')

        with open(avatar.name,'wb') as f:
            for line in avatar:
                f.write(line)
        return HttpResponse('ok')

    return render(request,'upload.html')

总结

这样,我们就做好了一个基本的文件上传小示例,这里需要注意的有几点:

  1. form表单里需要加上csrf_token验证
  2. 文件的input框的type的值为file
  3. 在视图函数中获取文件要用request.FILES.get()方法
  4. 通过obj.name可以获取文件的名字

2 将文件上传到数据库

models.py

from django.db import models


class User(models.Model):
    username = models.CharField(max_length=16)
    avatar = models.FileField(upload_to='avatar')

views.py

def upload(request):
    if request.method == 'POST':
        name = request.POST.get('username')
        avatar = request.FILES.get('avatar')

        models.User.objects.create(username=name,avatar=avatar)
        return HttpResponse('ok')

    return render(request,'upload.html')

总结

上面已经实现了将文件上传到数据库的功能,需要注意的有几点:

  1. 所谓的上传到数据库,不是讲图片本身或者二进制码放在数据库,实际上也是将文件上传到服务器本地,数据库只是存了一个文件的路径,这样用户要调用文件的时候就可以通过路径去服务器指定的位置找了
  2. 创建ORM的时候,avatar字段要有一个upload_to=''的属性,指定上传后的文件放在哪里
  3. 往数据库添加的时候,文件字段属性赋值跟普通字段在形式上是一样的,如:models.User.objects.create(username=name,avatar=avatar)
  4. 如果有两个用户上传的文件名重复,系统会自动将文件改名,效果如下:

附加

功能我们是实现了,看起来我们在调用文件的时候,只需要通过数据库文件路径已经保存的文件本身就可以访问图片,让它出现在网页上,其实并不是这样,

我们需要配置一些东西,django才可以找的到,不然的话就会过不了urls验证,而我们之所以可以直接访问static里的静态文件,是因为django已经帮我们配置好了。

配置步骤如下:

1、在站点的setting.py里配置

 MEDIA_ROOT=os.path.join(BASE_DIR,"blog","media")  #blog是项目名,media是约定成俗的文件夹名
 MEDIA_URL="/media/"      # 跟STATIC_URL类似,指定用户可以通过这个路径找到文件

2、在urls.py里配置

from django.views.static import serve
from upload import settings                #upload是站点名

url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
配置完后,就可以通过http://127.0.0.1:8001/media/milk.png访问到图片了

 3 用AJAX提交文件

upload.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>

<form>
    {% csrf_token %}
    <div>用户名:<input id="name-input" type="text"></div>
    <div>头像<input id="avatar-input" type="file"></div>
    <input id="submit-btn" type="button" value="提交">
</form>

<script src="/static/js/jquery-3.2.1.min.js"></script>
<script>
    $('#submit-btn').on('click',function () {
        formdata = new FormData();
        formdata.append('username',$('#name-input').val());
        formdata.append('avatar',$('#avatar-input').val());
        formdata.append("csrfmiddlewaretoken",$("[name='csrfmiddlewaretoken']").val());

        $.ajax({
            processData:false,
            contentType:false,

            url:'/upload',
            type:'post',
            data:formdata,
            success:function (arg) {
                if (arg.state == 1){
                    alert('成功!')
                }else {
                    alert('失败!')
                }
            }
        })
    });
</script>

</body>
</html>

views.py

from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from app01 import models

def upload(request):
    if request.method == 'POST':
        name = request.POST.get('username')
        avatar = request.FILES.get('avatar')

        try:
            models.User.objects.create(username=name,avatar=avatar)
            data = {'state':1}
        except:
            data = {'state':0}

        return JsonResponse(data)

    return render(request,'upload.html')

总结

  1. Ajax上传的时候,按钮的tpye一定不要用submit
  2. Ajax上传的时候data参数的值不再是一个普通‘字典’类型的值,而是一个FormData对像
    • 创建对象formdata = new FormData();
    • 往里面添加值formdata.append('username',$('#name-input').val());
  3. Ajax在做post提交的时候要加上csrf验证
    • formdata.append("csrfmiddlewaretoken",$("[name='csrfmiddlewaretoken']").val());
  4. 最后,Ajax上传文件的时候要有两个参数设置
    • processData:false
    • contentType:false

4 上传图片文件的时候有预览功能

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>

<form>
    <!----用一个label标签将上传文件输入框跟图片绑定一起,
          点击图片的时候就相当于点击了上传文件的按钮---->
    <label><img id="avatar-img" src="/static/img/default.png" width="80px" height="80px">
        <div>头像<input id="avatar-input" hidden type="file"></div>
    </label>

    <input id="submit-btn" type="button" value="提交">
</form>

<script src="/static/js/jquery-3.2.1.min.js"></script>

<script>

    // 上传文件按钮(label里的图片)点击事件
    $('#avatar-input').on('change',function () {
        // 获取用户最后一次选择的图片
        var choose_file=$(this)[0].files[0];
        // 创建一个新的FileReader对象,用来读取文件信息
        var reader=new FileReader();
        // 读取用户上传的图片的路径
        reader.readAsDataURL(choose_file);
        // 读取完毕之后,将图片的src属性修改成用户上传的图片的本地路径
        reader.onload=function () {
             $("#avatar-img").attr("src",reader.result)
        }
    });

</script>

 5 大总结

对于文件上传,不管是直接form提交也好,Ajax提交也好,根本问题是要告诉浏览器你要上传的是一个文件而不是普通的字符串

而怎么样告诉浏览器呢,就是通过请求体重的ContentType参数,我们上传普通的字符串的时候不用指定,因为它有默认值,

而如果要传文件的话,就要另外指定了。总结以下几点

  1. form表单上传的话是通过 enctype="multipart/form-data" 来指定ContentType
  2. ajax上传的话是通过  processData:false 和 contentType:false来指定ContentType
  3. form上传的时候,文件数据是通过<input type="file">标签来‘’包裹‘’数据,
  4. ajax上传的时候,是通过一个 FormData 实例对象来添加数据,传递的时候传递这个对象就行了
  5. 数据传递过去之后,是封装在request.FILES里而不是request.POST里

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏web

记一次webpack打包优化

2466
来自专栏about云

zookeeper思考与总结1:在其它组件的作用及hdfs对比

一、zookeeper在其它组件的作用 1.为什么会产生zookeeper zookeeper是模仿的谷歌的Chubby来解决分布式一致性的问题。2006年的时...

4035
来自专栏BIT泽清

React Native应用部署/马甲包热更新-CodePush最新集成总结(2018年最新)

React Native支持大家用React Native技术开发APP,并打包生成一个APP。在动态更新方面React Native只是提供了动态更新的基础,...

3960
来自专栏wblearn

代码提示插件Spket

Spket是一个可以在Eclipse及MyEclipse中实现JavaScript代码自动提示、自动校验、自动纠错的插件。

1171
来自专栏零基础使用Django2.0.1打造在线教育网站

零基础使用Django2.0.1打造在线教育网站(四):简易留言簿基础开发

努力与运动兼备~有任何问题可以加我好友或者关注微信公众号,欢迎交流,我们一起进步!

4125
来自专栏java 成神之路

mysql 启动出错问题排查

2173
来自专栏更流畅、简洁的软件开发方式

【自然框架】稳定版beta1——源码下载,Demo说明

在线演示:http://demo.naturefw.com/ 开源协议   一直想说来着,可是总忘。开源协议采用 BSD开源协议。   一直在说开源,但只是把源...

2415
来自专栏bdcn

Flask学习笔记-在Bootstrap框架下Web表单WTF的使用 顶

表单的处理一般都比较繁琐和枯燥,如果想简单的使用表单就可以使用Flask-WTF插件,同时我们把WTF融合到Bootstrap中这样样式的问题都自动解决了,本篇...

4424
来自专栏前端之心

解决 "Script Error" 的另类思路

前端的同学如果用 window.onerror 事件做过监控,应该知道,跨域的脚本会给出 "Script Error." 提示,拿不到具体的错误信息和堆栈信息。

81048
来自专栏小程序之家

如何在小程序中实现文件上传下载

在如何实现小程序登录鉴权这篇文章中,我们实现了小程序的wx.request请求操作,除了request之外,小程序还有文件下载wx.downloadFile和文...

13.3K7

扫码关注云+社区

领取腾讯云代金券