17 文章管理页功能+fastdfs 服务器搭建

后端功能实现

apps/admin/views.py

import json
import logging

from datetime import datetime
from urllib.parse import urlencode

import requests
from django.core.paginator import Paginator, EmptyPage

from django.db.models import Count
from django.shortcuts import render
from django.views import View


# Create your views here.
from apps.news import models
from apps.admin import constants

from utils.json_fun import to_json_data
from utils.res_code import Code, error_map
from utils import paginator_script

logger = logging.getLogger('django')


class IndexView(View):
    """
    /admin/
    """
    def get(self,request):
        """

        :param request:
        :return:
        """
        return render(request,'admin/index/index.html')


class TagManagerView(View):
    """
    /admin/tag/
    """
    def get(self,request):
        """
        要返回给前端: tag_name news_name
        :param request:
        :return:
        """
        #分组查询 annotate 配合 values 使用     values 指定输出的内容(id: ,name: ),是字典格式
         #按标签名(tag_name)和 id(tag_id) 分组,统计每个标签(Tag)中 news(外键关联,
        # 通过 news的主键 id 统计) 的数量,并且按降序排列
        tags = models.Tag.objects.select_related('news').values('id','name').annotate(num_news = \
                  Count('news')).filter(is_delete=False).order_by('-num_news','update_time')
        return render(request,'admin/news/tag_manage.html',locals())

    def post(self,request):
        """
        get_or_create(defaults=None, **kwargs)

         一个通过给出的kwargs 来查询对象的便捷方法(如果你的模型中的所有字段都有默认值,可以为空),需要的话创建一个对象。
         返回一个由(object, created)组成的元组,元组中的object 是一个查询到的或者是被创建的对象,
         created 是一个表示是否创建了新的对象的布尔值。

        :param request:
        :return:
        """
        # 1. 获取参数
        json_data = request.body
        if not json_data:
            return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
        dict_data = json.loads(json_data.decode('utf8'))
        #从前端传进来的要修改的标签名
        tag_name = dict_data.get('name')
        if tag_name:
            # tag_name = tag_name.strip()
            # a = tag_name
            #get_or_create 查名为 tag_name 的标签,无则增,有则查 得到的是元组类型    请确保只在 post 请求中使用它
            tag_tuple = models.Tag.objects.filter(is_delete=False).get_or_create(name=tag_name)
            #对得到的元组进行拆包
            tag_instance,tag_boolean = tag_tuple
            #如果 tag_boolean 为 True,即创建了名字为 tag_name 的标签
            # 否则就是查到了名字为 tag_name 的标签,即标签已存在,不创建
            if tag_boolean:
                news_tag_dict = {
                    'id':'tag_instance.id',
                    'name':'tag_instance.name',
                }
                return to_json_data(errmsg='标签创建成功!',data=news_tag_dict)
            else:
                return to_json_data(errno=Code.DATAEXIST,errmsg='标签已存在!')
        else:
            return to_json_data(errno=Code.PARAMERR,errmsg='标签不能为空!')


#传参:
class TagEditView(View):
    """
    /admin/tag/<tag_id>/
    """
    def delete(self,request,tag_id):
        """

        :param request:
        :param tag_id:
        :return:
        """
        tag = models.Tag.objects.only('id').filter(id=tag_id).first()
        if tag:
            tag.is_delete = True
            #只改 is_delete 字段,用于优化
            tag.save(update_fields=['is_delete'])
            #删除后局部刷新,用 ajax,返回给前端 json 格式
            return to_json_data(errmsg='标签删除成功!')
        else:
            return to_json_data(errno=Code.PARAMERR,errmsg='标签不存在!')

    def put(self,request,tag_id):
        """

        :param request:
        :param tag_id:
        :return:
        """
        #修改之后的 name 通过 ajax 获取  只有一个参数,用 ajax 即可
        # 1. 获取参数
        json_data = request.body    #byte str
        if not json_data:
            return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
        dict_data = json.loads(json_data.decode('utf8'))
        # 从前端传进 要修改的 tag
        tag_name = dict_data.get('name')
        # 从数据库中拿到 tag
        tag = models.Tag.objects.only('name').filter(id=tag_id).first()
        if tag:
            #去掉两边的空格
            tag_name = tag_name.strip()
            #如果不加 is_delete 删除的标签再创建或者修改无法实现
            if tag.name == tag_name:
                return to_json_data(errno=Code.PARAMERR,errmsg='标签未变化!')
            if not models.Tag.objects.only('id').filter(name=tag_name, is_delete=False).exists():
            #如果标签没变化,提示报错
                # if tag.name == tag_name:
                #     return to_json_data(errno=Code.PARAMERR,errmsg='标签未变化!')
                tag.name = tag_name
                tag.save(update_fields=['name', 'update_time'])
                return to_json_data(errmsg='标签更新成功!')
            else:
                return to_json_data(errno=Code.PARAMERR,errmsg='标签已经存在!')
        else:
            #有异常的一定会用到 errno
            return to_json_data(errno=Code.PARAMERR,errmsg='标签不存在!')


