1、Docker和FastDFS上传和下载文件
Docker安装运行FastDFS
获取FastDFS镜像
# 从仓库拉取镜像
sudo docker image pull delron/fastdfs
# 解压资料中本地镜像
sudo docker load -i 文件路径/fastdfs_docker.tar
开启tracker
容器
- 我们将
tracker
运行目录映射到宿主机的/var/fdfs/tracker
目录中。
sudo docker run -dit --name tracker --network=host -v /var/fdfs/tracker:/var/fdfs delron/fastdfs tracker
开启storage
容器
TRACKER_SERVER=Tracker
的ip地址:22122
(Tracker
的ip地址不要使用127.0.0.1
)- 我们将
storage
运行目录映射到宿主机的/var/fdfs/storage
目录中。
sudo docker run -dti --name storage --network=host -e TRACKER_SERVER=192.168.232.140:22122 -v /var/fdfs/storage:/var/fdfs delron/fastdfs storage
查看已经启动的storage和tracker
注意:如果无法重启storage
容器,可以删除/var/fdfs/storage/data
目录下的fdfs_storaged.pid
文件,然后重新运行storage
。
FastDFS客户端上传文件
Python版本的FastDFS
客户端使用参考文档:https://github.com/JaceHo/fdfs_client-py
- 安装
FastDFS
客户端扩展
• 安装准备好的fdfs_client-py-master.zip
到虚拟环境中
pip install fdfs_client-py-master.zip
pip install mutagen
pip install requests
从 Python版本的FastDFS
客户端使用参考文档:https://github.com/JaceHo/fdfs_client-py
中下载fdfs_client-py-master.zip
文件,安装到项目的虚拟环境中:
Windows的Python环境引入fdfs_client-py包遇到的问题
https://blog.csdn.net/mingyuli/article/details/104171195
如果安装不成功,请参考:
Windows的Python环境引入fdfs_client-py包遇到的问题
https://blog.csdn.net/mingyuli/article/details/104171195
解决方法:
Fastdfs
客户端ModuleNotFoundError: No module named 'mutagen._compat'
问题
https://www.cnblogs.com/sewen-H/p/13362598.html
- 准备FastDFS客户端扩展的配置文件
•shop.utils.fastdfs.client.conf
FastDFS客户端扩展的配置文件:shop/utils/fastdfs/client.conf
"""
FastDFS客户端扩展的配置文件:shop/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=E:\ENV\Django_project_shop\shop\static\images # 本机路径
# tracker_server can ocur more than once, and tracker_server format is
# "host:port", host can be hostname or ip address
tracker_server=192.168.232.140:22122 # 改为虚拟机ip
#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
# since V4.05
use_connection_pool = false
# connections whose the idle time exceeds this time will be closed
# unit: second
# default value is 3600
# since V4.05
connection_pool_max_idle_time = 3600
# if load FastDFS parameters from tracker server
# since V4.05
# 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
# since V4.05
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
# since V4.05
storage_ids_filename = storage_ids.conf
#HTTP settings
http.tracker_server_port=80
#use "#include" directive to include HTTP other settiongs
##include http.conf
注意:上面代码中需要修改的内容为
base_path=FastDFS客户端存放日志文件的目录
tracker_server=运行Tracker服务的机器ip:22122
- FastDFS客户端实现文件存储
# 使用 shell 进入 Python交互环境
python manage.py shell
# 1. 导入FastDFS客户端扩展
from fdfs_client.client import Fdfs_client
# 2. 创建FastDFS客户端实例 注意:windows环境下绝对路径会发生转义,需要加上 "r" 说明是原生字符串
client = Fdfs_client(r'绝对路径:shop/utils/fastdfs/client.conf')
# 3. 调用FastDFS客户端上传文件方法
ret = client.upload_by_filename('/kk.jpeg')
在创建FastDFS客户端实例
client = Fdfs_client(r'shop/utils/fastdfs/client.conf')
过程中,可能会出现:
UnicodeDecodeError: 'gbk' codec can't decode byte 0xaf in position 19: illegal multibyte sequence
的错误,
解决方案是:重新安装fdfs_client-py-master.zip
文件,并修改其中的代码文件
创建FastDFS客户端实例出现报错:Source contains parsing errors: [line 2]: '"""\n' [line 4]: '"""\n'
或者unicodeescape’ codec can’t decode bytes in position XXX: trun
解决方案为:
上传文件ret = client.upload_by_filename(r’E:/1.jpg’)
报错:Error: 10061 connect to 192.168.xxx,由于目标计算机积极拒绝,无法连接
解决方案:https://blog.csdn.net/CaoMei_HuaCha/article/details/80865444
上传文件fdfs_client.exceptions.DataError: [-] Error: 2, No such file or directory报错问题
:
解决方案:https://blog.csdn.net/weixin_45336184/article/details/104289171
最终上传成功
虚拟机服务器查看文件:
2、浏览器下载并渲染图片(nginx)
- 协议: http
- IP地址:192.168.232.140 (虚拟机服务器ip) Nginx服务器的IP地址
- 因为 FastDFS 擅长存储静态文件,但是不擅长提供静态文件的下载服务,所以我们一般会将 Nginx 服务器绑定到 Storage ,提升下载性能。
- 端口:8888 -------Nginx服务器的端口。
- 路径:
group1/M00/00/00/wKjojF9Q3pOAeBzvAAAN4yGVxhg331.png
• 文件在Storage上的文件索引。 - 完整图片下载地址
•http://192.168.232.140:8888/group1/M00/00/00/wKjojF9Q3pOAeBzvAAAN4yGVxhg331.png
Nginx安装
sudo apt install nginx
编写测试代码:shop.utils.fdfs_t.html
<img src="http://192.168.232.140:8888/group1/M00/00/00/wKjojF9Q3pOAeBzvAAAN4yGVxhg331.png" width="320" height="480">
安装nginx
虚拟机服务器
Win10中查看上传文件
成功查看上传的文件
3、录入商品数据和图片数据
SQL脚本录入商品数据
mysql -h127.0.0.1 -uroot -pmysql lgshop < 文件路径/goods_data.sql
FastDFS服务器录入图片数据
-
准备新的图片数据压缩包
-
删除 Storage 中旧的data目录
3. 拷贝新的图片数据压缩包到 Storage,并解压
# 解压命令
sudo tar -zxvf data.tar.gz
4. 查看新的data目录
4、首页广告
展示首页商品频道分类
分析首页商品频道分类数据结构
查询首页商品频道分类
首页广告视图文件apps/contents/views.py
"""
首页广告视图文件
apps/contents/views.py
"""
from django.shortcuts import render
from django.views import View
from django.http import HttpResponse
from collections import OrderedDict # 有序的字典,不同于普通字典
from goods.models import GoodsChannel,GoodsCategory # 商品频道,商品类别
class IndexView(View):
"""首页广告界面"""
def get(self, request):
"""提供首页界面"""
# 查看并展示商品的分类
categories = {}
# 查询所有的商品频道
# channels = GoodsChannel.objects.all()
# 37个一级类别
channels = GoodsChannel.objects.order_by('group_id', 'sequence') # 根据group_id排序,再根据组内顺序sequence排序
# 遍历所有频道
for channel in channels:
group_id = channel.group_id
if group_id not in categories: # category最顶级商品类别,如果当前频道不在顶级商品类别中
categories[group_id] = {'channel': [], "sub_cats": []} # 创建需要传输的数据格式
# 当前频道对应的一级类别
cat1 = channel.category # cat1是对象,category是外键字段
categories[group_id]['channel'].append({ # channel本身包含字典数据
"id": cat1.id,
"name": cat1.name, # category外键关联的name字段
"url": channel.url
})
# 查询二级和三级类别信息
for cat2 in GoodsCategory.objects.filter(parent__id=cat1.id).all(): # 通过频道对应的一级类别来查询
cat2.sub_cats = [] # 创建是为了方便cat3进行添加数据
categories[group_id]['sub_cats'].append({
"id": cat2.id,
"name": cat2.name, # cat2对象本身模型GoodsCategory的name字段
"sub_cats": cat2.sub_cats
})
# 查询三级类别信息
for cat3 in GoodsCategory.objects.filter(parent__id=cat2.id).all(): # 通过二级类别来查询三级
cat2.sub_cats.append({ # 方便cat3进行添加数据到cat2的list中
"id": cat3.id,
"name": cat3.name,
})
# categories[group_id]['sub_cats'][].append({ # 这种方法添加数据,sub_cats数据无法添加
# "id": cat3.id,
# "name": cat3.name,
# })
context = {
"categories": categories, # 整体拼接后的数据
}
return render(request, "index.html", context=context)
"""需要形成如下的数据格式并传输给前端页面进行渲染
{
"1":{
# 频道
"channels":[
{"id":1, "name":"手机", "url":"http://shouji.jd.com/"},
{"id":2, "name":"相机", "url":"http://www.baidu.cn/"}
],
# 二级标签
"sub_cats":[
{
"id":38,
"name":"手机通讯",
"sub_cats":[
{"id":115, "name":"手机"},
{"id":116, "name":"游戏手机"}
]
},
{
"id":39,
"name":"手机配件",
# 三级标签
"sub_cats":[
{"id":119, "name":"手机壳"},
{"id":120, "name":"贴膜"}
]
}
]
},
"2":{
"channels":[],
"sub_cats":[]
}
}
"""
渲染首页商品频道分类
首页界面:templates/index.html
{# apps/contents/views.py文件中传递的categories数据进行渲染 #}
<ul class="sub_menu">
{# 一级查询 #}
{% for group in categories.values %}
<li>
<div class="level1">
{% for channel in group.channels %}
<a href="{{ channel.url }}">{{ channel.name }}</a>
{% endfor %}
</div>
<div class="level2">
{# 二级查询 #}
{% for cat2 in group.sub_cats %}
<div class="list_group">
<div class="group_name fl">{{ cat2.name }} ></div>
<div class="group_detail fl">
{# 三级查询 #}
{% for cat3 in cat2.sub_cats %}
<a href="/list/{{ cat3.id }}/1/">{{ cat3.name }}</a>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
</li>
{% endfor %}
</ul>
展示首页商品广告
{ # 传输数据格式如下所示
"index_lbt":[
{
"id":1,
"category": 1,
"title": '美图',
"url": 'http://www.baidu.com',
"image": "group/M00/00/01/ADASDADA-CZXCSAasdsadada",
"text": "",
"sequence": 1,
"status": 1
},
{
"id":2,
"category": 1,
"title": '黑色星期五',
"url": 'http://www.baidu.com',
"image": "group/M00/00/01/ADASDADA-CZXCSAasdsadada",
"text": "",
"sequence": 2,
"status": 1
}
],
"index_kx":[
{
"id":5,
"category": 2,
"title": 'i7顽石低至3999',
"url": 'http://www.baidu.com',
"image": "group/M00/00/01/ADASDADA-CZXCSAasdsadada",
"text": "",
"sequence": 2,
"status": 1
},
{
"id":6,
"category": 2,
"title":'奥克斯转场',
"url": 'http://www.baidu.com',
"image": "group/M00/00/01/ADASDADA-CZXCSAasdsadada",
"text": "",
"sequence": 2,
"status": 1
}
]
}
"""
分析首页商品广告数据结构
结论:
- 首页商品广告数据由广告分类和广告内容组成。
- 广告分类带有标识符
key
,可以利用它确定广告展示的位置。 - 确定广告展示的位置后,再查询和渲染出该位置的广告内容。
- 广告的内容还有内部的排序字段,决定了广告内容的展示顺序。
查询首页商品广告:首页广告视图文件 apps/contents/views.py
"""
首页广告视图文件 apps/contents/views.py
"""
from django.shortcuts import render
from django.views import View
from django.http import HttpResponse
from collections import OrderedDict # 有序的字典,不同于普通字典
from goods.models import GoodsChannel,GoodsCategory # 商品频道,商品类别
from .models import ContentCategory,Content # 广告内容类别,广告内容
class IndexView(View):
"""首页广告界面"""
def get(self, request):
"""提供首页界面"""
# 查看并展示商品的分类
categories = {}
# 查询所有的商品频道
# channels = GoodsChannel.objects.all()
# 37个一级类别
channels = GoodsChannel.objects.order_by('group_id', 'sequence') # 根据group_id排序,再根据组内顺序sequence排序
# 遍历所有频道
for channel in channels:
group_id = channel.group_id
if group_id not in categories: # category最顶级商品类别,如果当前频道不在顶级商品类别中
categories[group_id] = {'channel': [], "sub_cats": []} # 创建需要传输的数据格式
# 当前频道对应的一级类别
cat1 = channel.category # cat1是对象,category是外键字段
categories[group_id]['channel'].append({ # channel本身包含字典数据
"id": cat1.id,
"name": cat1.name, # category外键关联的name字段
"url": channel.url
})
# 查询二级和三级类别信息
for cat2 in GoodsCategory.objects.filter(parent__id=cat1.id).all(): # 通过频道对应的一级类别来查询
cat2.sub_cats = [] # 创建是为了方便cat3进行添加数据
categories[group_id]['sub_cats'].append({
"id": cat2.id,
"name": cat2.name, # cat2对象本身模型GoodsCategory的name字段
"sub_cats": cat2.sub_cats
})
# 查询三级类别信息
for cat3 in GoodsCategory.objects.filter(parent__id=cat2.id).all(): # 通过二级类别来查询三级
cat2.sub_cats.append({ # 方便cat3进行添加数据到cat2的list中
"id": cat3.id,
"name": cat3.name,
})
# categories[group_id]['sub_cats'][].append({ # 这种方法添加数据,sub_cats数据无法添加
# "id": cat3.id,
# "name": cat3.name,
# })
# 查询所有的首页广告
context_categories = ContentCategory.objects.all()
contents = {}
for context_categorie in context_categories:
# contents是一个对象,根据category外键进行查询,status=True限制条件,order_by('sequence')按照sequence排序
contents[context_categorie.key] = Content.objects.filter(category__id=context_categorie.id,
status=True).order_by('sequence')
context = {
"categories": categories, # 商品频道整体拼接后的数据
"contents": contents, # 首页广告信息拼接结果
}
return render(request, "index.html", context=context)
渲染首页商品广告
- 轮播图广告
首页界面:templates/index.html
{# 轮播图的渲染 #}
<ul class="slide">
{# apps/contents/views.py文件传输的contents对象,通过.index_lbt进行访问属性数据 #}
{% for content in contents.index_lbt %}
<li><a href="{{ content.url }}"><img src="{{ content.image }}" alt="{{ content.title }}"></a></li>
{% endfor %}
</ul>
前端界面显示效果如下: