CentOS7设置静态IP、搭建单机版FastDFS图片服务器、使用FastDFS-Client客户端进行简单测试、实现图片上传、实现商品添加修改删除

CentOS7设置静态IP、搭建单机版FastDFS图片服务器、使用FastDFS-Client客户端进行简单测试、实现图片上传、实现商品添加修改删除

CentOS7设置静态IP而且还可以上网

192.168.173.148
但是用mac的终端远程连接到CentOS的时候,由于家里和学校IP不同,虚拟机的IP也会变化,每次还要重新查看,并且之后配置集群什么的也会出现问题,所以需要CentOS设置为固定IP。如果虚拟机使用桥接的方式,那么很多与ip相关的服务都会出现问题,所以我们希望使用nat模式,为了防止再次启动系统的时候网络IP发生变化,因此设置静态IP和DNS。

网上查阅了一番资料之后,发现这个问题在windows下很好解决,因为vmware workstation中有虚拟网络编辑器,可以直接把VMnet8,也就是NAT模式的DHCP关掉并设置子网IP。但vmware fusion不行,需要其他操作。

1.把网络配置nat模式

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.在mac终端到这个目录下

cd /Library/Preferences/VMware\ Fusion/vmnet8
cat nat.conf
在这里插入图片描述
这里的ip就是虚拟机的网关地址,netmask就是子网掩码

cat dhcpd.conf
在这里插入图片描述
range就是静态ip的范围,是虚拟机可分配的地址范围
netmask 255.255.255.0是子网掩码
option broadcast-address 192.168.173.255;是广播地址
option routers 192.168.173.2是网关

获取DNS(在mac系统偏好设置—>网络—>)

在这里插入图片描述
在这里插入图片描述
fe8O::262e:2ff:fe3e:72ad

3.设置虚拟机固定ip

cd /etc/sysconfig/network-scripts
vi ifcfg-ens33

进去后输入’i’,进入编辑模式,增加IPADDR,NETMASK,GATEWAY,HWADDR,DNS等信息,ONBOOT=no改为‘yes’。

dhcp
BOOTPROTO=“static”
ONBOOT=“yes”

IPADDR=192.168.173.148
GATEWAY=192.168.173.2
NETMASK=255.255.250.0
DNS1=192.168.3.1 注:DNS要和mac的DNS保持一致
DNS2=fe8O::262e:2ff:fe3e:72ad

#后面的这些可以选择性添加
PREFIX=24
IPV6_PEERDNS=yes
IPV6_PEERROUTES=yes
IPV6_PRIVACY=no

4.重启网卡

service network restart

ping baidu.com 试试

在这里插入图片描述

pin

接下来我们就可以通过SecureCRT等工具远程连接了,有一点请记住,如果你换了一个地方上网的话,可能会发现你的虚拟机有不通了,那是因为DNS地址发生了变化,此时只需要再次编辑ifcfg-enxxx文件,然后加上你现在网络的DNS地址即可

如:
DNS1=192.168.0.1
DNS2=114.114.114.114

在这里插入图片描述

又弄了一次终于设置成功了!
在这里插入图片描述

给Mac上的Fusion虚拟主机设置固定的IP地址2

因为最近需要安装k8s的本地测试环境,所以使用Mac上的Vmware Fusion安装了几台ubuntu系统的虚拟机,某次重启的时候发现ssh登录不上去了,打开虚拟机看了一下,发现是ip地址发生了变化,研究了半天,找到了解决的方法如下:

在Mac的Terminal上编辑下面这个文件:

sudo vi /Library/Preferences/VMware\ Fusion/vmnet8/dhcpd.conf

最后一行是下面的内容:

####### VMNET DHCP Configuration. End of “DO NOT MODIFY SECTION” #######

在这行的下面,添加虚拟机的ip信息如下:

host host1 {
hardware ethernet 00:0c:29:dd:a5:67;
fixed-address 192.168.32.131;
}

其中:

host1是在Vmware Fusion中看到的虚拟机列表里的名称;
00:0c:29:dd:a5:67是这台虚拟机的网卡MAC地址,进入虚拟机的terminal里执行ifconfig就可以找到

192.168.32.131是你要设置的固定ip的地址。

如果有多个虚拟主机,顺序填写就可以了。

设置好之后,执行下面的刷新操作:

sudo /Applications/VMware\ Fusion.app/Contents/Library/vmnet-cli --configure

然后,重新启动你的Mac,就可以生效了。

在这里插入图片描述

00:0C:29:8C:9F:AA

在这里插入图片描述

CentOS-7-x86_64-DVD-1810

host CentOS-7-x86_64-DVD-1810 {
hareware ethernet 00:0C:29:8C:9F:AA;
fixed-address 192.168.173.131
}

搭建单机版FastDFS图片服务器

一、背景描述
之前公司的图片服务器已经不能胜任了(直接使用tomcat),需要重新搭建图片服务器,这两天研究了下FastDFS,感觉挺好用记录下来以供日后参考。

二、FastDFS官方介绍
FastDFS是一款类Google FS的开源分布式文件系统,它用纯C语言实现,支持Linux、FreeBSD、AIX等UNIX系统。它只能通过 专有API对文件进行存取访问,不支持POSIX接口方式,不能mount使用。准确地讲,Google FS以及FastDFS、mogileFS、 HDFS、TFS等类Google FS都不是系统级的分布式文件系统,而是应用级的分布式文件存储服务。

FastDFS架构图
1)Tracker cluster中各个tracker server相互独立,不进行相互通信。
2)Storage cluster中各个storage组(Volume1,Volume2…)相互独立,不进行相互通信,也就是说各个组之间保存的数据是不相同的。但是各个组中的storage server之间是属于互相备份的关系,也就是说storage server之间保存相同的数据。
3)每个storage server会启动一个单独的线程主动向Tracker cluster中每个tracker server报告其状态信息,包括磁盘使用情况,文件同步情况及文件上传下载次数统计等信息。

文件上传和下载的时序图
1)Client通过Tracker server将文件上传到Storage server。
2)Tracker server向Client返回一台可用的Storage server的IP地址和端口号。
3)Client直接通过Tracker server返回的IP地址和端口与其中一台Storage server建立连接并进行文件上传。
4)上传完成,Storage server返回Client一个文件ID,文件上传结束。

1)Client通过Tracker server下载指定Storage组中某个Storage server上的某个文件(文件名包括Storage组名称)。
2)Tracker server向Client返回一台可用的Storage server的IP地址和端口号。
3)Client直接通过Tracker server返回的IP地址和端口与其中一台Storage server建立连接并进行文件下载。

1、运行环境及相关软件

CentOS 7
FastDFS_v5.05.tar.gz
nginx-1.7.8.tar.gz
fastdfs-nginx-module_v1.16.tar.gz
libfastcommonV1.0.7.tar.gz

解压后:
FastDFS
nginx-1.7.8
fastdfs-nginx-module
libfastcommon-1.0.7
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.安装libfastcommon

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.FastDFS主程序设置的目录为/usr/lib

把/usr/lib64/libfastcommon.so文件向/usr/lib/下复制一份
在这里插入图片描述
或者!!!先把上面/usr/lib下面复制的这个文件删除掉!再进行下列操作

创建软链接
FastDFS主程序设置的目录为/usr/local/lib/,所以我们需要创建/ usr/lib64/下的一些核心执行程序的软连接文件。如下所示。
在这里插入图片描述

4.安装FastDFS

./make.sh
在这里插入图片描述
./make.sh install
在这里插入图片描述

安装完后,服务脚本位置如下:cd /etc/init.d/ && ls | grep fdfs
在这里插入图片描述

配置文件位置如下:cd /etc/fdfs/
在这里插入图片描述
在这里插入图片描述

FastDFS一系列执行脚本如下,可以看到有上传文件脚本、下载文件脚本等等:cd /usr/bin/ && ls | grep fdfs

在这里插入图片描述

【另一个版本,还做了复制到/etc/fdfs
安装后在/usr/bin/目录下有以fdfs开头的文件都是编译出来的。
配置文件都放到/etc/fdfs文件夹
在这里插入图片描述
把FastDFS conf目录下的配置文件都复制到/etc/fdfs下
在这里插入图片描述

5.因为FastDFS服务脚本设置的bin目录为/usr/local/bin/下,但是实际我们安装在了/usr/bin/下面。所以我们需要修改FastDFS配置文件中的路径,也就是需要修改两个配置文件

使用命令vim /etc/init.d/fdfs_storaged进入编辑模式,然后直接输入":",光标会定位到最后一行,在":“后输入”%s+/usr/local/bin+/usr/bin",如下图所示。输入完之后回车,会提示修改了7处。为了确保所有的/usr/local/bin都被替换了,我们可以再打开文件确认一下。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

接着修改第二个配置文件,我们使用命令vim /etc/init.d/fdfs_trackerd进入编辑模式,接着按照上面那样输入":%s+/usr/local/bin+/usr/bin "并按回车,同样会提醒我们修改了7处。

6.配置跟踪器

1、进入到/etc/fdfs目录并且复制一份tracker.conf.sample并命名为tracker.conf,如下所示。【上述另外一个版本进行复制了,所以这里省略这一步】

2、配置tracker服务,修改/etc/fdfs/tracker.conf文件

此路径为储存文件的路径.自己设置,修改
在这里插入图片描述
在这里插入图片描述

3、我们在上图配置文件中配置的/fastdfs/tracker目前是不存在的,因此我们需要创建一下该目录

在这里插入图片描述

4、配置防火墙,放开tracker使用的端口22122,使用命令vim /etc/sysconfig/iptables进入编辑模式,添加一行内容-A INPUT -m state --state NEW -m tcp -p tcp --dport 22122 -j ACCEPT,如下图所示。

在这里插入图片描述

5、重启防火墙

[root@fastdfs fdfs]# service iptables restart
iptables:将链设置为政策 ACCEPT:filter [确定]
iptables:清除防火墙规则: [确定]
iptables:正在卸载模块: [确定]
iptables:应用防火墙规则: [确定]
[root@fastdfs fdfs]#

这里的四五两步在centos7中和以上有所不同

6、在启动tracker之前,/fastdfs/tracker目录下是没有任何文件的,如下所示

在这里插入图片描述
启动tracker,启动完之后,可以看到这个目录下多了两个目录data和logs。如下所示。

在这里插入图片描述

【另一个版本启动时说是找不到文件
启动tracker
在这里插入图片描述

7、设置开机自启动,在rc.local文件中添加/etc/init.d/fdfs_trackerd start,如下所示。

在这里插入图片描述
在这里插入图片描述

7.配置FastDFS存储

1、进入/etc/fdfs目录,复制一份storage.conf.sample文件并命名为storage.conf,如下所示。

这一步可以省略,因为在执行另一个版本时我已经进行了拷贝

2、修改storage.conf文件 ,我们使用命令vim /etc/fdfs/storage.conf进入编辑模式,对以下四项进行修改,192.168.156.13是我的虚拟机的IP,大家根据自己虚拟机的IP自行设置。

base_path=/fastdfs/storage
store_path0=/fastdfs/storage
tracker_server=192.168.156.13:22122
http.server_port=8888

路径为/usr/local/fastdfs/storage

3、创建存储目录,如下所示。

在这里插入图片描述

4、配置防火墙,允许外界访问storage的默认端口23000,如下所示。

[root@fastdfs fdfs]# vim /etc/sysconfig/iptables
# Firewall configuration written by system-config-firewall
# Manual customization of this file is not recommended.
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22122 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 23000 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
   添加完之后,重启防火墙,如下所示。
[root@fastdfs fdfs]# service iptables restart
iptables:将链设置为政策 ACCEPT:filter [确定]
iptables:清除防火墙规则: [确定]
iptables:正在卸载模块: [确定]
iptables:应用防火墙规则: [确定]
[root@fastdfs fdfs]#

5、在启动storage之前,/fastdbf/storage目录下是没有任何文件的

启动storage,启动后再看/fastdfs/storage目录,可以看到多了data和logs。
在这里插入图片描述

6、查看FastDFS tracker和storage 是否启动成功,当看到如下所示信息时说明都启动成功了。

在这里插入图片描述

7、我们进入到 /fastdfs/storage/data/目录下,可以看到两级共256*256个目录,每级都是从00到FF,如下只是列出了第一级的目录,点进去每个目录都还有00到FF共256个目录。

在这里插入图片描述

8、设置storage开机自启动,添加一行/etc/init.d/fdfs_storaged start,如下所示。

在这里插入图片描述

8.测试图片上传

1、进入到/etc/fdfs目录下并复制一份client.conf.sample并更名为client.conf,如下所示。

这步可以省略,因为另一个版本中进行了拷贝

2、使用命令vim /etc/fdfs/client.conf进入编辑模式并修改如下两项内容,如下所示。

base_path=/fastdfs/tracker
tracker_server=192.168.156.13:22122

3、我们找到命令的脚本位置,并且使用命令,进行文件的上传。

下面使用fdfs_upload_file脚本进行文件上传操作,如下所示。可以看到已经上传成功了,返回的是图片的保存位置:group1/M00/00/00/wKicDVjr_ayAE4VVAAHk-VzqZ6w020.jpg在这里插入图片描述
group1/M00/00/00/wKit1F1jS0uAUVZ-AAOLZ96tasM153.png

9.FastDFS与nginx相结合

注:FastDFS通过Tracker服务器,将文件放在Storage服务器存储,但是同组存储服务器之间需要进入文件复制,有同步延迟的问题。假设Tracker服务器将文件上传到了192.168.4.125,上传成功后文件ID已经返回给客户端。此时FastDFS存储集群机制会将这个文件同步到同组存储192.168.4.126,在文件还没有复制完成的情况下,客户端如果用这个文件ID在192.168.4.126上取文件,就会出现文件无法访问的错误。而fastdfs-nginx-module可以重定向文件连接到源服务器取文件,避免客户端由于复制延迟导致的文件无法访问错误。

1、先安装nginx,大家可以参考http://blog.csdn.net/u012453843/article/details/69396434这篇博客的第四步Nginx安装

一、Nginx概念
Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行。由俄罗斯的程序设计师Igor Sysoev所开发,供俄国大型的入口网站及搜索引擎Rambler(俄文:Рамблер)使用。其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。
二、负载均衡策略
1、使用硬件复杂均衡策略实现,如使用F5、Array等负载均衡器
2、使用软件进行负载均衡
2.1 使用阿里云服务器均衡负载SLB
2.2 使用Nginx+Keepalived
2.3 其它软件负载均衡如LVS(Linux Virtual Server)、haproxy等技术。
三、Nginx的优点
1、Nginx 可以在大多数 UnixLinux OS 上编译运行,并有 Windows 移植版。 Nginx 的1.4.0稳定版已经于2013年4月24日发布,一般情况下,对于新建站点,建议使用最新稳定版作为生产版本,已有站点的升级急迫性不高。Nginx 的源代码使用 2-clause BSD-like license。
2、Nginx 是一个很强大的高性能Web和反向代理服务器,它具有很多非常优越的特性:
在连接高并发的情况下,Nginx是Apache服务器不错的替代品:Nginx在美国是做虚拟主机生意的老板们经常选择的软件平台之一。能够支持高达 50,000 个并发连接数的响应,感谢Nginx为我们选择了 epoll and kqueue作为开发模型。
四、Nginx安装
如果大家是刚新建的虚拟机并且最小化安装的话,需要先配置静态IP并且要能上网,大家可以参考:http://blog.csdn.net/u012453843/article/details/52839105这篇博客进行学习,编辑文章用vi即可。配置好静态IP并且能上网之后需要先安装wget、vim和gcc。
yum install wget
yum install vim-enhanced
yum install make cmake gcc gcc-c++

1、下载nginx安装包
[root@nginx1 ~]# wget http://nginx.org/download/nginx-1.6.2.tar.gz
–2017-04-07 01:44:55-- http://nginx.org/download/nginx-1.6.2.tar.gz
正在解析主机 nginx.org… 206.251.255.63, 95.211.80.227, 2606:7100:1:69::3f, …
正在连接 nginx.org|206.251.255.63|:80… 已连接。
已发出 HTTP 请求,正在等待回应… 200 OK
长度:804164 (785K) [application/octet-stream]
正在保存至: “nginx-1.6.2.tar.gz”
在这里插入图片描述
2、安装依赖,其中pcre(perl compatible regular expressions)是一个pert库,包括perl兼容的正则表达式库。nginx的http模块使用pcre来解析正则表达式,所以需要在linux上安装pcre库。pcre-devel是使用pcre开发的一个二次库,nginx也需要此库。zlib库提供了很多种压缩的方式,nginx使用zlib对http包的内容进行gzip,所以需要在linux上安装zlib库。openssl是一个强大的安全套接字层密码库,囊括主要的密码算法、常用的秘钥和证书封装管理功能及SSL协议,并提供丰富的应用程序提供测试或其它目的的使用。nginx不仅支持http协议,还支持https(即在ssl协议上传输http),所以需要在linux安装openssl库。

yum install -y pcre pcre-devel
在这里插入图片描述
yum install -y zlib zlib-devel
在这里插入图片描述
yum install -y openssl openssl-devel
在这里插入图片描述
3、解压nginx-1.6.2.tar.gz到/usr/local/目录下
[root@nginx1 ~]# tar -zxvf nginx-1.6.2.tar.gz -C /usr/local/
在这里插入图片描述
4、进入到/usr/local目录下,可以看到我们解压后的nginx-1.6.2文件夹了,然后我们进行configure配置,命令:cd nginx-1.6.2 && ./configure --prefix=/usr/local/nginx。可以看出,这条命令是组合命令,先进入nginx-1.6.2目录然后在执行./configure命令。如下图所示。

5、编译安装
[root@nginx1 nginx-1.6.2]# make && make install

6、启动Nginx,启动完之后检查nginx是否已经正常启动,看到如下信息说明正常启动。
[root@nginx1 nginx-1.6.2]# /usr/local/nginx/sbin/nginx
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
突然想起来,tomcat服务器配置的就是80端口。

[root@nginx1 nginx-1.6.2]# ps -ef | grep nginx
root 3640 1 0 04:40 ? 00:00:00 nginx: master process /usr/local/nginx/sbin/nginx
nobody 3641 3640 0 04:40 ? 00:00:00 nginx: worker process
root 3643 1368 0 04:40 pts/0 00:00:00 grep nginx
[root@nginx1 nginx-1.6.2]#
如果要关闭nginx,我们可以使用如下命令:
[root@nginx1 nginx-1.6.2]# /usr/local/nginx/sbin/nginx -s stop
如果想要重新热启动nginx,则使用如下命令:
[root@nginx1 nginx-1.6.2]# /usr/local/nginx/sbin/nginx -s reload

配置nginx,如下所示:
在这里插入图片描述
说明:
a、"user root"是解决下载操作时报404的问题
b、8888端口号与/etc/fdfs/storage.conf中的http.server_port=8888相对应
c、storage对应有多个group的情况下,访问路径带group名称,例如:/group1/M00/00/00/**,对应nginx配置:
location ~/group[0-9]/ {
ngx_fastdfs_module;
}
在这里插入图片描述
然后再启动
在这里插入图片描述
在这里插入图片描述

7、nginx默认的端口是80,我们需要在防火墙配置中添加80端口:"-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT"从而可以让外界访问80端口,否则防火墙将禁止外界访问80端口。如下所示。
[root@nginx1 nginx-1.6.2]# vim /etc/sysconfig/iptables

#Firewall configuration written by system-config-firewall
#Manual customization of this file is not recommended.
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
添加完80端口之后,我们重启防火墙,如下所示。
[root@nginx1 nginx-1.6.2]# service iptables restart
iptables:将链设置为政策 ACCEPT:filter [确定]
iptables:清除防火墙规则: [确定]
iptables:正在卸载模块: [确定]
iptables:应用防火墙规则: [确定]

8、通过浏览器访问nginx欢迎页,我们在地址栏输入:http://192.168.156.11/(80端口不用输也可以)或http://192.168.156.11:80/,如下图所示。

2、安装fastdfs-nginxmodule_v1.16.tar.gz(fast与nginx相结合的模块安装包)

看到解压的fastdfs-nginx-module目录,然后进入到fastdfs-nginx-module/src/目录下,可以看到config文件。
在这里插入图片描述

3、修改该conf文件,我们把文件的第四行配置中的/usr/local/include都改为/usr/include,共两处。

在这里插入图片描述

4、fastdfs与nginx进行结合,由于我们刚才安装过nginx了,因此在/usr/local目录下已经生成了一个nginx目录了,如下图所示。

为了将nginx与fastdfs相结合,我们先把这个nginx目录删除掉,如下图所示,可以看到已经没有nginx目录了。
进入到nginx-1.6.2/目录下并执行配置和编译安装,如下所示。
在这里插入图片描述
[root@fastdfs local]# cd nginx-1.6.2/
[root@fastdfs nginx-1.6.2]# ./configure --add-module=/usr/local/fast/fastdfs-nginx-module/src/
[root@fastdfs nginx-1.6.2]# make && make install

复制fastdfs-nginx-module中的配置文件,到/etc/fdfs目录中,如下所示。
[root@fastdfs fdfs]# cd /usr/local/fast/fastdfs-nginx-module/src/
[root@fastdfs src]# cp /usr/local/fast/fastdfs-nginx-module/src/mod_fastdfs.conf /etc/fdfs/
在这里插入图片描述
我们到 /etc/fdfs/ 目录下,修改我们刚copy过来的mod_fastdfs.conf 文件,需要修改的项如下,其中第一项是超时时长,第三项是允许外界通过http方式访问资源。
connect_timeout=10
tracker_server=192.168.156.13:22122
url_have_group_name = true
store_path0=/fastdfs/storage

在这里插入图片描述

复制FastDFS里的2个文件,到/etc/fdfs目录中,如下所示。

[root@fastdfs fdfs]# cd /usr/local/fast/FastDFS/conf/
[root@fastdfs conf]# ll
总用量 84
-rw-r--r--. 1 8980 users 23981 12月 2 2014 anti-steal.jpg
-rw-r--r--. 1 8980 users 1461 12月 2 2014 client.conf
-rw-r--r--. 1 8980 users 858 12月 2 2014 http.conf
-rw-r--r--. 1 8980 users 31172 12月 2 2014 mime.types
-rw-r--r--. 1 8980 users 7829 12月 2 2014 storage.conf
-rw-r--r--. 1 8980 users 105 12月 2 2014 storage_ids.conf
-rw-r--r--. 1 8980 users 7102 12月 2 2014 tracker.conf
[root@fastdfs conf]# cp http.conf mime.types /etc/fdfs/
[root@fastdfs conf]#

在这里插入图片描述

  创建一个软连接,在/fastdfs/storage文件存储目录下创建软连接,将其链接到实际存放数据 的目录,如下所示。
  [root@fastdfs conf]# ln -s /fastdfs/storage/data/ /fastdfs/storage/data/M00

在这里插入图片描述
进入到/usr/local/nginx/conf/目录下,修改nginx.conf文件,如下图所示。
在这里插入图片描述
修改的内容如下图示
在这里插入图片描述
listen 8888;
location ~/group([0-9])/M00 {
ngx_fastdfs_module;
}

在这里插入图片描述

设置nginx开机自启动,这样下次重启设备之后,tracker、storage、nginx都自动启动了,直接就可以使用服务,如下所示。

 [root@fastdfs ~]# vim /etc/rc.d/rc.local
#!/bin/sh
#
# This script will be executed *after* all the other init scripts.
# You can put your own initialization stuff in here if you don't
# want to do the full Sys V style init stuff.
touch /var/lock/subsys/local
/etc/init.d/fdfs_trackerd start
/etc/init.d/fdfs_storaged start
/usr/local/nginx/sbin/nginx

在这里插入图片描述
启动nginx,如下所示。
在这里插入图片描述
在这里插入图片描述
这是因为之前安装单独的nginx的时候设置了8888端口号
杀掉这个进程,重新启动
在这里插入图片描述

5、在通过8888端口访问图片之前先配置下防火墙,允许外界访问8888端口,添加的一行是-A INPUT -m state --state NEW -m tcp -p tcp --dport 8888 -j ACCEPT,如下图所示。

在这里插入图片描述
配置完防火墙后重启防火墙

[root@fastdfs conf]# service iptables restart
iptables:将链设置为政策 ACCEPT:filter [确定]
iptables:清除防火墙规则: [确定]
iptables:正在卸载模块: [确定]
iptables:应用防火墙规则: [确定]
[root@fastdfs conf]#

6、现在我们便可以通过http的方式访问我们刚才上传的图片了(我们刚才上传图片返回的地址是group1/M00/00/00/wKicDVjr_ayAE4VVAAHk-VzqZ6w020.jpg),如下图所示。

group1/M00/00/00/wKit1F1jS0uAUVZ-AAOLZ96tasM153.png
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
http://192.168.173.148:8888/group1/M00/00/00/wKitlF1jS0uAUVZ-AAOLZ96tasM153.png
至此,一个单机版的FastDFS便搭建完毕了!!

使用FastDFS-Client客户端进行简单测试

第一件事:由于中央仓库并没有fastdfs-client的包,因此需要我们自己整,大家可以参考:http://blog.csdn.net/u012453843/article/details/70135826这篇博客学习怎样使用Eclipse从Github官网下载fastdfs-client源码并转化为maven工程以及如何打包到本地maven仓库。

FastDFS Client目前在中央仓库是没有坐标的
1,首先去我的github上下载下来fastdfs的压缩包
直接下载地址:https://github.com/wangwei216/fastdfs-client-java-master
然后直接解压出来

2,使用cmd命令行 使用cd命令走到到你自己下载到的目录下,然后使用mvn clean install 直接打包到本地的maven仓库(注意:本机必须安装Maven,并配置Maven环境变量,并且盘符要切换到fastdfs-client-java解压的目录下)
在这里插入图片描述
当上述操作完成时,我们会看到Maven本地仓库有了fastdfs-client-java-1.27-SNAPSHOT.jar。
此时我们就可以在我们需要的项目中添加对它的依赖,就可以使用fastdfs-client-java此开发工具包。
FastDFS有两个角色:跟踪服务器和存储服务器。跟踪服务器负责文件访问的调度和负载均衡,存储文件服务器负责文件的存储、文件同步、提供文件访问接口,管理元数据(文件相关属性,键值对方式),例如:width = 1024,键为“width”,值为“1024”。
在这里插入图片描述
在这里插入图片描述

3,然后添加到pom.xml文件中就不会报错了


org.csource
fastdfs-client-java
1.27-SNAPSHOT

写的不错比较细致,但是有一点需要注意一下,在生成JAR包的时候,要看下fastdfs-client-java项目中pom文件的版本,我使用的时候,并不是1.25而是1.27-SNAPSHOT,这个很重要

总结:
这种方式不仅适用于fastdfs的打包到本地的方法,而且还可以运用到所有中央仓库中没有jar包的方法,只要是中央仓库中没有的,都可以适用这中方式
例如dubbo的jar包也可以适用这种,先把需要的jar包下载下来,然后使用cmd命令到本地文件,然后安装到本地maven仓库

第二件事:在taotao-manager-web工程的resources目录下新建一个resource文件夹并在它下面创建client.conf文件,client.conf文件中输入tracker所在的设备的IP及端口,由于我的tracker是在192.168.156.13上,因此我这里写的是"tracker_server=192.168.156.13:22122",如下图所示。
在这里插入图片描述
下面我们新建一个测试类来进行测试,我们在src/test/java目录下新建一个包com.taotao.fastdfs,在该包下新建一个测试类TestFastDFS.java,如下图所示。
在这里插入图片描述
为方便复制,现把测试类代码粘贴如下:

package com.taotao.fastdfs;

import org.csource.common.NameValuePair;
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.StorageClient;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;
import org.junit.Test;

public class TestFastDFS {
   
	@Test
	public void testUploadFile() throws Exception{
		//1.向工程添加jar包
		//2.创建一个配置文件,配置tracker服务器地址
		//3.加载配置文件
		ClientGlobal.init("E:/workspace/taotao-manager-web/src/main/resources/resource/client.conf");
		//4.创建一个TrackerClient对象
		TrackerClient trackerClient = new TrackerClient();
		//5.使用TrackerClient对象获得trackerserver对象。
		TrackerServer trackerServer = trackerClient.getConnection();
		//6.创建一个StorageServer的引用,我们用null就可以
		StorageServer storageServer = null;
		//7.创建一个StorageClient对象。trackerserver、StorageServer两个参数
		StorageClient storageClient = new StorageClient(trackerServer, storageServer);
		//8.使用StorageClient对象上传文件
		NameValuePair[] metaList = new NameValuePair[3];
		metaList[0] = new NameValuePair("fileName", "2");
		metaList[1] = new NameValuePair("createTime", "2017-04-13 16:01:00");
		metaList[2] = new NameValuePair("createUser", "zhangsan");
		String[] upload_files = storageClient.upload_file("E:/images/2.jpg", "jpg", metaList);
		for(String file : upload_files){
			System.out.println(file);
		}
	}
}

上面的代码有三点需要注意:

第一、taotao-manager-web工程还未添加junit的依赖,因此需要在pom.xml文件中添加junit的依赖,如下所示,这里之所以不用写版本是因为在taotao-parent工程中已经统一定义好版本了。

junit junit

第二、NameValuePair是给图片添加附加信息的,包括图片原文件名、大小、作者等等,导包时会发现有多个选项,我们选择"import org.csource.common.NameValuePair"

第三、我们在复制粘贴win10系统的本地文件绝对路径(E:/images/2.jpg)时,Eclipse是识别不了的,运行会报如下错误。解决方法是手动输入,而且要注意把包括双引号在内的这个路径串删除(“E:/images/2.jpg”),然后手动输入一遍。

注意上面三点后,我们再执行这个测试方法便成功了,如下图所示。回显信息的第一行是该图片被保存到哪个组了,由于我们现在只是用的单机FastDFS服务器,因此现在都是group1。第二行是存放的具体位置。
在这里插入图片描述
在这里插入图片描述
group1
M00/00/00/wKitlF1lD_OAci8OAAHApE3lJzA14.jpeg

带有附加信息的图片上传后,会生成一个以"-m"结尾的文件,如下图所示。这个文件中保存了我们附加的图片信息。
在这里插入图片描述
在这里插入图片描述

既然上传上去了,现在我们试着用http的方式来访问下该图片,我们需要把group1和M00/00/00/wKicDVjvjkqAISg-AAGDL8Ay0xY563.jpg拼接到一块来访问。端口8888是我在Nginx配置的对外暴露的访问接口。这样我们便可以查看到我们刚才上传的照片了,如下图所示。
在这里插入图片描述
在这里插入图片描述

经过上面的操作,说明我们搭建的图片服务器没问题,但有个问题是,上传操作步骤繁琐,因此我们迫切需要对其进行封装,现在我把封装好的类FastDFSClient.java文件的内容粘贴如下。

构造方法中的conf = conf.replace(“classpath:”, this.getClass().getResource("/").getPath());这句话的意思是,如果用户传入的文件路径是相对路径(相对路径 以resources目录为根目录,比如用户传入的文件路径是"classpath:applications.properties",那么需要转为绝对路径,因此需要把"classpath:"给替换掉,改为E:/workspace/taotao-manager-web/src/main/resources)。

封装类中使用的Storage客户端是StorageClient1而不是StorageClient,这个客户端的好处是能够帮我们自动把文件所在的组以及存放位置拼接到一块。

package cn.itcast.fastdfs.cliennt;

import org.csource.common.NameValuePair;
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.StorageClient1;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;

public class FastDFSClient {

	private TrackerClient trackerClient = null;
	private TrackerServer trackerServer = null;
	private StorageServer storageServer = null;
	private StorageClient1 storageClient = null;
	
	public FastDFSClient(String conf) throws Exception {
		if (conf.contains("classpath:")) {
			conf = conf.replace("classpath:", this.getClass().getResource("/").getPath());
		}
		ClientGlobal.init(conf);
		trackerClient = new TrackerClient();
		trackerServer = trackerClient.getConnection();
		storageServer = null;
		storageClient = new StorageClient1(trackerServer, storageServer);
	}
	
	/**
	 * 上传文件方法
	 * <p>Title: uploadFile</p>
	 * <p>Description: </p>
	 * @param fileName 文件全路径
	 * @param extName 文件扩展名,不包含(.)
	 * @param metas 文件扩展信息
	 * @return
	 * @throws Exception
	 */
	public String uploadFile(String fileName, String extName, NameValuePair[] metas) throws Exception {
		String result = storageClient.upload_file1(fileName, extName, metas);
		return result;
	}
	
	public String uploadFile(String fileName) throws Exception {
		return uploadFile(fileName, null, null);
	}
	
	public String uploadFile(String fileName, String extName) throws Exception {
		return uploadFile(fileName, extName, null);
	}
	
	/**
	 * 上传文件方法
	 * <p>Title: uploadFile</p>
	 * <p>Description: </p>
	 * @param fileContent 文件的内容,字节数组
	 * @param extName 文件扩展名
	 * @param metas 文件扩展信息
	 * @return
	 * @throws Exception
	 */
	public String uploadFile(byte[] fileContent, String extName, NameValuePair[] metas) throws Exception {
		
		String result = storageClient.upload_file1(fileContent, extName, metas);
		return result;
	}
	
	public String uploadFile(byte[] fileContent) throws Exception {
		return uploadFile(fileContent, null, null);
	}
	
	public String uploadFile(byte[] fileContent, String extName) throws Exception {
		return uploadFile(fileContent, extName, null);
	}