# 热门文章管理页面
#news.title,tag.name,hotnews.priority
class HotNewsManageView(View):
    """
    /admin/hotnews/
    """
    def get(self,request):
        hot_news = models.HotNews.objects.select_related('news__tag').only('news__title','news__tag__name','news__id').\
            filter(is_delete=False).order_by('priority','-news__clicks')[:constants.SHOW_HOTNEWS_COUNT]
        return render(request,'admin/news/news_hot.html',locals())


class HotNewsEditView(View):
    """
    /admin/hotnews/<int:tag_id>
    """
    def delete(self,request,hotnews_id):
        hotnews = models.HotNews.objects.only('id').filter(id=hotnews_id).first()
        if hotnews:
            hotnews.is_delete = True
            hotnews.save(update_fields = ['is_delete','update_time'])
            return to_json_data(errmsg='热门文章删除成功!')
        else:
            return to_json_data(errno=Code.PARAMERR,errmsg='需要删除的热门文章不存在!')

    def put(self,request,hotnews_id):
        """
        更新热门新闻
        #1. 获取参数
        :param request:
        :param hotnews_id:
        :return:
        """
        json_data = request.body
        if not json_data:
            return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
        dict_data = json.loads(json_data.decode('utf8'))
        try:
            priority = int(dict_data.get('priority'))
            #列表生成式 对于不需要的可以用 _ 代替
            priority_list = [i for i,_ in models.HotNews.PRI_CHOICES]
            if priority not in priority_list:
                return to_json_data(errno=Code.PARAMERR,errmsg='热门文章优先级设置错误!')
        except Exception as e:
            logger.info('热门文章优先级异常:\n{}'.format(e))
            return to_json_data(errno=Code.PARAMERR,errmsg='热门文章优先级设置错误!')

        hotnews = models.HotNews.objects.only('id').filter(id=hotnews_id).first()
        if not hotnews:
            return to_json_data(errno=Code.PARAMERR,errmsg='需要更新的热门新闻不存在!')
        if hotnews.priority == priority:
            return to_json_data(errno=Code.PARAMERR,errmsg='热门新闻优先级未改变!')
        hotnews.priority = priority
        hotnews.save(update_fields=['priority','update_time'])
        return to_json_data(errmsg='热门新闻优先级更新成功!')


class HotNewsAddView(View):
    """
    添加热门新闻,先选标签,才能选标签下的文章,选择优先级不受他们影响
    /addmin/hotnews/add/
    """
    def get(self,request):
        #选择标签和优先级
        #有排序
        tags = models.Tag.objects.values('id','name').annotate(num_news = Count('news')).filter(is_delete=False).\
                order_by('-num_news','update_time')
        #将列表嵌套个元组转换成字典
        priority_dict = dict(models.HotNews.PRI_CHOICES)
        return render(request,'admin/news/news_hot_add.html',locals())

    def post(self,request):
        #添加热门新闻
        #1.获取参数
        json_data = request.body
        if not json_data:
            return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])
        dict_data = json.loads(json_data.decode('utf8'))
        #news_id priority
        try:
            news_id = int(dict_data.get('news_id'))
        except Exception as e:
            logger.info('热门文章:\n{}'.format(e))
            return to_json_data(errno=Code.PARAMERR,errmsg='参数错误')
        if not models.News.objects.filter(id=news_id).exists():
            return to_json_data(errno=Code.PARAMERR,errmsg='文章不存在!')
        try:
            priority = int(dict_data.get('priority'))
            priority_list = [i for i,_ in models.HotNews.PRI_CHOICES]
            if priority not in priority_list:
                return to_json_data(errno=Code.PARAMERR,errmsg='热门文章优先级设置错误!')
        except Exception as e:
            logger.info('热门文章优先级异常:\n{}'.format(e))
            return to_json_data(errno=Code.PARAMERR,errmsg='热门文章优先级设置错误')
        hotnews_tuple = models.HotNews.objects.get_or_create(news_id=news_id)
        hotnews,is_created = hotnews_tuple
        hotnews.priority = priority
        hotnews.save(update_fields=['priority','update_time'])
        return to_json_data(errmsg='热门文章创建成功!')


class NewsByTagIdView(View):
    """
    选完标签后进行这步,选标签下的文章
    不用展示 tag_id
    /admin/tags/<int:tag_id>/news/
    """
    def get(self,request,tag_id):
        news = models.News.objects.filter(is_delete=False,tag_id=tag_id).values('id','title')
        #将字典嵌套进列表
        news_list = [i for i in news]
        return to_json_data(data={
            'news':news_list
        })


#请求方式:get
#携带的方式:?start_time=&end_time=&title=&author_name=&tag_id=
#返回的参数:5个 title author_username tag_name update_time id
class NewsManageView(View):
    """
    /admin/news/
    """
    def get(self,request):
        #查询
        tags = models.Tag.objects.only('id','name').filter(is_delete=False)
        newses = models.News.objects.select_related('author','tag').only('title',\
                'author__username','tag__name','update_time').filter(is_delete=False)
        #通过时间进行过滤
        try:
            #用于查询 url 中的字符串参数
            # 寻找名为 start_time 的 GET 参数,而且如果参数没有提交,返回一个空的字符串
            # 这里的 get() 是每个python的的字典数据类型都有的方法
            # 使用 get('start_time', '') 提供一个缺省的返回值''(一个空字符串)
            # 如果只是使用 request.GET['q'] 访问变量,在Get数据时 q 不可得, 可能引发 KeyError
            #查询起始时间
            start_time = request.GET.get('start_time','')
            #对时间格式化
            start_time = datetime.strptime(start_time,'%Y/%m/%d') if start_time else ''
            #查询截止时间
            end_time = request.GET.get('end_time','')
            #对时间格式化
            end_time = datetime.strptime(end_time,'%Y/%m/%d') if end_time else ''
        except Exception as e:
            #获取不到起始,截止时间就报错,然后重置
            logger.info('用户输入的时间有误:\n{}'.format(e))
            start_time = end_time = ''
        if start_time and not end_time:
            #__gte 大于 __let  小于 __range 范围
            newses = newses.filter(update_time__gte=start_time)

        if end_time and not start_time:
            newses = newses.filter(update_time__lte=end_time)

        if start_time and end_time:
            newses = newses.filter(update_time__range=(start_time,end_time))
        #通过 title 进行过滤
        title = request.GET.get('title','')
        if title:
            #__icontains 包含并且忽略大小写
            newses = newses.filter(title__icontains=title)
        #通过作者名进行过滤
        author_name = request.GET.get('author_name','')
        if author_name:
            newses = newses.filter(author__username__icontains=author_name)
        #通过标签 id 进行过滤
        try:
            tag_id = int(request.GET.get('tag_id',0))
        except Exception as e:
            logger.info('标签错误:\n{}'.format(e))
            tag_id = 0
        #id 存在但没有 news 的时候,返回值为空
        if tag_id:
            newses = newses.filter(tag_id=tag_id)
        #id 存在但没有 news 的时候,返回前面查询到的 news
        # newses = newses.filter(is_delete=False,tag_id=tag_id) or newses.filter(is_delete=False)

        #获取第几页内容
        try:
            page = int(request.GET.get('page',1))
        except Exception as e:
            logger.info('当前页数错误:\n{}'.format(e))
            page = 1
        paginator = Paginator(newses,constants.PER_PAGE_NEWS_COUNT)
        try:
            news_info = paginator.page(page)
        except EmptyPage:
            #若用户访问的页数大于实际页数,则返回最后一页
            logging.info('用户返回的页数大于总页数!')
            news_info = paginator.page(paginator.num_pages)

        paginator_data = paginator_script.get_paginator_data(paginator,news_info)
        start_time = start_time.strftime('%Y/%m/%d') if start_time else ''
        end_time = end_time.strftime('%Y/%m/%d') if end_time else ''
        context = {
            'news_info': news_info,
            'tags': tags,
            'start_time': start_time,
            'end_time': end_time,
            'title': title,
            'author_name': author_name,
            'tag_id': tag_id,
            'other_param': urlencode({  #将以下参数拼接成 url 返回给前端
                'start_time': start_time,
                'end_time': end_time,
                'title': title,
                'author_name': author_name,
                'tag_id': tag_id,
            })
        }
        #将 paginator_script 里的返回值增加到上下文中,返回给前端
        #update 字典方法,将 paginator_data 加入到 context 中
        context.update(paginator_data)
        return render(request, 'admin/news/news_manage.html', context=context)

apps/admin/constants.py

#每页新闻数
PER_PAGE_NEWS_COUNT = 8
#显示热门新闻条数
SHOW_HOTNEWS_COUNT = 3

apps/admin/urls.py

from apps.admin import views
from django.urls import path


app_name = 'admin'

urlpatterns = [
    path('',views.IndexView.as_view(),name='index'),
    path('tags/',views.TagManagerView.as_view(),name='tags'),
    path('tags/<int:tag_id>/',views.TagEditView.as_view(),name='tag_edit'),
    path('hotnews/',views.HotNewsManageView.as_view(),name='hotnews'),
    path('hotnews/<int:hotnews_id>/',views.HotNewsEditView.as_view(),name='hotnews_edit'),
    path('hotnews/add/',views.HotNewsAddView.as_view(),name='hotnews_add'),
    path('tags/<int:tag_id>/news/',views.NewsByTagIdView.as_view(),name='hotnews_by_tagid'),
    path('news/', views.NewsManageView.as_view(), name='news_manage'),

]

utils/paginator_script.py

def get_paginator_data(paginator, current_page, around_count=3):
    """
    :param paginator: 分页对象
    :param current_page: 当前页数据
    :param around_count: 显示的页码数
    :return: 当前页码、总页数、左边是否有更多页标记、右边是否有更多标记
    左边页码范围、右边页码范围
    """
    current_page_num = current_page.number  # 获取当前页面所在的页码
    total_page_num = paginator.num_pages  # 获取总页数

    left_has_more_page = False  # 默认左边没有更多页
    right_has_more_page = False  # 默认右边没有更多页

    # 算出当前页面左边的页码
    left_start_index = current_page_num - around_count
    left_end_index = current_page_num
    if current_page_num <= around_count * 2 + 1:
        left_page_range = range(1, left_end_index)
    else:
        left_has_more_page = True
        left_page_range = range(left_start_index, left_end_index)

    right_start_index = current_page_num + 1
    right_end_index = current_page_num + around_count + 1
    if current_page_num >= total_page_num - around_count * 2:
        right_page_range = range(right_start_index, total_page_num + 1)
    else:
        right_has_more_page = True
        right_page_range = range(right_start_index, right_end_index)

    return {
        "current_page_num": current_page_num,
        "total_page_num": total_page_num,
        "left_has_more_page": left_has_more_page,
        "right_has_more_page": right_has_more_page,
        "left_pages": left_page_range,
        "right_pages": right_page_range,
    }

前端功能实现

templates/admin/news/news_manage.html

{% extends 'admin/base/base.html' %}

{% load static %}
{% block title %}
 文章管理页
{% endblock %}

{% block content_header %}
  文章管理
{% endblock %}

{% block header_option_desc %}
  正确的决策来自众人的智慧
{% endblock %}


{% block content %}
  <link rel="stylesheet" href="{% static 'css/admin/base/bootstrap-datepicker.min.css' %}">
 <style>
   .ml20 {
     margin-left: 20px;
   }

   .mt20 {
     margin-top: 20px;
   }
 </style>
 <div class="box">
   <div class="box header" style="margin: 0;">
     <form action="" class="form-inline">
       <div class="form-group ml20 mt20">
         <label for="select-time">时间:</label>
         {% if start_time %}
         <input type="text" class="form-control" placeholder="请选择起始时间" readonly
                id="select-time" name="start_time" value="{{ start_time }}">
           {% else %}
           <input type="text" class="form-control" placeholder="请选择起始时间" readonly
                  id="select-time" name="start_time">
         {% endif %}
         -
          {% if end_time %}
        <input type="text" class="form-control" placeholder="请选择结束时间" readonly
               name="end_time" value="{{ end_time }}">
          {% else %}
            <input type="text" class="form-control" placeholder="请选择结束时间" readonly name="end_time">
          {% endif %}
       </div>
       <div class="form-group ml20 mt20">
         <label for="title">标题:</label>
         {% if title %}
           <input type="text" class="form-control" placeholder="请输入标题" id="title" name="title" value="{{ title }}">
           {% else %}
          <input type="text" class="form-control" placeholder="请输入标题" id="title" name="title">
         {% endif %}

       </div>
       <div class="form-group ml20 mt20">
         <label for="author">作者:</label>
         {% if author_name %}
           <input type="text" class="form-control" placeholder="请输入作者" id="author" name="author_name"
                  value="{{ author_name }}">
         {% else %}
           <input type="text" class="form-control" placeholder="请输入作者" id="author" name="author_name">
         {% endif %}
       </div>
       <div class="form-group ml20 mt20">
         <label for="tag">标签:</label>
         <select class="form-control" id="tag" name="tag_id">
           <option value="0">--请选择标签--</option>
           {% for one_tag in tags %}

             {% if tag_id and one_tag.id == tag_id %}
               <option value="{{ one_tag.id }}" selected>{{ one_tag.name }}</option>
             {% else %}
               <option value="{{ one_tag.id }}">{{ one_tag.name }}</option>
             {% endif %}

           {% endfor %}
         </select>
       </div>
       <div class="form-group ml20 mt20">
         <button class="btn btn-primary">查询</button>
         <a href="{% url 'admin:news_manage' %}" class="btn btn-info ml20">清除查询</a>
       </div>
     </form>
   </div>
   <div class="box-body">
     <table class="table table-bordered table-hover">
       <thead>
       <tr>
         <th>标题</th>
         <th>作者</th>
         <th>标签</th>
         <th>发布时间</th>
         <th>操作</th>
       </tr>
       </thead>
       <tbody>
        {% for one_news in news_info %}
          <tr>
{#           <td><a href="{% url 'news:news_detail' one_news.id%}" target="_blank">{{ one_news.title }}</a></td>#}
           <td><a href="#" target="_blank">{{ one_news.title }}</a></td>
           <td>{{ one_news.author.username }}</td>
           <td>{{ one_news.tag.name }}</td>
           <td>{{ one_news.update_time }}</td>
           <td>
{#             <a href="{% url 'admin:news_edit' one_news.id %}" class="btn btn-xs btn-warning">编辑</a>#}
             <a href="#" class="btn btn-xs btn-warning">编辑</a>
{#             <a href="#" class="btn btn-xs btn-warning">编辑</a>#}
             <a href="javascript:void (0);" class="btn btn-xs btn-danger btn-del" data-news-id="{{ one_news.id }}">删除</a>
           </td>
         </tr>
        {% endfor %}


       </tbody>
     </table>
   </div>
   <div class="box-footer">
     <span class="pull-left">第{{ current_page_num }}页/总共{{ total_page_num }}页</span>
     <nav class="pull-right">
       <!-- 分页 -->
       <ul class="pagination">
         <!-- 上一页 -->
         {% if news_info.has_previous %}
            <li><a href="?page={{ news_info.previous_page_number }}&{{ other_param }}">上一页</a></li>
           {% else %}
           <li class="disabled"><a href="javascript:void(0);">上一页</a></li>
         {% endif %}
       
          {% if left_has_more_page %}
            <li><a href="?page=1&{{ other_param }}">1</a></li>
            <li><a href="javascript:void(0);">...</a></li>
          {% endif %}
          <!-- 左边的页码 -->
          {% for left_page in left_pages %}
            <li><a href="?page={{ left_page }}&{{ other_param }}">{{ left_page }}</a></li>
          {% endfor %}

          <!-- 当前页面 -->
          {% if current_page_num %}
            <li class="active"><a href="?page={{ current_page_num }}&{{ other_param }}">{{ current_page_num }}</a></li>
          {% endif %}
          <!-- 右边的页面 -->
          {% for right_page in right_pages %}
              <li><a href="?page={{ right_page }}&{{ other_param }}">{{ right_page }}</a></li>
          {% endfor %}

         {% if right_has_more_page %}
          <li><a href="javascript:void(0);">...</a></li>
            <li><a href="?page={{ total_page_num }}&{{ other_param }}">{{ total_page_num }}</a></li>
        {% endif %}

         <!-- 下一页 -->
          {% if news_info.has_next %}
            <li><a href="?page={{ news_info.next_page_number }}&{{ other_param }}">下一页</a></li>
            {% else %}
            <li class="disabled"><a href="javascript:void(0);">下一页</a></li>
          {% endif %}

       </ul>
     </nav>
   </div>
 </div>
{% endblock %}

{% block script %}
 <script src="{% static 'js/admin/news/bootstrap-datepicker.min.js' %}"></script>
 <script src="{% static 'js/admin/news/bootstrap-datepicker.zh-CN.min.js' %}"></script>
 <script src="{% static 'js/admin/news/news_manage.js' %}"></script>
{% endblock %}

static/js/admin/news/news_manage.js

$(function () {
  let $startTime = $("input[name=start_time]");
  let $endTime = $("input[name=end_time]");
  const config = {
    // 自动关闭
    autoclose: true,
    // 日期格式
    format: 'yyyy/mm/dd',
    // 选择语言为中文
    language: 'zh-CN',
    // 优化样式
    showButtonPanel: true,
    // 高亮今天
    todayHighlight: true,
    // 是否在周行的左侧显示周数
    calendarWeeks: true,
    // 清除
    clearBtn: true,
    // 0 ~11  网站上线的时候
    startDate: new Date(2018, 10, 1),
    // 今天
    endDate: new Date(),
  };
  $startTime.datepicker(config);
  $endTime.datepicker(config);

  // 删除标签
  let $newsDel = $(".btn-del");  // 1. 获取删除按钮
  $newsDel.click(function () {   // 2. 点击触发事件
    let _this = this;
    let sNewsId = $(this).data('news-id');
    swal({
      title: "确定删除这篇文章吗?",
      text: "删除之后,将无法恢复!",
      type: "warning",
      showCancelButton: true,
      confirmButtonColor: "#DD6B55",
      confirmButtonText: "确定删除",
      cancelButtonText: "取消",
      closeOnConfirm: true,
      animation: 'slide-from-top',
    }, function () {

      $.ajax({
        // 请求地址
        url: "/admin/news/" + sNewsId + "/",  // url尾部需要添加/
        // 请求方式
        type: "DELETE",
        dataType: "json",
      })
        .done(function (res) {
          if (res.errno === "0") {
            // 更新标签成功
            message.showSuccess("标签删除成功");
            $(_this).parents('tr').remove();
          } else {
            swal({
              title: res.errmsg,
              type: "error",
              timer: 1000,
              showCancelButton: false,
              showConfirmButton: false,
            })
          }
        })
        .fail(function () {
          message.showError('服务器超时,请重试!');
        });
    });

  });


  // get cookie using jQuery
  function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
      let cookies = document.cookie.split(';');
      for (let i = 0; i < cookies.length; i++) {
        let cookie = jQuery.trim(cookies[i]);
        // Does this cookie string begin with the name we want?
        if (cookie.substring(0, name.length + 1) === (name + '=')) {
          cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
          break;
        }
      }
    }
    return cookieValue;
  }

  function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
  }

  // Setting the token on the AJAX request
  $.ajaxSetup({
    beforeSend: function (xhr, settings) {
      if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
        xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
      }
    }
  });

});

