django 用户上传头像

转载:https://blog.csdn.net/Ayhan_huang/article/details/78193553

static & media

在Django应用的文件夹下,经常会发现这两个文件夹:static, media; static称为静态文件夹,用于存放CSS, JavaScript, 网站logo等不变的文件;相对的,把media称为媒体文件夹,用于存放用户上传的图片。

static 配置和使用

  • 配置项目的settings.py:
STATIC_URL = '/static/'  # 静态文件别名(相对路径) 和 绝对路径
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'app01/static'),
)
# STATIC_ROOT 配置部署的时候才用
  • 1
  • 2
  • 3
  • 4
  • 5
  • 使用:
{% load static %}
<img src="{% static 'img/default.jpg' %}" alt="default_photo"/>
  • 1
  • 2

media 配置和使用

如果需要保存用户上传的图片或文件,需要作如下配置:

  • 配置项目的settings.py:
MEDIA_URL = "/media/"   # 媒体文件别名(相对路径) 和 绝对路径
MEDIA_ROOT = (
    os.path.join(BASE_DIR, 'app01/media')
)
  • 1
  • 2
  • 3
  • 4
  • 配置路由:
from django.conf.urls import url
from django.views.static import serve
from . import settings

urlpatterns = [
    # ... the rest of your URLconf goes here ...
    url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT})
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • models.py中设置上传图片/文件字段:
from django.db import models


class UserInfo(models.Model):
    username = models.CharField(verbose_name='用户名', max_length=32)
    avatar = models.FileField(verbose_name='头像', upload_to='upload/avatar/')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

upload_to相当于上传到app01/media/upload/avatar/目录下。

注意:

FileFieldImageField字段适用于存储文件/图片,出于性能考虑,文件并不直接保存到数据库,而是保存在文件系统里,因此该字段只是记录一个路径而已。

这个路径是相对于MEDIA_ROOT的,要想得到文件/图片的绝对路径,需要用.url方法。比如,要在页面中显示用户user_obj的头像,那么在模板中可以这样写:

<img src="{{ user_obj.avatar.url }}" alt="user_avatar">
  • 1

其它参考:
http://blog.csdn.net/java2king/article/details/5334303

http://blog.csdn.net/junli_chen/article/details/47335919

用户上传头像实践

准备工作

  • 新建一个项目,创建应用app01,如上配置好static和media,配置路由如下:
from django.conf.urls import url
from app01 import views
from django.views.static import serve
from . import settings