我们新建一个工具包com.taotao.utils,然后把我们的封装类FastDFSClient.java放到下面。我们来测测这个工具类是否好使,我们再新建个测试方法testFastDFSClient,如下图所示。
在这里插入图片描述
代码如下

@Test
	public void testFastDFSClient() throws Exception{
        FastDFSClient fastDFSClient = new FastDFSClient("E:/workspace/taotao-manager-web/src/main/resources/resource/client.conf");
        String imgPath = fastDFSClient.uploadFile("E:/images/2.jpg");
        System.out.println(imgPath);
	}

我们运行testFastDFSClient方法,返回的结果如下图所示。
在这里插入图片描述
在这里插入图片描述
我们来访问这个图片,如下图所示。
在这里插入图片描述
在这里插入图片描述

Cannot resolve symbol ‘Test’ 解决办法 Intellij Idea 的maven工程在sources目录中使用 @Test

Cannot resolve symbol ‘Test’ 解决办法

问题:
intellij的maven工程,在sources目录中的一个java文件中,使用junit的@Test 注释类,但是intellij提示找不到类 cannot resolve symbol ‘Test’。而查看自己的pom文件,已经引入了junit包,该注释类在maven的依赖包中是存在的。

方法一:
将pom文件中 junit的引用设置scope为compile

junit
junit
4.10
compile

这里还有个问题,一般jar包的默认作用域都是compile。难道junit这个包的默认作用域是test。

原理:
在使用idea中遇到在maven项目里能引入类提示 cannot resolve symbol XXX,但项目组已经存在jar包,就是无法引用,清理项目缓存依然不管用。后来发现idea中libray是可以设置范围的,Project structure->Modules->Dependencies 在此可对jar包设置范围,共有如下值 Compile ,Test, Runtime ,Provided , 翻译如下:

Scope Use this drop-down to affect the classpath for the various build phases.
Compile: This is the default option. If it is selected, the dependency is resolved and is available in classpath during the compilation and run phases.
Test: Select this option, if this dependency is only required for tests, and should not be available in normal application use. If this scope is selected, the dependency is resolved and is available in classpath during the test compilation and run phases.
Runtime: This scope indicates that the dependency is only required when running the application, and should not be available in classpath during the compilation.
Provided: If this option is selected, the dependency is resolved and is available in classpath during the compilation, but is not included in classpath at runtime. This dependency scope is useful, when you have some container that provides the dependency at runtime.

范围 使用此下拉菜单来影响各个构建阶段的类路径。
编译:这是默认选项。如果选中,依赖关系将在编译和运行阶段解析并在类路径中可用。
测试:选择此选项,如果此依赖关系仅用于测试,并且在正常的应用程序使用中不可用。如果选择了此作用域,则在测试编译和运行阶段期间,将解析依赖关系,并在classpath中可用。
运行时:这个范围表示只在运行应用程序时需要依赖关系,在编译过程中不应该在classpath中使用依赖关系。
提供的:如果选择了这个选项,依赖被解析并在编译期间在类路径中可用,但在运行时不包含在类路径中。这个依赖范围是有用的,当你有一些容器在运行时提供依赖。

将有问题的jar包改为Compile 问题解决!

图片上传功能

上节课我们一起学习了搭建一个单机版的FastDFS图片服务器以及使用FastDFS-Client进行简单的文件上传操作测试,这节我们一起学习项目中添加商品时上传图片的问题,目前上传图片还没有实现,如下图所示。
在这里插入图片描述

我们看下list-add.jsp页面,可以看到上传图片触发方法picFileUpload是通过class来处理的,在标签的下方是一个隐藏域,是用来接收上传到图片服务器的回显地址的,当我们提交表单的时候,可以把这些图片地址保存到数据库中。
在这里插入图片描述

流程是这样的,页面加载完之后,会自动调用TAOTAO.init进行初始化,如下图所示。
在这里插入图片描述
TAOTAO在common.js中定义,我们来看下common.js,可以看到TAOTAO=TT都是在这里定义的,在init方法中this.initPicUpload(data);用来初始化上传组件。

初始化上传组件中就有我们在jsp页面中定义的类picFileUpload,由于上传操作可能不只一个地方调用,因此$(".picFileUpload").each(function(i,e){来对所有调用上传的页面的组件进行初始化。

如果已经上传过图片,现在处于编辑状态的话,那么就使用_ele.siblings(".pics").find(“ul”).append来加载原来已经添加过的图片。

点击上传图片按钮后,就会加载富文本编辑的上传图片界面,富文本编辑器的参数是在上面var TT=TAOTAO={的下面,指定了上传文件参数的名称,请求的url是/pic/upload,上传类型是image、flash、media、file四种。
在这里插入图片描述

下面我们来实现图片上传功能

第一步:导包
上传图片需要依赖commons-io和commons-fileupload开发包,我们需要在taotao-manger-web工程的Maven依赖中查一下是否有这两个包,目前是有commons-io-1.3.2.jar这个包的(它是在taotao-manager-common中依赖的,而taotao-manager-web依赖了taotao-manager-common,因此它也有这个包了),但目前没有commons-fileupload,因此我们需要在taotao-manager-web的pom.xml文件中添加对commons-fileupload的依赖。
在这里插入图片描述

添加的依赖如下,由于在taotao-parent当中统一定义了版本号,因此这里不用指定版本号。

<dependency>
		<groupId>commons-fileupload</groupId>
		<artifactId>commons-fileupload</artifactId>
	</dependency>

第二步:配置文件上传解析器
我们需要在taotao-manager-web工程的springmvc.xml文件当中配置一下文件上传解析器。如下所示。

<!-- 配置文件上传解析器 -->
	<bean id="multipartResolver"
		class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<!-- 设定默认编码 -->
		<property name="defaultEncoding" value="UTF-8"></property>
		<!-- 设定文件上传的最大值5MB,5*1024*1024 -->
		<property name="maxUploadSize" value="5242880"></property>
	</bean>

第三步:配置访问图片前缀
我们在访问图片时是以http的方式访问的,例如http://192.168.156.13:8888/group1/M00/00/00/wKicDVjxPn2AOBiGAAHk-VzqZ6w952.jpg,从上节课我们知道图片服务器返回的图片路径是group1/M00/00/00/wKicDVjxPn2AOBiGAAHk-VzqZ6w952.jpg,也就是说没有前面那部分路径,我们不能在代码中写死前缀,因为IP及端口号都有可能更改,因此最好是放到配置文件当中,我们在resource目录下新建一个resource.properties文件,配置文件中输入IMAGE_SERVER_URL=http://192.168.156.13:8888/,这里输入端口8888是因为我在图片服务器的nginx当中配置的访问端口是8888,如果端口是默认的80端口的话,可以直接输入IMAGE_SERVER_URL=http://192.168.156.13/即可,下图所示。
在这里插入图片描述

第四步:加载配置文件
我们在第三步新建了resource.properties,在spring中我们需要加载该配置文件,因此我们在springmvc.xml中加入<context:property-placeholder location=“classpath:resource/resource.properties”/>,如下图所示。
在这里插入图片描述

当前springmvc.xml文件的完整内容如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
        http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd">
       
    <!-- 加载配置文件 -->
    <context:property-placeholder location="classpath:resource/resource.properties"/>    
	<!-- 配置注解驱动 -->
	<mvc:annotation-driven />
	<!-- 视图解析器 -->
	<bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/jsp/" />
		<property name="suffix" value=".jsp" />
	</bean>
	<!-- 配置包扫描器,扫描@Controller注解的类 -->
	<context:component-scan base-package="com.taotao.controller"/>
	
	<!-- 配置资源映射 -->
	<mvc:resources location="/css/" mapping="/css/**"></mvc:resources>
	<mvc:resources location="/js/" mapping="/js/**"></mvc:resources>
	
	<!-- 多媒体解析器 -->
	<!-- 配置文件上传解析器 -->
	<bean id="multipartResolver"
		class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<!-- 设定默认编码 -->
		<property name="defaultEncoding" value="UTF-8"></property>
		<!-- 设定文件上传的最大值5MB,5*1024*1024 -->
		<property name="maxUploadSize" value="5242880"></property>
	</bean>
	
	<!-- 引用dubbo服务 -->
	<dubbo:application name="taotao-manager-web"/>
	<dubbo:registry protocol="zookeeper" address="192.168.156.14:2181"/>	
	<dubbo:reference interface="com.taotao.service.ItemService" id="itemService" />
	<dubbo:reference interface="com.taotao.service.ItemCatService" id="itemCatService" />
</beans>

第五步:创建Controller
我们需要新建一个PictureController类来处理上传操作,如下图所示。
在这里插入图片描述
上图中@Value("${IMAGE_SERVER_URL}")是为了注入我们在配置文件resource.properties中配置的图片访问前缀。@RequestMapping("/pic/upload")指定上传文件请求的url,与下图指定url一样,上图的public Map uploadFile(MultipartFile uploadFile) 参数"uploadFile"与下图的上传文件的方法参数名称是要求一样的。
在这里插入图片描述

那么方法uploadFile应该返回什么样的格式呢,我们可以从kindeditor官网http://kindeditor.net/docs/upload.html查看一下,如下图所示,可以看到返回值格式是json串,那么我们便有三种实现方式。

第一种是直接返回Map格式的数据,json和Map数据都是key和value的形式,因此返回Map是没问题的。
第二种是创建一个POJO类,该类有三个属性,分别是error、url、message,然后将该类转换为json之后返回。
第三种是将Map转变为json字符串返回。这里我们暂且使用第一种方式。

在这里插入图片描述

现把Controller类代码粘贴如下

package com.taotao.controller;
 
import java.util.HashMap;
import java.util.Map;
 
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
 
import com.taotao.utils.FastDFSClient;
 
@Controller
public class PictureController {
 
	@Value("${IMAGE_SERVER_URL}")
	private String IMAGE_SERVER_URL;
	
	@RequestMapping("/pic/upload")
	@ResponseBody
	public Map uploadFile(MultipartFile uploadFile) {
		Map result  = new HashMap<>();
		try {
			//1.接收上传的文件
			//2.获取扩展名
			String orignalName = uploadFile.getOriginalFilename();
			String extName = orignalName.substring(orignalName.lastIndexOf(".")+1);
			//3.上传到图片服务器
			FastDFSClient fastDFSClient = new FastDFSClient("classpath:resource/client.conf");
			String url = fastDFSClient.uploadFile(uploadFile.getBytes(), extName);
			url = IMAGE_SERVER_URL + url;
			result.put("error", 0);
			result.put("url", url);
			return result;
		}  catch (Exception e) {
			e.printStackTrace();
			result.put("error", 0);
			result.put("message", "上传图片失败!");
			return result;
		}
	}
}

这样我们便配置完了,现在我们试试图片上传功能,如下图所示,发现点击"开始上传"之后图片正常回显了。我们点击"全部插入"按钮。
在这里插入图片描述

点击"全部插入"按钮之后可以看到在"上传图片"按钮的下方有我们刚才上传的三张图片,我们是可以点击任何一张图片在浏览器中浏览的,比如我们点击第三张图片。

在这里插入图片描述

在浏览器中访问的效果如下图所示。这样我们的图片上传功能便实现了。
在这里插入图片描述

解决KindEditor上传图片不兼容的问题

在上节课我们实现了图片上传功能,但是有个问题,那就是对浏览器兼容性不够,因为Map类型的返回值在火狐浏览器无法识别,Controller代码如下图所示。为了解决这个兼容问题,我们需要修改下返回值类型,将Map类型变为String类型(也就是json串)。我们至少可以使用以下两种方案来解决。

第一种:使用fastjson来实现转换
在taoao-common工程的maven依赖中添加对fastjson的依赖,之所以把这个放到taotao-common工程下是因为这样的操作可能在多个工程都用得着,因此放到taotao-common工程更合适。我们可以从中央仓库复制fastjson的依赖坐标,如下所示(版本有很多,我只是随便选了个)。

com.alibaba fastjson 1.2.25

由于我们是在taotao-parent工程统一定义maven依赖版本,因此我们便在taotao-parent的pom.xml文件中添加如下图所示的内容。注意,这里只定义版本,不实际依赖。

在这里插入图片描述

下面我在taotao-common工程添加对fastjson的实际依赖,如下图所示。
在这里插入图片描述
注意:由于我们刚才修改了taotao-parent和taotao-common工程,因此需要对这两个工程打下包,依次在taotao-parent和taotao-common工程上右键-------->Run As-------->Maven install进行打包。

我们的taotao-manager-web工程是依赖taotao-common工程的,因此自动就会把fastjson给加到taotao-manager-web工程的maven依赖当中了。我们便可以直接使用fastjson的功能了。如下图所示。
在这里插入图片描述
我们重启taotao-manager-web,启动之后,我们再去尝试上传图片,这次发现图片回显成功,如下图所示。

第二种:使用已有的jackson来处理
我们知道SpringMVC的@ResponseBody将对象转变为json传到前台展示,帮我们做转换操作的便是便是jackson。那么我们怎样使用jackson来处理转换操作呢?我们可以封装一个JSonUtil类,还是放到taotao-common工程下,如下图所示。
在这里插入图片描述

KindEditor的图片上传插件,对浏览器兼容性不好。
使用@ResponseBody注解返回java对象,
Content-Type:application/json;charset=UTF-8
返回字符串时:
Content-Type:text/plan;charset=UTF-8

/Users/zhangcongrong/IdeaProjects/taotao/taotao-manager-web/src/main/java/com/taotao/utils/JsonUtils.java

package com.taotao.utils;
 
import java.util.List;
 
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
 
/**
 * json转换对象或对象转json
 */
public class JsonUtils {
 
    // 定义jackson对象
    private static final ObjectMapper MAPPER = new ObjectMapper();
 
    /**
     * 将对象转换成json字符串。
     * <p>Title: pojoToJson</p>
     * <p>Description: </p>
     * @param data
     * @return
     */
    public static String objectToJson(Object data) {
    	try {
			String string = MAPPER.writeValueAsString(data);
			return string;
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
    	return null;
    }
    
    /**
     * 将json结果集转化为对象
     * 
     * @param jsonData json数据
     * @param clazz 对象中的object类型
     * @return
     */
    public static <T> T jsonToPojo(String jsonData, Class<T> beanType) {
        try {
            T t = MAPPER.readValue(jsonData, beanType);
            return t;
        } catch (Exception e) {
        	e.printStackTrace();
        }
        return null;
    }
    
    /**
     * 将json数据转换成pojo对象list
     * <p>Title: jsonToList</p>
     * <p>Description: </p>
     * @param jsonData
     * @param beanType
     * @return
     */
    public static <T>List<T> jsonToList(String jsonData, Class<T> beanType) {
    	JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);
    	try {
    		List<T> list = MAPPER.readValue(jsonData, javaType);
    		return list;
		} catch (Exception e) {
			e.printStackTrace();
		}
    	
    	return null;
    }
    
}

注意:由于taotao-common工程添加了一个工具类,因此需要重新打下包,在工程上右键--------->Run As-------->Maven install。
下面使用JsonUtil来做json转换,如下图所示。在这里插入图片描述
在这里插入图片描述
下面我们重启taotao-manager-web工程,重启后,再尝试上传图片,如下图所示,发现成功了。两种方法大家随意选择。

解决KindEditor上传图片 不显示上传按钮问题

在学习淘淘商城第三天的时候,批量上传图片到服务器,正确的姿势是这样的:
在这里插入图片描述
但是添加图片那个按钮没有出现,然后百度说是缺少flash ,所以把flash更新了,然而并没有解决问题。

最终解决方案是,打开淘淘商城网页,然后用浏览器调试功能,显示缺少一个images包
在这里插入图片描述
然后去把images包添加进工程,ok解决问题。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
原因:
在这里插入图片描述
是花括号不是圆括号

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

但是在查阅资料时发现了以下问题:

spring和springmvc的父子容器间访问properties文件内容

在这里插入图片描述
spring相当于父容器,springmvc相当于子容器。
1、在同一个容器中,可以任意访问对象
2、子容器可以访问父容器的对象 比如:control中可以注入到service中
3、父容器不能访问子容器的对象

由于springmvc是spring的子容器,springmvc可以访问父容器的对象,但不能访问父容器的属性。
所以在controller层(子容器加载),要使用@Value注解访问properties文件(父容器加载)中的属性是不能访问的
要实现访问父容器的属性,怎么办呢
我们思考,我们可以在service层(父容器加载)中使用@Value注解读取properties文件属性,并把属性权限设置为public,
这样通过注入@Resource方式把service注入,然后通过service来访问properties文件属性

原因分析:
@Value作用:为当前属性注入值。
注入值:在Spring容器初始化(所有的bean)之后,在当前的所在ApplicationContext容器中获取配置文件中的值,然后注入。

我们的Controller所在的容器是哪个? SpringMVC的容器
我们把配置文件注入了哪个容器?Spring容器
不在同一个容器中,这就是问题所在。

Spring的父子容器
Spring容器 – 父容器
SpringMVC容器 – 子容器
父子容器的关系:
1、子容器能够访问父容器的资源(bean)
a)示例:Controller可以注入Service
2、父容器不能访问子容器的资源(bean)

解决父子容器资源问题
子容器可以访问父容器的对象,但是@Value并不会主动去父容器中查找配置文件属性值。这是现在的问题。

我们的解决方案是:
在父容器中注册一个Bean,然后把配置文件的值注入到这个Bean中,然后子容器可以访问到这个bean,自然可以访问到属性

因为Service层的对象是有Spring容器创建,因此我们定义一个Service

PropertiesService
在这里插入图片描述

在controller中注入Service
在这里插入图片描述
在这里插入图片描述
测试结果
在这里插入图片描述

添加商品的实现

上节课我们一起学习了富文本编辑器的使用,这节课我们一起学习下商品添加的实现。

在item-add.jsp当中,当点击提交按钮后,会触发submitForm方法,如下图所示。
在这里插入图片描述

在提交表单前需要校验输入的内容是否合法,如下图所示。
在这里插入图片描述

下面我们看下数据库中商品表的建表信息,可以看到价格定义的字段类型是long型,单位为分,之所以这样做是为了避免使用小数点,因为小数点使用起来比较麻烦。因此存到数据库中的价格都是价格(以元为单位)乘以100的(变为分)。
在这里插入图片描述

我们发现在商品表当中没有商品描述这个字段,其实商品描述是专门用一张表来存放了,如下图所示。可以看到商品描述与商品ID是一一对应的,之所以把商品描述单独放到一张表当中是因为它是个大文本字段,存储的信息量非常大,对于不需要商品描述的查询情况来说连带这个字段查询会影响查询效率,因此单独存放。

在这里插入图片描述

我们看看表单中是如何表示的,如下图所示,可以看到有两个组件,第一个用来展示的价格,即单位为元的价格(这更符合用户的习惯),第二个是个隐藏域,专门用来存放以分为单位而的价格(即将以元为单位的价格乘以100)。表单提交便会提交name="price"的价格并保存到数据库。
在这里插入图片描述

提交表单会请求url为"/item/save",第二个参数$("#itemAddForm").serialize()是用来将表单的数据序列化为key-value形式的字符串。如果提交成功应该返回状态码为200。
在这里插入图片描述

由于每个操作都需要有状态码来表示操作成功与否以及相关信息,因此我们定义一个TaotaoResult来专门处理,该类定义三个属性,分别是状态、消息及数据。由于这个类会被多个工程所使用,因此放到taotao-common的pojo目录下。
在这里插入图片描述
TaotaoResult类的全部代码如下,里面最常用的便是ok方法和build方法。

package com.taotao.common.pojo;
 
import java.io.Serializable;
import java.util.List;
 
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
 
/**
 * 淘淘商城自定义响应结构
 */
public class TaotaoResult implements Serializable{
 
    // 定义jackson对象
    private static final ObjectMapper MAPPER = new ObjectMapper();
 
    // 响应业务状态
    private Integer status;
 
    // 响应消息
    private String msg;
 
    // 响应中的数据
    private Object data;
 
    public static TaotaoResult build(Integer status, String msg, Object data) {
        return new TaotaoResult(status, msg, data);
    }
 
    public static TaotaoResult ok(Object data) {
        return new TaotaoResult(data);
    }
 
    public static TaotaoResult ok() {
        return new TaotaoResult(null);
    }
 
    public TaotaoResult() {
 
    }
 
    public static TaotaoResult build(Integer status, String msg) {
        return new TaotaoResult(status, msg, null);
    }
 
    public TaotaoResult(Integer status, String msg, Object data) {
        this.status = status;
        this.msg = msg;
        this.data = data;
    }
 
    public TaotaoResult(Object data) {
        this.status = 200;
        this.msg = "OK";
        this.data = data;
    }
 
//    public Boolean isOK() {
//        return this.status == 200;
//    }
 
    public Integer getStatus() {
        return status;
    }
 
    public void setStatus(Integer status) {
        this.status = status;
    }
 
    public String getMsg() {
        return msg;
    }
 
    public void setMsg(String msg) {
        this.msg = msg;
    }
 
    public Object getData() {
        return data;
    }
 
    public void setData(Object data) {
        this.data = data;
    }
 
    /**
     * 将json结果集转化为TaotaoResult对象
     * 
     * @param jsonData json数据
     * @param clazz TaotaoResult中的object类型
     * @return
     */
    public static TaotaoResult formatToPojo(String jsonData, Class<?> clazz) {
        try {
            if (clazz == null) {
                return MAPPER.readValue(jsonData, TaotaoResult.class);
            }
            JsonNode jsonNode = MAPPER.readTree(jsonData);
            JsonNode data = jsonNode.get("data");
            Object obj = null;
            if (clazz != null) {
                if (data.isObject()) {
                    obj = MAPPER.readValue(data.traverse(), clazz);
                } else if (data.isTextual()) {
                    obj = MAPPER.readValue(data.asText(), clazz);
                }
            }
            return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj);
        } catch (Exception e) {
            return null;
        }
    }
 
    /**
     * 将json串转化为TaotaoResult对象
     * 
     * @param json
     * @return
     */
    public static TaotaoResult format(String json) {
        try {
            return MAPPER.readValue(json, TaotaoResult.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
 
    /**
     * Object是集合转化
     * 
     * @param jsonData json数据
     * @param clazz 集合中的类型
     * @return
     */
    public static TaotaoResult formatToList(String jsonData, Class<?> clazz) {
        try {
            JsonNode jsonNode = MAPPER.readTree(jsonData);
            JsonNode data = jsonNode.get("data");
            Object obj = null;
            if (data.isArray() && data.size() > 0) {
                obj = MAPPER.readValue(data.traverse(),
                        MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
            }
            return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj);
        } catch (Exception e) {
            return null;
        }
    }
 
}

添加商品和商品描述对应的都是单表操作,因此我们使用逆向工程生成的代码即可,也就是说我们不用写Dao层的代码。下面我们来写Service层代码,首先在ItemService接口当中添加一个"添加商品"的接口(这一个接口要操作两张表,一张是商品表,另一张是商品描述表)。如下图所示,参数有两个,一个是商品表的pojo,另一个是商品描述。之所以要抛出异常是因为这个接口要操作两张表,而且这两张表的操作要都成功才叫成功,否则事务就回滚,因此异常要向上抛,在实现类代码中不能用try catch来捕获异常,因为这样的话springmvc会认为代码正常结束了,便不会回滚。
在这里插入图片描述

下面我们到service层来实现这个接口,如下图所示,我们在itemServiceImpl当中实现了createItem接口,其中商品ID(也叫商品编号)是采用当前毫秒数加两位随机数来生成的,为了方便以后调用,我们专门封装了一个类,叫IDUtils,里面不仅封装了商品ID的生成方法还封装了图片名称的生成方法,由于该类会被多个工程使用,因此我们也放到taotao-common的utils目录下。
在这里插入图片描述
当前ItemServiceImpl的全部代码如下

package com.taotao.service.impl;
 
import java.util.Date;
import java.util.List;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.taotao.common.pojo.EasyUIDataGridResult;
import com.taotao.common.pojo.TaotaoResult;
import com.taotao.common.utils.IDUtils;
import com.taotao.mapper.TbItemDescMapper;
import com.taotao.mapper.TbItemMapper;
import com.taotao.pojo.TbItem;
import com.taotao.pojo.TbItemDesc;
import com.taotao.pojo.TbItemExample;
import com.taotao.service.ItemService;
 
@Service
public class ItemServiceImpl implements ItemService {
	
	@Autowired
	private TbItemMapper itemMapper;
	@Autowired
	private TbItemDescMapper itemDescMapper;
 
	@Override
	public TbItem getItemById(long itemId) {
		TbItem tbItem = itemMapper.selectByPrimaryKey(itemId);
		return tbItem;
	}
 
	@Override
	public EasyUIDataGridResult getItemList(int page, int rows) {
		//设置分页信息
		PageHelper.startPage(page, rows);
		//执行查询
		TbItemExample example = new TbItemExample();
		List<TbItem>  list = itemMapper.selectByExample(example);
		//获取查询结果
		PageInfo<TbItem> pageInfo = new PageInfo<>(list);
		EasyUIDataGridResult result = new EasyUIDataGridResult();
		result.setRows(list);
		result.setTotal(pageInfo.getTotal());
		//返回结果
		return result;
	}
 
	@Override
	public TaotaoResult createItem(TbItem tbItem, String desc) throws Exception{
		//生成商品ID
		long itemId = IDUtils.genItemId();
		//补全item的属性
		tbItem.setId(itemId);
		//商品状态,1-正常,2-下架,3-删除
		tbItem.setStatus(((byte) 1));
		tbItem.setCreated(new Date());
		tbItem.setUpdated(new Date());
		itemMapper.insert(tbItem);
		//添加商品描述
		insertItemDesc(itemId, desc);
		return TaotaoResult.ok();
	}
	
	//添加商品描述
	private void insertItemDesc(long itemId,String desc){
		//创建一个商品描述表对应的pojo
		TbItemDesc itemDesc = new TbItemDesc();
		//补全pojo的属性
		itemDesc.setItemId(itemId);
		itemDesc.setItemDesc(desc);
		itemDesc.setCreated(new Date());
		itemDesc.setUpdated(new Date());
		//向商品描述表插入数据
		itemDescMapper.insert(itemDesc);
	}
 
}

IDUtils类的代码如下。

package com.taotao.utils;
 
import java.util.Random;
 
/**
 * 各种id生成策略
 * <p>Title: IDUtils</p>
 * <p>Description: </p>
 * @version 1.0
 */
public class IDUtils {
 
	/**
	 * 图片名生成
	 */
	public static String genImageName() {
		//取当前时间的长整形值包含毫秒
		long millis = System.currentTimeMillis();
		//long millis = System.nanoTime();
		//加上三位随机数
		Random random = new Random();
		int end3 = random.nextInt(999);
		//如果不足三位前面补0
		String str = millis + String.format("%03d", end3);
		
		return str;
	}
	
	/**
	 * 商品id生成
	 */
	public static long genItemId() {
		//取当前时间的长整形值包含毫秒
		long millis = System.currentTimeMillis();
		//long millis = System.nanoTime();
		//加上两位随机数
		Random random = new Random();
		int end2 = random.nextInt(99);
		//如果不足两位前面补0
		String str = millis + String.format("%02d", end2);
		long id = new Long(str);
		return id;
	}
	
	public static void main(String[] args) {
		for(int i=0;i< 100;i++)
		System.out.println(genItemId());
	}
}

下面我们来写Controller层,我们在Controller层添加addItem方法,其中value="/item/save"是在list-add.jsp的js当中定义好的,我们要保持一致才行。在Controller层我们要捕获从实现类抛出的异常,避免异常直接抛到前台页面。
在这里插入图片描述

下面我们便来试试我们的添加商品功能是否好使,由于taotao-common工程以及taotao-manager工程都做了修改,因此我们需要对这两个工程重新打包,打包方法是在工程上右键------>Run As------>Maven install。

下面我们重启taotao-manager工程和taotao-manager-web工程,启动完工程之后,我们到新增商品页面,输入相关表单信息,然后点击"提交"按钮。

我们会发现弹出一个提示框,提示我们添加成功,添加完之后,我们到商品列表中去查询,看是否有我们刚才添加的商品,我们直接查看最后一页的数据,发现最后一条就是我刚才添加的商品信息,说明添加商品成功了。

我们再到数据库中看看添加的商品信息,首先看tb_item表,我们还是到最后一页去查看,发现有我刚才添加的信息,如下图所示。

再看商品描述表,最后一页也有我们添加的商品描述信息,这说明数据存储完全没问题。这样,我们的商品添加功能便实现了。

第三天总结

8/21~8/27
1、商品类目选择
2、图片上传
a) 图片服务器FastDFS
b) 图片上传功能实现
3、富文本编辑器的使用KindEditor
4、商品添加功能完成

1、 商品类目选择

展示商品分类列表,使用EasyUI的tree控件展示。异步树控件:树控件内建异步加载模式的支持,用户先创建一个空的树,然后指定一个服务器端,执行检索后动态返回json数据来填充树并完成异步请求。

初始化tree请求的url:/item/cat/list
参数:初始化tree时只需要把第一级节点展示,子节点异步加载。
long id(父节点id)
返回值:json。数据格式
[{
“id”: 1,
“text”: “Node 1”,
“state”: “closed”
},{
“id”: 2,
“text”: “Node 2”,
“state”: “closed”
}]
state:如果节点下有子节点“closed”,如果没有子节点“open”
创建一个pojo来描述tree的节点信息,包含三个属性id、text、state。放到taotao-common工程中。
查询的表:tb_item_cat
查询列:Id、name、isparent
查询条件parentId

Dao层
tb_item_cat可以使用逆向工程生成的代码
taotao-manager-dao/src/main/java/com/taotao/mapper/TbItemCatMapper.java
TbItemCatMapper.xml

taotao-manager-pojo/src/main/java/com/taotao/pojo/TbItemCat.java
TbItemCatExample.java

Service层
参数:longparentId
业务逻辑:
1、根据parentId查询节点列表
2、转换成EasyUITreeNode列表。
3、返回。
返回值:List
taotao-manager-interface/src/main/java/com/taotao/service/ItemCatService.java
taotao-manager-service/src/main/java/com/taotao/service/impl/ItemCatServiceImpl.java
发布服务
Controller层
初始化tree请求的url:/item/cat/list
参数:
long id(父节点id)
返回值:json。数据格式
List
taotao-manager-web/src/main/java/com/taotao/controller/ItemCatController.java
引入服务

2、图片上传
a) 图片服务器FastDFS

图片上传分析
我们知道,对于传统项目来说,所有的模块都在一个项目中开发,包括所有静态资源文件比如图片等,都存储在这一个tomcat服务器上。如果访问量小的话,这样做问题倒不大,但是对于互联网项目来说,用户访问量很大,这样一个tomcat服务是远远不能满足业务需求的。这就需要部署tomcat集群,有集群就需要用到负载均衡,我们一般都会使用nginx来作为负载均衡服务器。如下图所示,但是这种tomcat集群的缺点也很明显,假如我们把一张1.jpg的图片上传到了tomcat1的images目录下了,由于nginx负责均衡处理请求,当用户去请求访问这张图片的时候,第一次,Nginx把请求交给tomcat1去处理,它到自己的images目录下去找这张图片,发现是可以找到的,于是我们便能看到这张图片,当我们第二次通过Nginx去请求访问该图片时,Nginx把请求交给tomcat2区处理了,这时tomcat2去它自己的images目录下去查找这张图片,发现并没有这张图片,因此页面上便看不到图片。作为用户来讲,一次访问能看到,再刷新就看不到,再刷新能看到,很不理解,直观的感觉便是我们的系统太烂了。
传统方式:
图片上传/下载。 Tomcat<webapap<Images|-a.jpg
集群环境:
负载均衡服务器ngnix
1.上传图片。 Tomcat1<webapap<Images|-a.jpg
Tomcat2<webapap<Images|-a.jpg
2.上传到图片服务器
图片服务器FastDFS < Images|-a.jpg<http服务器(1.Tomcat2.Apache3.ngnix)
3.访问图片

解决方案:搭建一个图片服务器,专门保存图片。可以使用分布式文件系统FastDFS。
1.上传图片。 Tomcat1<webapap
Tomcat2<webapap
2.上传到图片服务器
图片服务器FastDFS < Images|-a.jpg<http服务器
3.访问图片
针对上面提到的问题,我们对集群做下改善,我们专门搞一个图片服务器,所有的tomcat都将用户上传的图片上传到图片服务器上,tomcat本身并不保存图片。我们采用http的方式来访问图片,这样我们就需要使用http服务器,能作为http服务器的有多种选择。
第一:tomcat可以作为http服务器,但是由于tomcat的强项并不在于处理静态资源( 它的强项是处理servlet和jsp等动态请求)因此我们不选择tomcat。
第二:使用Apache作为http服务器,Apache是由c语言编写的一款服务器(注意,这里指的并不是Apache 组织,仅仅是一个服务器),这款服务器在以前用的人是很多的,现在用的人少了。
第三:使用nginx,nginx因为其独特的优势,作为http服务器是目前最火的。我们就使用nginx来统一管理这些图片,这样用户要访问图片的时候,nginx直接把图片服务器上的图片给返回就可以了,从而解决了tomcat集群资源无法共享的问题。
但是这里需要考虑一个问题,那就是作为服务器,容量肯定是有限的,当这个服务器容量满了,怎么办?还有就是图片服务器挂了,怎么办?这些都是必须要解决的问题,为了解决这两个问题,我们使用FastDFS集群来解决,FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。它的优势是可以水平扩容,FastDFS存储资源的设备是按组来区分的,当存储空间不足时,便可以通过水平增加分组并相应添加设备来达到扩容的目的,而且是没有上限的。还有个优势是高可用,也就是说FastDFS集群能够做到当提供服务的nginx发生故障时,自动切换到另一台nginx设备上,保障服务的稳定。
图片服务器的安装
1、存储空间可扩展。
2、提供一个统一的访问方式。
使用FastDFS,分布式文件系统。存储空间可以横向扩展,可以实现服务器的高可用。支持每个节点有备份机。
4.1. 什么是FastDFS?
FastDFS是用c语言编写的一款开源的分布式文件系统。FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
4.2. FastDFS架构
FastDFS架构包括 Tracker server和Storage server。客户端请求Tracker server进行文件上传、下载,通过Tracker server调度最终由Storage server完成文件上传和下载。
Trackerserver作用是负载均衡和调度,通过Trackerserver在文件上传时可以根据一些策略找到Storage server提供文件上传服务。可以将tracker称为追踪服务器或调度服务器。
Storageserver作用是文件存储,客户端上传的文件最终存储在Storage服务器上,Storage server没有实现自己的文件系统而是利用操作系统的文件系统来管理文件。可以将storage称为存储服务器。

服务端两个角色:
Tracker:管理集群,tracker也可以实现集群。每个tracker节点地位平等。
收集Storage集群的状态。
Storage:实际保存文件
Storage分为多个组,每个组之间保存的文件是不同的。每个组内部可以有多个成员,组成员内部保存的内容是一样的,组成员的地位是一致的,没有主从的概念。
文件上传的流程

组名:文件上传后所在的storage组名称,在文件上传成功后有storage服务器返回,需要客户端自行保存。
虚拟磁盘路径:storage配置的虚拟路径,与磁盘选项store_path*对应。如果配置了store_path0则是M00,如果配置了store_path1则是M01,以此类推。
数据两级目录:storage服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据文件。
文件名:与文件上传时不同。是由存储服务器根据特定信息生成,文件名包含:源存储服务器IP地址、文件创建时间戳、文件大小、随机数和文件拓展名等信息。
4.4. 文件下载

4.5. 最简单的FastDFS架构

图片服务器安装方法
写了博客

b) 图片上传功能实现
6.2.1. 上传步骤
1、加载配置文件,配置文件中的内容就是tracker服务的地址。
配置文件内容:tracker_server=192.168.25.133:22122
2、创建一个TrackerClient对象。直接new一个。
3、使用TrackerClient对象创建连接,获得一个TrackerServer对象。
4、创建一个StorageServer的引用,值为null
5、创建一个StorageClient对象,需要两个参数TrackerServer对象、StorageServer的引用
6、使用StorageClient对象上传图片。
7、返回数组。包含组名和图片的路径。

6.3. 使用工具类上传
7. 图片上传功能
7.1. 功能分析
使用的是KindEditor的多图片上传插件。
请求的url:/pic/upload
参数:MultiPartFileuploadFile
返回值:返回格式JSON(成功时。{“error”:0,”url”:”http://www.example.com/path/to/file.ext”} 失败时。{“error”:1,”message”:”错误信息”} )
可以创建一个pojo对应返回值。可以使用map

业务逻辑:
1、接收页面传递的图片信息uploadFile
2、把图片上传到图片服务器。使用封装的工具类实现。需要取文件的内容和扩展名。
3、图片服务器返回图片的url
4、将图片的url补充完整,返回一个完整的url。
5、把返回结果封装到一个Map对象中返回。

1、需要把commons-io、fileupload 的jar包添加到工程中。
2、配置多媒体解析器。

Controller
PictureController.java->fileUpload

解决浏览器兼容性的问题
KindEditor的图片上传插件,对浏览器兼容性不好。
使用@ResponseBody注解返回java对象,
Content-Type:application/json;charset=UTF-8
返回字符串时:
Content-Type:text/plan;charset=UTF-8
指定响应结果的content-type:

3、富文本编辑器的使用KindEditor
纯js开发,跟后台语言没有关系。
使用方法
第一步:在jsp中引入KindEditor的css和js代码。
第二步:在表单中添加一个textarea控件。是一个富文本编辑器的载体。类似数据源。
第三步:初始化富文本编辑器。使用官方提供的方法初始化。
第四步:取富文本编辑器的内容。
表单提交之前,把富文本编辑器的内容同步到textarea控件中。

4、商品添加功能完成
9.1. 功能分析
请求的url:/item/save
参数:表单的数据。可以使用pojo接收表单的数据,要求pojo的属性和input的name属性要一致。
使用TbItem对象接收表单的数据。
TbItem item,String desc
返回值:Json数据。应该包含一个status的属性。
可以使用TaotaoResult,放到taotao-common中。(status msg data)
taotao-common/src/main/java/com/taotao/common/pojo/TaotaoResult.java
业务逻辑:
1、生成商品id
实现方案:
a) Uuid,字符串,不推荐使用。
b) 数值类型,不重复。日期+时间+随机数20160402151333123123
c) 可以直接去毫秒值+随机数。可以使用。
d) 使用redis。Incr。推荐使用。
使用IDUtils生成商品id
taotao-common/src/main/java/com/taotao/common/util/IDUtils.java
2、补全TbItem对象的属性
3、向商品表插入数据
4、创建一个TbItemDesc对象
5、补全TbItemDesc的属性
6、向商品描述表插入数据
7、TaotaoResult.ok()
9.2. Dao层
向tb_item,tb_item_desc表中插入数据
可以使用逆向工程
taotao-manager-pojo/src/main/java/com/taotao/pojo/TbItemDesc.java
TbItemDescExample.java
TbItem.java
TbItemExample.java
taotao-manager-dao/src/main/java/com/taotao/mapper/TbItemDescMapper.java
TbItemDescMapper.xml
TbItemMapper.java
TbItemMapper.xml

9.3. Service层
参数:TbItemitem,String desc
业务逻辑:略,参加上面
返回值:TaotaoResult
taotao-manager-interface/src/main/java/com/taotao/service/ItemService.java
taotao-manager-service/src/main/java/com/taotao/service/impl/ItemServiceImpl.java
发布服务

9.4. 表现层
9.4.1. 引用服务
9.4.2. Controller
请求的url:/item/save
参数:TbItemitem,String desc
返回值:TaotaoResult
9.5. 作业
商品修改、商品删除、上架下架。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值