fastdfs 分布式文件系统

  • FastDFS是用c语言编写的一款开源的分布式文件系统,FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务

简介

  • c 语言
  • 开源
  • 冗余备份,负载均衡,线性扩容

架构

  • tracker server(调度服务器):负载均衡和调度
  • storage server(存储服务器):负载文件存储
    在这里插入图片描述

文件上传流程

在这里插入图片描述

安装 fastdfs

  • 安装 tracker
    docker run -dti --network=host --name tracker -v /var/fdfs/tracker:/var/fdfs youkou1/fastdfs tracker
  • 安装 storage
    docker run -dti --network=host --name storage -e TRACKER_SERVER=10.0.2.15:22122 -v /var/fdfs/storage:/var/fdfs youkou1/fastdfs storage

测试是否安装成功

utils/fastdfs/client.conf

# connect timeout in seconds
# default value is 30s
connect_timeout=30

# network timeout in seconds
# default value is 30s
network_timeout=60

# the base path to store log files
# base_path=utils/fastdfs/logs

# tracker_server can ocur more than once, and tracker_server format is
#  "host:port", host can be hostname or ip address
tracker_server=自己的 ip:22122	#端口固定

#standard log level as syslog, case insensitive, value list:
### emerg for emergency
### alert
### crit for critical
### error
### warn for warning
### notice
### info
### debug
log_level=info

