15.Django 文件上传

文件上传

简单的文件上传

# 前端\django\myweb\blog\views\upload.py
from django.shortcuts import render

# 文件上传
def upload_list(request):

    if request.method == "GET":

        return render(request, 'upload_list.html')

    # print(request.POST)
    # 请求体中的数据  <QueryDict: {'csrfmiddlewaretoken': ['a1XA483cVlBrDSjxKUmKFuceHVH1xK71zcN5RkMOi2O3s3QsYY2ZXRJUc4c5rtxd'], 'username': ['123'], 'avatar': ['haha.png']}>  只有文件名
    # print(request.FILES)    # 请求发过来的文件  <MultiValueDict: {}>

    # form enctype="multipart/form-data" 标签增加<form method="post" enctype="multipart/form-data">
    # print(request.POST)    # <QueryDict: {'csrfmiddlewaretoken': ['vajr76cDl985ap69GUW2puOdyRDRgIOJUl9WUiVfIQlHZAD4UYChHRlT308VareV'], 'username': ['123']}>
    # print(request.FILES)    # 请求发过来的文件  <MultiValueDict: {'avatar': [<InMemoryUploadedFile: haha.png (image/png)>]}>

    file_obj = request.FILES.get("avatar")
    # print(file_obj.name)  # haha.png

    # with open('blog/static/img/haha2.png', mode="wb") as f:
    with open(f'blog/static/img/{file_obj.name}', mode="wb") as f:
        for chunk in file_obj.chunks():
            f.write(chunk)
    # 前端\django\myweb\haha2.png  默认保存在项目根目录

    return HttpResponse("haha")
<!-- 前端\django\myweb\blog\templates\upload_list.html -->
{% extends 'layout.html' %}

{% block content %}
<div class="container">
  <form method="post" enctype="multipart/form-data">
    {% csrf_token %}
    <input type="text" name="username">
    <input type="file" name="avatar">
    <input type="submit" value="提交">
  </form>
</div>

{% endblock %}

批量上传案例-普通

excel 处理: pip install openpyxl

<!-- 前端\django\myweb\blog\templates\pretty_list.html -->
{% extends 'layout.html' %}

{% block content %}
<div class="container">
  <div class="row">
    <div class="col-lg-6">
      <div class="input-group">
        <a href="/pretty/add/">
          <button class="btn btn-success"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>
            新增靓号-ModelForm</button>
        </a>
      </div>
    </div>
    <div class="col-lg-6">
      <form method="get" action="/pretty/list/">
        <div class="input-group">
          <input type="text" class="form-control" placeholder="查询手机号" name="q" value="{{ search_data }}">
          <span class="input-group-btn">
            <button class="btn btn-primary" type="submit"><span class="glyphicon glyphicon-search"
                aria-hidden="true"></span><strong> 搜索</strong></button>
          </span>
        </div>
      </form>
    </div>
  </div>
  <div style="margin-top: 20px;" class="panel panel-default">
    <div class="panel-heading">
      <strong>
        <span class="glyphicon glyphicon-upload" aria-hidden="true"></span> 普通上传案例
      </strong>
    </div>
    <div class="panel-body">
      <form method="post" enctype="multipart/form-data" action="/pretty/upload/">
        {% csrf_token %}
        <input type="file" name="pretty_excel">
        <br>
        <input type="submit" value="Form 上传" class="btn btn-primary">
      </form>
    </div>
  </div>
  <div style="margin-top: 20px;">
    <div class="panel panel-default">
      <div class="panel-heading">
        <strong>
          <span class="glyphicon glyphicon-th-list" aria-hidden="true"></span> 靓号列表
        </strong>
      </div>
      <table class="table table-bordered">
        <thead>
          <tr>
            <th>ID</th>
            <th>手机号码</th>
            <th>价格</th>
            <th>等级</th>
            <th>状态</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody>
          {% for iteam in pretty_list %}
          <tr>
            <th scope="row">{{ iteam.id }}</th>
            <td>{{ iteam.mobile }}</td>
            <td>{{ iteam.price }}</td>
            <td>{{ iteam.get_level_display }}</td>
            <td>{{ iteam.get_status_display }}</td>
            <td>
              <a class="btn btn-primary btn-xs" href="/pretty/{{ iteam.id }}/edit/"> 编辑 </a>
              <a class="btn btn-danger btn-xs" href="/pretty/{{ iteam.id }}/del/"> 删除 </a>
            </td>
          </tr>
          {% endfor %}
        </tbody>
      </table>
    </div>
  </div>
  <nav aria-label="page">
    <ul class="pagination">
      {{ page_string }}
      <form method="get" action="/pretty/list/">
        <div class="input-group col-sm-2" style="display: inline-block;margin-left: 20px;">
          <span class="input-group-btn">
            <input type="text" class="form-control" placeholder="页面跳转" name="page">
            <button class="btn btn-primary" type="submit">>> 跳转</button>
          </span>
        </div>
      </form>
    </ul>
  </nav>
</div>
{% endblock %}
# 前端\django\myweb\blog\views\pretty.py
# pretty_upload函数
def pretty_upload(request):

    # 获取用户上传的文件对象  # 前端\django\myweb\blog\static\img\test.xlsx
    file_obj = request.FILES.get("pretty_excel")
    # print(type(file_obj))   # <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
    # from django.core.files.uploadedfile import InMemoryUploadedFile     # 查看对象原码内容

    # 对象传递给 openpyxl,由openpyxl 读取文件的内容
    # import os
    from openpyxl import load_workbook

    # file_path = os.path.join('files', 'test.xlsx')
    # work_book_obj = load_workbook(file_path)
    work_book_obj = load_workbook(file_obj)
    sheet_list = work_book_obj.sheetnames    # 获取所有sheet的名字
    # print(sheet_list)   # ['Sheet1']
    sheet = work_book_obj.worksheets[0]
    # print(type(sheet))   # <class 'openpyxl.worksheet.worksheet.Worksheet'>
    # print(sheet)    # <Worksheet "Sheet1">

    # 读取表格内容
    # cell = sheet.cell(1, 1)   # 读取第一行第一列
    # print(cell)   # <Cell 'Sheet1'.A1>
    # print(cell.value)  # 手机号码

    # 循环获取第一行数据
    # sheet.rows # 每一行
    for row in sheet.iter_rows(min_row=2):    # 第一行是标题,从第二行开始取数据
        # print(row)   # (<Cell 'Sheet1'.A2>, <Cell 'Sheet1'.B2>, <Cell 'Sheet1'.C2>, <Cell 'Sheet1'.D2>) ......

        # print(row[0].value, row[1].value, row[2].value, row[3].value)   # 15388868690 88888690 1 2 ......
        row_dict = {
            "mobile": row[0].value, 
            "price": row[1].value, 
            "level": row[2].value, 
            "status": row[3].value
            }

        exists = PrettyNum.objects.filter(**row_dict).exists()
        if not exists:
            PrettyNum.objects.create(**row_dict)

    return redirect("/pretty/list/")

文件上传插件Bootstrap File Input

Bootstrap文件上传插件File Input: https://plugins.krajee.com/file-input

# bootstrap file input 插件上传
@csrf_exempt
def upload_fileinput(request):

    file_obj = request.FILES.get("input-b1")
    # print(file_obj.name)

    with open(f'blog/static/img/{file_obj.name}', mode="wb") as f:
        for chunk in file_obj.chunks():
            f.write(chunk)

    return JsonResponse({"status": True})
<div style="margin-top: 20px;" class="panel panel-default">
<div class="panel-heading">
    <strong>
    <span class="glyphicon glyphicon-upload" aria-hidden="true"></span> Bootstrap File Input 插件案例
    </strong>
</div>
<div class="panel-body file-loading">
    <input id="input-b1" name="input-b1" type="file" data-show-preview="true" multiple>
</div>
</div>

{% block js %}
{% load static %}
<script src="{% static 'plugins/bootstrap-fileinput-5.5.4/js/fileinput.min.js' %}"></script>
<script src="{% static 'plugins/bootstrap-fileinput-5.5.4/js/locales/zh.js' %}"></script>
{% endblock %}
{% block script %}
<script>
$(document).ready(function () {
    $("#input-b1").fileinput({
    uploadUrl: "/upload/fileinput/",
    maxFileCount: 5
    });
});
</script>
{% endblock %}

混合数据案例-Form方法