urlpatterns = [
    url(r'^register/$', views.register),
    url(r'^upload/$', views.upload),
    url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 定义register视图函数,处理用户注册请求:
def register(request):
    if request.method == 'GET':
        return render(request, 'register.html')
  • 1
  • 2
  • 3
  • register.html如下:
{% load static %}

<style>
    div.custom-avatar {
        position: relative;
        width: 60px;
        height: 60px;
    }

    .sol {
        position: absolute;
        top: 0;
        left: 50px;
        width: 60px;
        height: 60px;
    }

    div.custom-avatar input {
        opacity: 0;
    }

</style>

<form>
    <p><label for="username">用户名:</label></p>
    <p><input type="text" id="username" name="username"></p>
    <div class="custom-avatar">
        <label for="avatar">头像:</label>
        <img src="{% static 'img/default.jpg' %}" id="avatar" class="sol">
        <input type="file" id="file-choose" class="sol">
    </div>
    <p><button>提交</button></p>
</form>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

说明:

这里通过<img src="{% static 'img/default.jpg' %}"...从server请求一张默认头像;

定义CSS,令选择文件按钮和默认头像重合;

  • 浏览器访问http://127.0.0.1:8000/register/,页面如下:

    2.png

说明:浏览器查看默认头像地址为:http://127.0.0.1:8000/static/img/default.jpg;如果在调试过程中无法显示来自server的图像,可以通过在浏览器中查看图像地址是否正确。

图像预览

要求:用户每次选择一张图片,页面中即时显示该图片的预览

要点:JS onchange事件,每次用户选择了新图片,生成新的预览;FileReader文件阅读器,将文件对象转化为路径对象

<script>
    //头像预览
    var fileChoose = document.getElementById("file-choose");

    fileChoose.onchange = function() {
        var file = this.files[0]; //files[0] 通过DOM对象拿到文件对象;如果是使用jQuery: $(this)[0].files[0], 要通过$(this)[0] 索引,先从JQ对象集合中拿到DOM对象
        var reader = new FileReader(); //实例化FileReader
        reader.readAsDataURL(file); //将文件对象转化为路径对象

        reader.onload = function () { //FileReader.onload 属性
            var imgEle = document.getElementById("avatar");
            imgEle.src = this.result //这里的this指reader对象
        }
    }
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

说明:

  1. var file = this.files[0]files[0] 通过DOM对象拿到文件对象;如果是使用jQuery: $(this)[0].files[0], 要通过$(this)[0]索引,先从JQ对象集合中拿到DOM对象
  2. FileReader.onload属性:当FileReader对象调用readAsDataURL 方法后,会触发load事件,执行后面的函数。

图像上传

要点:FormDate对象发送二进制数据

  • 前端上传数据
<script>
    //上传图片,这里选择通过Ajax提交
    var form = document.getElementsByTagName('form')[0];
    form.onsubmit = function (e) {
        e.preventDefault(); //阻止默认提交
        var username = document.getElementById('username').value;
        var fileObject = document.getElementById("file-choose").files[0]; //拿到图片文件对象

        //实例化FormData对象,添加数据 data.append(key, value)
        var data = new FormData();
        data.append('username', username);
        data.append('img', fileObject);

        //XMLHttpRequest对象发送Ajax请求
        var url = '/register/';
        var xmlHttp = new XMLHttpRequest();
        xmlHttp.open('POST', url, true);
        xmlHttp.send(data);
    }
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

说明:如果是通过jQuery发送FormDate数据,那么需要加两个参数:contentType: false, processData: false:

var data = new FormData();
data.append('username', username);
data.append('img', fileObject);

$.ajax({
    url: '/register/',
    type: 'POST',
    data: data,
    processData: false, //不进行转码或预处理
    contentType: false, //不进行"application/x-www-form-urlencoded"的默认编码处理
    success: function (response) {
        console.log(response)
    }
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 后端处理,改写register视图函数,处理POST请求:
def register(request):
    if request.method == 'GET':
        return render(request, 'register.html')

    username = request.POST.get('username')
    avatar = request.FILES.get('img')
    print(avatar)  # 会打印出文件名'XXX.jpg'
    models.UserInfo.objects.create(username=username, avatar=avatar)

    return HttpResponse('ok')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 测试,输入用户名,选择一张图片,点击提交

这里写图片描述

说明:为了方便测试,settings.py中注释掉了csrf中间件。

查看数据库,用户创建成功:

2.png

查看media目录,图片上传成功:

3.png


以上测试项目完整源码:https://github.com/Ayhan-Huang/Dango-static-media

补充:

如果图片并不是数据中model的字段,那么需要自己写一个函数,来保存图片:

def upload_file(request):
    # 拿到文件,保存在指定路径
    print(request.FILES) # {'imgFile': [<InMemoryUploadedFile: QQ图片20170523192846.jpg (image/jpeg)>]}
    imgFile = request.FILES.get('imgFile')  # 拿到文件对象,imgFile.name, 拿到文件名

    with open('app01/media/upload/img/'+imgFile.name,'wb')as f:   # with open 无法创建文件夹,需要自己创建
        for chunk in imgFile.chunks():
            f.write(chunk)
    # 返回json响应
    response = {
        'error': 0,
        'url': '/media/upload/img/'+imgFile.name
        # 客户端拿到路径,才能预览图片; media在setting中配置了别名,这里只写media,客户端就可以找到路径,前面不需要加/app
    }
    return HttpResponse(json.dumps(response))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

说明:

  1. request.FILES中拿到图片对象
  2. imgFile.name拿到文件名
  3. 返回Json响应,error告知错误状态,url用于前端对图片进行预览。
posted @ 2024-09-21 14:33  silence_cho  阅读(34)  评论(0编辑  收藏  举报