# if use connection pool
# default value is false
use_connection_pool = false

# connections whose the idle time exceeds this time will be closed
# unit: second
# default value is 3600
connection_pool_max_idle_time = 3600

# if load FastDFS parameters from tracker server
# default value is false
load_fdfs_parameters_from_tracker=false

# if use storage ID instead of IP address
# same as tracker.conf
# valid only when load_fdfs_parameters_from_tracker is false
# default value is false
use_storage_id = false

# specify storage ids filename, can use relative or absolute path
# same as tracker.conf
# valid only when load_fdfs_parameters_from_tracker is false
storage_ids_filename = storage_ids.conf


#HTTP settings
http.tracker_server_port=80

安装相关包

  • fdfs_client.zip 文件是从百度云中下载的
pip install fdfs_client.zip
pip install mutagen
pip install requests

通过 shell 测试

(env) xiaoge@xiaoge-i23:~/web_prv$ python manage.py shell
Python 3.6.7 (default, Oct 22 2018, 11:32:17) 
Type 'copyright', 'credits' or 'license' for more information
IPython 7.5.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: from fdfs_client.client import Fdfs_client    	                                                                              

In [2]: FDFS_Client = Fdfs_client('utils/fastdfs/client.conf')                                                                         

In [3]: ret = FDFS_Client.upload_by_filename('media/2018.png')                                                                         
getting connection
<fdfs_client.connection.Connection object at 0x7f6bfa511f28>
<fdfs_client.fdfs_protol.Tracker_header object at 0x7f6bfa511e48>

In [4]: ret                                                                                                                            
Out[4]: 
{'Group name': 'group1',
 'Remote file_id': 'group1/M00/00/00/wKgAaV0XhviANYUiAAfh_rrm7jw453.png',
 'Status': 'Upload successed.',
 'Local file name': 'media/2018.png',
 'Uploaded size': '504.00KB',
 'Storage IP': '自己的 ip'}
 #登录服务器查看:  ip+端口(设置为8888)/ +Remote file_id,效果如下图

在这里插入图片描述

  • 由于 docker 在国外,国内限制比较大,所以我们一般会使用镜像加速
  • 在 /etc/docker/daemon.json 中写入如下内容(如果文件不存在请新建该文件)
{
  "registry-mirrors": [
    "https://dockerhub.azk8s.cn",
    "https://reg-mirror.qiniu.com"
  ]
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值