# 前端\django\myweb\blog\utils\modelform.py
class BootStrap:
    bootstrap_exclude_fields = []
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 循环ModelForm中的所有字段,给每个字段的插件设置
        for name, field in self.fields.items():
            if name in self.bootstrap_exclude_fields:
                continue
            # 字段中有属性,保留原来的属性,没有属性,才增加
            if field.widget.attrs:
                field.widget.attrs["class"] = "form-control"
                field.widget.attrs["style"] = "width: 300px;"
                if  "placeholder" in field.widget.attrs:
                    continue
                else:
                    field.widget.attrs["placeholder"] = field.label
            else:
                field.widget.attrs = {
                    "class": "form-control",
                    "style": "width: 300px;",
                    "placeholder": field.label
                }
class BootStrapForm(BootStrap, forms.Form):
    pass

# 前端\django\myweb\blog\utils\form.py
# Form 文件上传
class UploadForm(BootStrapForm):
    bootstrap_exclude_fields = ["img"]
    name = forms.CharField(label="姓名")
    age = forms.IntegerField(label="年龄")
    img = forms.FileField(label="头像")
# 前端\django\myweb\blog\views\upload.py
# Form上传案例
def upload_form(request):

    form =  UploadForm(data=request.POST, files=request.FILES)

    if form.is_valid():
        # print(form.cleaned_data)   # {'name': 'tang', 'age': 18, 'img': <InMemoryUploadedFile: haha.png (image/png)>}
        # 读取图片内容,写入到文件夹中并获取文件的路径
        image_obj = form.cleaned_data.get('img')
        file_path = f'blog/static/img/{image_obj.name}'
        with open(file_path, mode="wb") as f:
            for chunk in image_obj.chunks():
                f.write(chunk)

        # 装图片文件路径写入到数据库
        db_file_path = f'/static/img/{image_obj.name}'
        Boss.objects.create(
            name=form.cleaned_data['name'],
            age=form.cleaned_data['age'],
            img=db_file_path
        )
        return redirect(db_file_path)

    return render(request, 'upload_list.html', {"form": form})
<!-- 前端\django\myweb\blog\templates\upload_list.html -->
<div style="margin-top: 20px;" class="panel panel-default">
<div class="panel-heading">
    <strong>
    <span class="glyphicon glyphicon-upload" aria-hidden="true"></span> Form 上传案例
    </strong>
</div>
<div class="panel-body">
    <form method="post" enctype="multipart/form-data" action="/upload/form/" novalidate>
    {% csrf_token %}
    {% for field in form %}
    <div class="form-group">
        <label>{{ field.label }}</label>
        {{ field }}
        <span style="color: red;">{{ field.errors.0 }}</span>
    </div>
    {% endfor %}
    <div class="form-group" style="margin-top: 20px;">
        <button type="submit" class="btn btn-primary">Form 上传案例</button>
    </div>
    </form>
</div>
</div>

Django开过程中两个特殊的文件夹

  • static 存放静态文件的路径,包括:css,js,项目图片
  • media 用户上传的数据
启用media

在urls.py中进行配置 前端\django\myweb\myweb\urls.py

from django.urls import path, re_path
from django.conf import settings
from django.views.static import serve

urlpatterns = [
    re_path(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}, name='media'),
]
# 前端\django\myweb\myweb\settings.py
import os

# 启用media
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = '/media/'

混合数据案例-ModelForm方法-完整代码

<!-- # 前端\django\myweb\blog\templates\upload_list.html -->
{% extends 'layout.html' %}

{% block css %}
{% load static %}
<link rel="stylesheet" href="{% static 'plugins/bootstrap-fileinput-5.5.4/css/fileinput.min.css' %}">
<link rel="stylesheet" href="{% static 'plugins/icons-1.5.0/font/bootstrap-icons.min.css' %}">
{% endblock %}

{% block content %}
<div class="container">
  <div>
    <form method="post" enctype="multipart/form-data">
      {% csrf_token %}
      <input type="text" name="username">
      <input type="file" name="avatar">
      <input type="submit" value="提交">
    </form>
  </div>
  <br>
  <div style="margin-top: 20px;" class="panel panel-default">
    <div class="panel-heading">
      <strong>
        <span class="glyphicon glyphicon-upload" aria-hidden="true"></span> Bootstrap File Input 插件案例
      </strong>
    </div>
    <div class="panel-body file-loading">
      <input id="input-b1" name="input-b1" type="file" data-show-preview="true" multiple>
      <input readonly="" class="file-caption-name form-control kv-fileinput-caption" placeholder="Select files ..." title="">
    </div>
  </div>
  <br>

  <div style="margin-top: 20px;" class="panel panel-default">
    <div class="panel-heading">
      <strong>
        <span class="glyphicon glyphicon-upload" aria-hidden="true"></span> Form 上传案例-Boss
      </strong>
    </div>
    <div class="panel-body">
      <form method="post" enctype="multipart/form-data" action="/upload/form/" novalidate>
        {% csrf_token %}
        {% for field in form %}
        <div class="form-group">
          <label>{{ field.label }}</label>
          {{ field }}
          <span style="color: red;">{{ field.errors.0 }}</span>
        </div>
        {% endfor %}
        <div class="form-group" style="margin-top: 20px;">
          <button type="submit" class="btn btn-primary">Form 上传案例-Boss</button>
        </div>
      </form>
    </div>
  </div>
  <!-- 新建/编辑-对话框 -->
  <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
    <div class="modal-dialog" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
              aria-hidden="true">×</span></button>
          <h3 class="modal-title" id="myModalLabel">ModelForm 上传案例-City</h3>
        </div>
        <div class="modal-body">
          <form id="formModal" class="form-group" method="post" enctype="multipart/form-data"
            action="/upload/modelform/" novalidate>
            {% csrf_token %}
            {% for field in modelform %}
            <div class="form-group">
              <label>{{ field.label }}</label>
              {{ field }}
              <span style="color: red;">{{ field.errors.0 }}</span>
            </div>
            {% endfor %}
            <div class="form-group" style="margin-top: 20px;">
              <button type="submit" class="btn btn-primary">ModelForm 上传案例-City</button>
            </div>
          </form>
        </div>
      </div>
    </div>
  </div>
  <div style="margin-top: 20px;" class="panel panel-default">
    <div class="panel-heading">
      <strong>
        <span class="glyphicon glyphicon-upload" aria-hidden="true"></span> ModelForm 上传案例-City
      </strong>
    </div>
    <div class="panel-body">
      <div class="form-group" style="margin-top: 20px;">
        <input type="button" value="ModelForm 上传案例" class="btn btn-primary" data-toggle="modal"
        data-target="#myModal">
      </div>
    </div>
  </div>
  <div style="margin-top: 20px;">
    <div class="panel panel-default">
      <div class="panel-heading">
        <strong>
          <span class="glyphicon glyphicon-th-list" aria-hidden="true"></span> 城市列表
        </strong>
      </div>
      <table class="table table-bordered">
        <thead>
          <tr>
            <th>ID</th>
            <th>城市</th>
            <th>人口</th>
            <th>LOGO</th>
          </tr>
        </thead>
        <tbody>
          {% for iteam in city_list %}
          <tr>
            <th scope="row">{{ iteam.id }}</th>
            <td>{{ iteam.name }}</td>
            <td>{{ iteam.count }}</td>
            <td><img src="/media/{{ iteam.img }}" style="height: 80px;" alt=""></td>
          </tr>
          {% endfor %}
        </tbody>
      </table>
    </div>
  </div>
</div>
{% endblock %}

{% block js %}
{% load static %}
<script src="{% static 'plugins/bootstrap-fileinput-5.5.4/js/fileinput.min.js' %}"></script>
<script src="{% static 'plugins/bootstrap-fileinput-5.5.4/js/locales/zh.js' %}"></script>
{% endblock %}
{% block script %}
<script>
  $(document).ready(function () {
    $("#input-b1").fileinput({
      language: "zh",
      uploadUrl: "/upload/fileinput/",
      maxFileCount: 5,
      allowedFileExtensions: ["jpg", "png", "gif"]
    });
  });
</script>
{% endblock %}
# 前端\django\myweb\blog\views\upload.py

from django.shortcuts import render, redirect
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from blog.utils.form import UploadForm, UploadModelForm
from blog.models import Boss,City
from django.conf import settings

# 文件上传

def upload_list(request):

    if request.method == "GET":
        form = UploadForm()
        modelform = UploadModelForm()

        city_list = City.objects.all().order_by('-id')

        return render(request, 'upload_list.html', {"form": form, "modelform": modelform, "city_list": city_list})

    # print(request.POST)
    # 请求体中的数据  <QueryDict: {'csrfmiddlewaretoken': ['a1XA483cVlBrDSjxKUmKFuceHVH1xK71zcN5RkMOi2O3s3QsYY2ZXRJUc4c5rtxd'], 'username': ['123'], 'avatar': ['haha.png']}>  只有文件名
    # print(request.FILES)    # 请求发过来的文件  <MultiValueDict: {}>

    # form enctype="multipart/form-data" 标签增加<form method="post" enctype="multipart/form-data">
    # print(request.POST)    # <QueryDict: {'csrfmiddlewaretoken': ['vajr76cDl985ap69GUW2puOdyRDRgIOJUl9WUiVfIQlHZAD4UYChHRlT308VareV'], 'username': ['123']}>
    # print(request.FILES)    # 请求发过来的文件  <MultiValueDict: {'avatar': [<InMemoryUploadedFile: haha.png (image/png)>]}>

    file_obj = request.FILES.get("avatar")
    # print(file_obj.name)  # haha.png

    # with open('blog/static/img/haha2.png', mode="wb") as f:
    with open(f'blog/static/img/{file_obj.name}', mode="wb") as f:
        for chunk in file_obj.chunks():
            f.write(chunk)
    # 前端\django\myweb\haha2.png  默认保存在项目根目录

    return HttpResponse("haha")

# bootstrap file input 插件上传
@csrf_exempt
def upload_fileinput(request):

    file_obj = request.FILES.get("input-b1")
    # print(file_obj.name)

    with open(f'blog/static/img/{file_obj.name}', mode="wb") as f:
        for chunk in file_obj.chunks():
            f.write(chunk)

    return JsonResponse({"status": True})

# Form上传案例
def upload_form(request):

    form = UploadForm(data=request.POST, files=request.FILES)

    if form.is_valid():
        # print(form.cleaned_data)   # {'name': 'tang', 'age': 18, 'img': <InMemoryUploadedFile: haha.png (image/png)>}
        # 读取图片内容,写入到文件夹中并获取文件的路径
        image_obj = form.cleaned_data.get('img')
        file_path = f'blog/static/img/{image_obj.name}'
        with open(file_path, mode="wb") as f:
            for chunk in image_obj.chunks():
                f.write(chunk)

        # 装图片文件路径写入到数据库
        db_file_path = f'/static/img/{image_obj.name}'
        Boss.objects.create(
            name=form.cleaned_data['name'],
            age=form.cleaned_data['age'],
            img=db_file_path
        )
        # print(settings.MEDIA_ROOT)
        return redirect(db_file_path)

    return render(request, 'upload_list.html', {"form": form})

# ModelForm上传案例
def upload_modelform(request):

    modelform = UploadModelForm(data=request.POST, files=request.FILES)
    if modelform.is_valid():
        # 对于文件,自动保存至upload_to目录,img字段自动写入文件上传路径
        modelform.save()
        # print(modelform.instance.img)   # city/haha.png

        return redirect(settings.MEDIA_URL + str(modelform.instance.img))

    return render(request, 'upload_list.html', {"modelform": modelform})
# 前端\django\myweb\blog\utils\form.py
# Form 文件上传
class UploadForm(BootStrapForm):
    bootstrap_exclude_fields = ["img"]
    name = forms.CharField(label="姓名")
    age = forms.IntegerField(label="年龄")
    img = forms.FileField(label="头像")

# ModelForm 文件上传
class UploadModelForm(BootStrapModelForm):
    bootstrap_exclude_fields = ["img"]

    class Meta:
        model = City
        fields = '__all__'
# 前端\django\myweb\blog\models.py

# Form文件上传
class Boss(models.Model):
    name = models.CharField(verbose_name="姓名", max_length=32)
    age = models.IntegerField(verbose_name="年龄")
    img = models.CharField(verbose_name="头像",max_length=128)     # 保存文件路径

# ModelForm文件上传
class City(models.Model):
    name = models.CharField(verbose_name="名称", max_length=32)
    count = models.IntegerField(verbose_name="人口")
    img = models.FileField(verbose_name="Logo", max_length=128, upload_to="city/")   # 本质上数据也是CharField,自动保存数据,upload_to 指的是media目录下指定目录