文章目录
vm虚拟机启动命令
[root@localhost ~]# /usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf
[root@localhost ~]# /usr/bin/fdfs_storaged /etc/fdfs/storage.conf
[root@localhost conf]# /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx-fdfs.conf
分布式文件系统
技术应用场景
传智播客拥有大量优质的视频教程,并且免费提供给用户去下载,文件太多如何高效储存?用户访问量大如何保证下载速度?
分布式系统将解决这些问题
分布式系统解决海量文件存储以及传输访问瓶颈问题,对海量视频,文件的管理等.
系统文件量增长的速度,远远大于扩充硬盘的速度
借助于互联网来存储
什么是分布式文件系统?
什么是分布式文件系统?
首先,先了解什么是文件系统
引用百度百科中的描述:
文件系统
文件系统是操作系统的明确存储设备(常见是磁盘,也有基于NAND Flash固态硬盘)或分区上的文件的方法或者数据结构;
即在存储设备上组织文件的方法 . 操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统. 文件系统由三部分组成:文件系统的接口,对对象的操作和管理软件的集合,对象及属性.从操作系统角度来看,从系统角度来看,文件系统是对文件存设备空间上的组织和分配,负责文件存储并对存储文件进行保护和检索的系统,具体来说,它负责为用户建立文件,存入,读出,修改,和转存文件,控制文件的存取,当用户不在使用时撤销文件等.
总结:文件系统是负责管理文件和存储文件的系统软件,操作系统通过文件系统提供的接口取存取文件,用户通过操作系统访问磁盘上的文件.
常见的文件系统:
FAT16/FAT32 , NTFS , HFS,UFS,APFS,XFS,Ext4等.
如果没有文件系统,那么我们该怎么管理自己的文件?
这个就是硬盘,不同的内容存在于不用的扇区
什么是分布式文件系统
分布式文件系统
引用百度百科:
分布式文件系统(Distributed File System)是指文件系统管理的物理存储资源不一定直接连接在本地节点上,而是通过计算机网路于节点相连接,分布式文件系统的设计基于客户机/服务器模式.一个典型的网络可能包括供多个用户访问的服务器.另外,对等特性允许一些系统扮演客户机和服务器的双重角色.例如,用户可以发表一个允许其他客户访问的目录,一旦被访问,这个目录对于客户机来说就像使用本地驱动器一样,下面是三个基本的分布式文件系统.
为什么会有分布式文件系统呢?
分布式文件系统是面对互联网的需求而产生,互联网时代对海量数据如何存储?靠简单的增加硬盘的个数已经无法满足.我们的要求.因为硬盘的传输速度有限但是数据在急速的增长,另外,我们要做好数据的备份,数据安全等.
采用分布式文件系统可以将多个地点的文件系统通过网络连接起来,组成一个文件系统网络,节点之间通过网络进行通信,一台文件系统的存储和传输能力有限,我们让文件在多台计算机上存储,通过多台计算机共同传输.
好处:
- 一台计算机的文件系统的处理能力扩充到多台计算机上同时处理.
- 一台计算机挂了还有另外副本计算机提供数据.
- 每台计算机可以放在不同的地域,这样用户就可以就近访问,提高访问速度
主流的分布式文件系统
NFS
NFS(网络文件系统)
百度百科:
NFS(Network File System)即网络文件系统,是FreeBSD支持的文件系统中的一种,它允许网络中的计算机通过TCP/IP网络共享资源.在NFS的应用中,本地NFS的客户端应用可以透明的读写位于远端的NFS服务器上的文件,就像访问本地文件一样.
1.在客户端上映射NFS服务器的驱动器.
2.客户通过网络访问NFS的服务器的硬盘完全透明.
GFS
googleFs
百度百科:
GFS是一个可拓展的分布式文件系统,用于大型的,分布式的,对大量数据进行访问的应用.它用于廉价的普通硬件上,可以提供容错功能,它可以给大量用户提供总体性能较高的服务.
- GFS采用主从结构,一个GFS由一个master和大量的chunkserver组成.
- master存储了数据文件的元数据,一个文件被分成了若干块存储在chunkserver中.
- 用户从master中获取元信息,从chunkserver中存储数据.
将同一个文件的不同部分分别存储在不同的服务器上 这样性能高,速度快,而且安全.
HDFS
HDFS:
Hadoop分布式文件系统(HDFS)被设计成适合运行在通用硬件(Commodity hardware)上的分布式文件系统.它和现有的分布式文件系统有很多共同点.但同时,它和其他分布式文件系统也有很大的区别.HDFS是一个高度容错性的系统,适合部署在廉价的机器上.HDFS能提高吞吐量的数据访问,但非常适合大规模数据集上的应用.HDFS放宽了一部分POSIX约束,来实现流式读取分布式文件系统的目的.HDFS在最开始是Apach Nutch搜索引擎项目的基础架构而开发的.HDFS是ApachHadoopCore项目的一部分.
HDFS有着高容错性(fault-tolerant)的特点,并且设计用拉力部署在低廉的(low-cost)硬件上.而且它提高吞吐量(highThroughPut)来访问应用程序数据,适合那些有着超大数据集(largeDataSet)的应用程序.HDFS放宽了(relax)POSIX的要求(requirement)这样可以实现流的形式访问(streamingAccess)文件系统中的数据.
- HDFS采用主从结构,一个HDFS汲取能有一个名称节点和若干个数据节点组成.名称节点存储数据的元信息,一个完整的文件分成若干块存储在数据节点.
- 客户端从名称节点获取数据的元信息,得到信息客户端即可从数据块来存取数据.
分布式文件服务提供商
阿里的OSS
什么是OSS
阿里云对象存储服务(objectStorageService,简称OSS),是阿里提供的海量,安全,低成本,可靠的云存储服务.它具有与平台无关的RestfulApi接口,能提供99.999999999%的服务持久性.您可以在任何应用,任何时间,任何地点存储和访问任意类型的数据.OSS适合各种网站,开发企业及开发者使用.
您可以使用阿里云提供的API/SDK接口或者OSS迁移工具轻松的将海量数据移入移出阿里云OSS.数据存储到阿里云OSS以后,您可以选择标准类型(Standard)的阿里云OSS服务作为移动应用,大型网站,图片分享或热点视频的主要存储方式,也可以选择低成本,存储期限更长的低频访问类型(InfrequentAccess)和归档类型(Archive)的阿里云OSS服务作为不经常访问数据的备份和归档类型.
七牛云存储
百度云存储
什么是fastDFS
fastDFS介绍
FastDFS是用c语言编写的一个分布式文件管理系统,它由淘宝资深架构师余庆编写并且开源 , FastDFS专为互联网量身定制, 从分考虑冗余备份,负载均衡,线性扩容等机制,并且注重高可用,高性能等指标,使用fastDFS很容易单间一个高性能文件服务器集群,提供文件上传,下载等服务.
为什么要用fastDFS呢?
上边介绍的NFS,GFS都是通过分布式文件系统,通过分布式文件系统的有点就是开发体验好,但是系统的复杂度高,性能一般,而且专用的分布式系统虽然开发体验差,但是系统复杂性低并且性能高,fastDFS非常适合用来存储图片等那些小的文件,fastDFS不对文件进行分块,所以他就没有分块合并的开销,fastDFS网络通信采用socker,通信速度很快
fastDFS工作原理
fastDFS架构
文件上传流程
客户端上传文件后存储服务器将文件id返回给客服端,此文件id用于后续访问该文件的索引信息.文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名.
文件id格式
- 组名:文件上传所在的storage组名称,在文件上传成功后,由storage服务器返回,需要客服端自行保存.
- 虚拟磁盘路径:storage配置的虚拟路径,与磁盘选项store_path*对应,如果配置了store_path0,则是M00,
- 数据两级目录:strage服务器在每个虚拟磁盘路径下创建两级目录,用于存储数据文件
- 文件名:与文件上传不同,是由存储服务器根据特定信息生成,文件名包含:原存储服务器ip地址,文件创建时间戳,文件大小,随机数和文件拓展名等信息.
文件下载流程
tracker根据请求文件的路径即文件id来快速定义文件.
比如请求下边的文件:
fastDFS入门
fastDFS安装与配置
导入虚拟机
对fastDFS安装过程不需要掌握
关于tracker和storage的配置,可以自行查询,我这里就不再一一介绍了,
过程可能有点麻烦,
但是我们作为学者,纵使知道不可为
而为之
这就是我们的精神,
正是有着这种学者精神,
人类的历史书页,
才会那样的多姿多彩
在idea中新建一个项目
test-fdfs
在pom文件中加入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/net.oschina.zcx7878/fastdfs-client-java -->
<dependency>
<groupId>net.oschina.zcx7878</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
这个时test文件中的测试代码
在测试之前,需要在java文件下编写运行代码
package com.xuecheng.test.fastdfs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author Administrator
* @version 1.0
* @create 2018-06-21 12:40
**/
@SpringBootApplication
public class TestFastDFSApplication {
public static void main(String[] args) {
SpringApplication.run(TestFastDFSApplication.class,args);
}
}
上传测试中的Test代码
package com.xuecheng.test.fastdfs;
import org.csource.common.MyException;
import org.csource.fastdfs.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import sun.usagetracker.UsageTrackerClient;
import java.io.IOException;
/**
* @author Andrewer
* @version 1.0
* @project xcEduService01
* @description
* @date 2022/12/11 14:06:13
*/
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestFastDFS {
// 上传测试
@Test
public void testUplod(){
try {
// 加载fastdfs-client.properties配置文件
ClientGlobal.initByProperties("config/fastdfs-client.properties");
// 定义,TrackerClient,用于请求TrackerServer
TrackerClient trackerClient = new TrackerClient();
// 连接tracker
TrackerServer trackerServer = trackerClient.getConnection();
// 获取Storage
StorageServer storeStorage = trackerClient.getStoreStorage(trackerServer);
// 创建storageClient
StorageClient1 storageClient1 = new StorageClient1(trackerServer,storeStorage);
// 向Storage服务器上传文件
// 确定本地文件的路径
String filePath = "E:/screen/2b9fd311f7e3d71ed7f99725d03010f5.jpeg";
// 上传成功后拿到文件的id
String fileId = storageClient1.upload_file1(filePath, "jpeg", null);//meta_list辕信息,就是文件的一些原始的摘要信息
System.out.println(fileId);
//group1/M00/00/00/wKjqg2OVfIqAeDVqAABnB9qZjYw28.jpeg
// group1/M00/00/00/wKjqg2OV4pWAZx0SAABnB9qZjYw11.jpeg
} catch (Exception e) {
e.printStackTrace();
}
}
// 下载测试
}
通过在sout(fileId)处打断点,可以得到fileId的内容
通过fileId在Storage处查询,即可查到
[root@localhost data]#ll 00/00/wKjqg2OV4pWAZx0SAABnB9qZjYw11.jpeg
-rw-r--r--. 1 root root 26375 12月 11 22:00 00/00/wKjqg2OV4pWAZx0SAABnB9qZjYw11.jpeg
[root@localhost data]#
文件下载流程
下载测试代码:
// 下载测试
@Test
public void testsDownload(){
try {
// 加载fastdfs-client.properties配置文件
ClientGlobal.initByProperties("config/fastdfs-client.properties");
// 定义TracekrClient,用于请求TrackerServer
TrackerClient trackerClient = new TrackerClient();
// 连接Tracker
TrackerServer trackerServer = trackerClient.getConnection();
// 获取storage
StorageServer storeStorage = trackerClient.getStoreStorage(trackerServer);
// 创建SotrageClient
StorageClient1 storageClient1 = new StorageClient1(trackerServer,storeStorage);
//
//
// 下载文件
// 文件的id
String fileId = "group1/M00/00/00/wKjqg2OV4pWAZx0SAABnB9qZjYw11.jpeg";
byte[] bytes = storageClient1.download_file1(fileId);
// 使用输出流,来保存文件
FileOutputStream fileOutputStream = new FileOutputStream(new File("d:/testdfs/logo.jpeg"));
fileOutputStream.write(bytes);
} catch (Exception e) {
e.printStackTrace();
}
和上传测试差不多,
- 加载fastdfs配置文件
- 定义trackerclient
- 连接tracker
- 获取storage
- 创建storageclient
- 下载文件
- 使用输出流保存文件
- 调用write方法输出
加载fastdfs配置文件
后面的上传变成了下载,其中fileoutputStream流中可能会遇到访问不允许,
这个文件是下载文件所在文件夹有权限设置,idea无法操作导致的报错
修改文件的安全属性就可以了.
下载成功!
文件查询
@Test
public void testQueryFile() throws MyException, IOException {
ClientGlobal.initByProperties("config/fastdfs-client.properties");
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
StorageServer storageServer = null;
StorageClient storageClient = new StorageClient(trackerServer,storageServer);
FileInfo fileInfo = storageClient.query_file_info("group1","M00/00/00/wKjqg2OV4pWAZx0SAABnB9qZjYw11.jpeg");
System.out.println(fileInfo);
运行后输出fileInfo
source_ip_addr = 192.168.234.131, file_size = 26375, create_timestamp = 2022-12-11 22:00:53, crc32 = -627470964
搭建图片虚拟主机
在storage上安装nginx
cd进入到nginx-1.18.0目录内执行
[root@centos7 nginx-1.18.0]# ./configure --prefix=/usr/local/nginx
[root@centos7 nginx-1.18.0]# make
[root@centos7 nginx-1.18.0]# make install
ps:–prefix=path 定义一个目录,存放服务器上的文件 ,也就是nginx的安装目录,默认使用 /usr/local/nginx
这步你会看到local目录下有一个跟你安装的nginx1.12.2同级的nginx目录,里面就有sbin目录了。
启动:cd /usr/local/nginx/sbin;输入./nginx 启动nginx。如果nginx被配置成了服务可以这样启动/etc/init.d/nginx start
配置Nginx图片服务虚拟机主机
图片服务虚拟主机的作用是负载均衡,将土拍你请求转发到storage server上.
1.通过图片服务虚拟主机请求图片流程图
这个是虚拟机上面的tracker和storage,nginx启动后面跟着的是配置文件
[root@localhost ~]# /usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf
[root@localhost ~]# /usr/bin/fdfs_storaged /etc/fdfs/storage.conf
[root@localhost conf]# /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx-fdfs.conf
启动nginx
这里的配置文件目录更具不同人的操作不同,其中nginx-fdfs.conf 是我自己配置的文件
文件内容如下:
http
{
server{
listen 80;
server_name 192.168.234.131;
location /group1/M00/{
root /home/fastdfs/fdfs_storage/data;
}
}
}
events {
worker_connections 1024; ## Default: 1024
}
这里有的的人可能会遇到nginx包中没有sbin这个问题
将nginx-1.8.0.tar.gz拷贝到/usr/local下
解压nginx-1.8.0.tar.gz
进入nginx-1.8.0目录,执行如下配置命令:
下边红色部分表示添加FastDFS-nginx-module模块
./configure --prefix=/usr/local/nginx
./configure --pid-path=/var/run/nginx/nginx.pid
./configure --lock-path=/var/lock/nginx.lock
./configure --error-log-path=/var/log/nginx/error.log
./configure --http-log-path=/var/log/nginx/access.log
./configure --with-http_gzip_static_module
./configure --http-client-body-temp-path=/var/temp/nginx/client
./configure --http-proxy-temp-path=/var/temp/nginx/proxy
./configure --http-fastcgi-temp-path=/var/temp/nginx/fastcgi
./configure --http-uwsgi-temp-path=/var/temp/nginx/uwsgi
./configure --http-scgi-temp-path=/var/temp/nginx/scgi
./configure --add-module=/usr/local/FastDFS-nginx-module/src
make
make install
查看fdfs进程
[root@localhost conf]# /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx-fdfs.conf
[root@localhost conf]# ps aux|grep fdfs
root 6562 0.0 6.6 212720 66772 ? Sl 12月11 0:24 /usr/bin/fdfs_storaged /etc/fdfs/storage.conf
root 61487 0.0 0.0 24884 748 ? Ss 01:45 0:00 nginx: master process /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx-fdfs.conf
root 61518 5.5 0.0 112828 988 pts/1 S+ 01:58 0:00 grep --color=auto fdfs
[root@localhost conf]#
这里会遇到这个问题,显示fastdfs这个module无法理解
,可能是这个包未导入也就是上面
./configure --add-module=/usr/local/FastDFS-nginx-module/src
这句的问题,如果已经导入的话,也有可能是名字的问题,我这里把FastDFS换成小写的就成功了
[root@localhost sbin]# ./nginx -t
nginx: [emerg] unknown directive "ngx_FastDFS_module" in /usr/local/nginx/conf/nginx.conf:49
nginx: configuration file /usr/local/nginx/conf/nginx.conf test failed
[root@localhost sbin]# ./nginx -t
ngx_http_fastdfs_set pid=94877
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/
pkill -9 nginx
./nginx
反正无论是什么样子的问题,只要耐心下来仔细解决,看清楚系统提示的错误,那么,就一定会解决的!
加载成功后,
`
[root@localhost conf]# /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx-fdfs.conf
ngx_http_fastdfs_set pid=27453
访问web端口,得到图片
在win主机上的nginx中,调节conf
#CMS页面预览
upstream cms_server_pool{
server 127.0.0.1:31001 weight=10;
}
#图片服务配置了group1下的storage服务器地址
upstream img_server_pool{
#server 192.168.234.128:80 weight=10;
server 192.168.234.131:80 weight=10;
}
#upstream img_server_pool_group2{
#server 192.168.234.128:80 weight=10;
# server 192.168.234.131:80 weight=10;
}
server {
listen 80;
server_name www.xuechengliuguanghan.com;
ssi on;
ssi_silent_errors on;
location / {
alias E:/jetbrain/ws/xc-ui-pc-static-portal/;
index index.html;
}
#页面预览
location /cms/preview/{
proxy_pass http://cms_server_pool/cms/preview/;
}
}
#学成网图片服务
server{
listen 80;
server_name img.xuecheng.com
#个人中心
location /group1{
proxy_pass http://img_server_pool;
}
}
}
C:\Windows\System32\drivers\etc
下有一个文件hosts
打开
配置
0.0.0.0 account.jetbrains.com
127.0.0.1 www.xuecheng.com
127.0.0.1 img.xuecheng.com
127.0.0.1 ucenter.xuecheng.com
127.0.0.1 system.xuecheng.com
127.0.0.1 teacher.xuecheng.com
127.0.0.1 video.xuecheng.com
127.0.0.1 image.xuecheng.com
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
#CMS页面预览
upstream cms_server_pool{
server 127.0.0.1:31001 weight=10;
}
#图片服务配置了group1下的storage服务器地址
upstream img_server_pool{
#server 192.168.234.128:80 weight=10;
server 192.168.234.131:80 weight=10;
}
server {
listen 80;
server_name www.xuecheng.com;
location / {
ssi on;
ssi_silent_errors on;
alias E:/jetbrain/ws/xc-ui-pc-static-portal/;
index index.html;
}
#页面预览
location /cms/preview/{
proxy_pass http://cms_server_pool/cms/preview/;
}
}
#学成网图片服务
server{
listen 80;
server_name img.xuecheng.com;
#个人中心
location /group1{
proxy_pass http://img_server_pool;
}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
在这里我遇到了一个问题,就是nginx访问img.xuecheng.com时候拒绝访问
host没有问题,
nginx.conf也没有问题,
log日志没有出错
虚拟机那边也没有问题
换了个浏览器也没有问题
我这边判断可能是hosts这里出的问题,可能是应为没有走内网络导致的拒绝访问
更具网上的资料修改host,文件格式,内容格式,ipconfig /flushdns清除内缓存等,都没有用
卸载掉nginx也没有用,这时查看任务管理器中发现有两个nginx,以为有一个是真的而我修改的这个是假的,查了一下,一个是工程nginx,一个是守护nginx
但是新下载的未配置内域名,访问localhost的时候,却还是弹出之前配置好的网页
我觉得我这个应该是假的nginx,应该还有一个真的nginx在运行,抢占了我一直再修改的nginx,查询了一下之前可能存在过使用另一个nginx的踪迹,终于找到了,但是当我打开另一个的conf文件时,却发现这个conf是初始化的文件,
打开任务栏窗口,发现此时却有七个nginx.exe进程在运行,全部结束掉之后,重新打开新下载的nginx,未配置conf运行localhost
弹出
在这个conf中开始配置内域名和映射,
再次访问img.xuecheng.com/group1/M00/00/00/wKjqg2OYJjyAX2t-AC5x3kz8Zco29.jpeg
获取到虚拟机中storage中的图片
上传图片开发
需求分析
在很多系统都有上传图片/上传文件的需求,比如:上传课程图片,上传课程资料,上传用户头像等,为了提供系统的可重用性,装维设立文件系统服务承担图片/文件的管理,文件系统服务实现对文件的上传,删除,查询等功能进行管理.
各各子系统不在开发上传文件请求,各各子系统同通过文件系统服务进行文件的上传,删除等操作,文件系统服务最总会将文件存储到fastDFS文件系统中.
下图是各各子系统与文件系统服务之间的关系:
下图是课程管理中上传图片处理流程:
执行流程如下:
创建文件系统服务工程
导入xc-service-base-filesystem.zip工程
1.工程目录结构
将这个工程加入项目module
<dependencies>
<dependency>
<groupId>com.xuecheng</groupId>
<artifactId>xc-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.xuecheng</groupId>
<artifactId>xc-framework-model</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.xuecheng</groupId>
<artifactId>xc-framework-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>net.oschina.zcx7878</groupId>
<artifactId>fastdfs-client-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
</dependencies>
pom中的依赖文件
里里面有fastdfs
和mongdb
在mongdb中新建xc_fs
对应配置问年中的数据
application.yml
server:
port: 22100
spring:
application:
name: xc-service-base-filesystem
#mongo配置
data:
mongodb:
# 文件系统服务的数据库
database: xc_fs
uri: mongodb://root:123@127.0.0.1:27017
#SpringMVC上传文件配置
servlet:
multipart:
#默认支持文件上传.
enabled: true
#支持文件写入磁盘.
file-size-threshold: 0
# 上传文件的临时目录
location:
# 最大支持文件大小
max-file-size: 1MB
# 最大支持请求大小
max-request-size: 30MB
xuecheng:
fastdfs:
connect_timeout_in_seconds: 5
network_timeout_in_seconds: 30
charset: UTF-8
tracker_servers: 192.168.234.131:22122 #多个 trackerServer中间以逗号分隔
因为在model中存在
文件,导入配置之后会自动在xc_fs中根据model填写mongdb中的文件
Api接口
package com.xuecheng.api.filesystem;
import com.xuecheng.framework.domain.filesystem.response.UploadFileResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import jdk.internal.org.objectweb.asm.tree.analysis.Value;
import org.springframework.web.multipart.MultipartFile;
/**
* @author Andrewer
* @version 1.0
* @project xcEduService01
* @description
* @date 2022/12/14 13:01:49
*/
@Api(value = "文件管理接口",description = "文件的管理接口,提供文件的上传服务")
public interface FileSystemControllerApi {
// 上传文件
@ApiOperation("上传文件接口")
public UploadFileResult upload(MultipartFile multipartFile,//文件本身
String filetag,//文件tag
String businesskey,//文件业务标识
String metadata);//文件原信息,上传json格式,妆化为map
}
Dao
将文本信息存入数据库,主要存储文件系统中的文件路径
package com.xuecheng.filesystem.dao;
import com.xuecheng.framework.domain.filesystem.FileSystem;
import org.springframework.data.mongodb.repository.MongoRepository;
/**
* @author Andrewer
* @version 1.0
* @project xcEduService01
* @description
* @date 2022/12/14 13:15:01
*/
//FileSystem类型,主键String
public interface FileSystemRepository extends MongoRepository<FileSystem,String> {
}
Surround with 快捷键 cont+art+t
Service
package com.xuecheng.filesystem.service;
import com.alibaba.fastjson.JSON;
import com.xuecheng.filesystem.dao.FileSystemRepository;
import com.xuecheng.framework.domain.filesystem.FileSystem;
import com.xuecheng.framework.domain.filesystem.response.FileSystemCode;
import com.xuecheng.framework.domain.filesystem.response.UploadFileResult;
import com.xuecheng.framework.exception.ExceptionCast;
import com.xuecheng.framework.model.response.CommonCode;
import org.apache.commons.lang3.StringUtils;
import org.csource.common.MyException;
import org.csource.fastdfs.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.Map;
/**
* @author Andrewer
* @version 1.0
* @project xcEduService01
* @description
* @date 2022/12/14 13:17:02
*/
@Service
public class FileSystemService {
//将application中的配置信息注入到service中
@Value("${xuecheng.fastdfs.tracker_servers}")
String tracker_servers;
@Value("${xuecheng.fastdfs.connect_timeout_in_seconds}")
int connect_timeout_in_seconds;
@Value("${xuecheng.fastdfs.network_timeout_in_seconds}")
int network_timeout_in_seconds;
@Value("${xuecheng.fastdfs.charset}")
String charset;
@Autowired
FileSystemRepository fileSystemRepository;
// 上传文件
public UploadFileResult upload(MultipartFile multipartFile,
String filetag,
String businesskey,
String metadata) {
if (multipartFile ==null){
ExceptionCast.cast(FileSystemCode.FS_UPLOADFILE_FILEISNULL);
}
//第一步,将文件上传到fastDFS中 ,得到一个文件的id
String fileId = fdfs_upload(multipartFile);
if (StringUtils.isEmpty(fileId)){
ExceptionCast.cast(FileSystemCode.FS_UPLOADFILE_SERVERFAIL);
}
// 第二步,将文件的id以及文件的信息存储到mongodb中
FileSystem fileSystem = new FileSystem();
fileSystem.setFileId(fileId);
fileSystem.setFilePath(fileId);
fileSystem.setFiletag(filetag);
fileSystem.setBusinesskey(businesskey);
fileSystem.setFileName(multipartFile.getOriginalFilename());
fileSystem.setFileType(multipartFile.getContentType());
// 注意
if(StringUtils.isNotEmpty(metadata)){
try {
Map map = JSON.parseObject(metadata, Map.class);//转化为map格式
fileSystem.setMetadata(map);
} catch (Exception e) {
e.printStackTrace();
}
}
fileSystemRepository.save(fileSystem);
return new UploadFileResult(CommonCode.SUCCESS,fileSystem);
}
// 上传文件到dfs中
/**
* @param multipartFile 文件本身
* @return 返回文件的id
*/
private String fdfs_upload(MultipartFile multipartFile) {
//初始化fastDFS的环境
initFdfsConfig();
// 创建一个trackerClient
TrackerClient trackerClient = new TrackerClient();
try {
TrackerServer trackerServer = trackerClient.getConnection();
// 得到storage服务器
StorageServer storeStorage = trackerClient.getStoreStorage(trackerServer);
// 创建也给storageCLient来上传文件
StorageClient1 storageClient1 =new StorageClient1(trackerServer,storeStorage);
// 上传文件
// 得到文件的字节
byte[] bytes = multipartFile.getBytes();
// 得到文件的原始名称
String originalFilename = multipartFile.getOriginalFilename();
// 得到扩展名
String ex = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);//获取文件最后一个点加1的index开始的字符串
String fileId = storageClient1.upload_file1(bytes, ex, null);
return fileId;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// 初始化fastDFS环境
private void initFdfsConfig() {
// 初始化tracker服务器(多个tracker服务器,中间是以半角逗号分割(英文状态下打一个逗号))
try {
ClientGlobal.initByTrackers(tracker_servers);
ClientGlobal.setG_charset(charset);
ClientGlobal.setG_network_timeout(network_timeout_in_seconds);
ClientGlobal.setG_connect_timeout(connect_timeout_in_seconds);
} catch (Exception e) {
e.printStackTrace();
// 抛出异常
ExceptionCast.cast(FileSystemCode.FS_INITFDFS_ERROR);
}
}
}
Controller
package com.xuecheng.filesystem.controller;
import com.xuecheng.api.filesystem.FileSystemControllerApi;
import com.xuecheng.filesystem.service.FileSystemService;
import com.xuecheng.framework.domain.filesystem.response.UploadFileResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
/**
* @author Andrewer
* @version 1.0
* @project xcEduService01
* @description
* @date 2022/12/14 19:00:09
*/
@RestController
@RequestMapping("/filesystem")
public class FileSystemController implements FileSystemControllerApi {
@Autowired
FileSystemService fileSystemService;
@Override
@PostMapping("/upload")
public UploadFileResult upload(MultipartFile multipartFile, String filetag, String businesskey, String metadata) {
return fileSystemService.upload(multipartFile, filetag, businesskey, metadata);
}
}
运行application文件,
访问swagger-ui.html
得到接口,通过此处,在idea中打断点测试
测试成功!
{
"success": true,
"code": 10000,
"message": "操作成功!",
"fileSystem": {
"fileId": "group1/M00/00/00/wKjqg2OZOWKAAr2yAACaiorb23o22.jpeg",
"filePath": "group1/M00/00/00/wKjqg2OZOWKAAr2yAACaiorb23o22.jpeg",
"fileSize": 0,
"fileName": "9ae58735c9a6d0bb483bfa6d95de3562.jpeg",
"fileType": "image/jpeg",
"fileWidth": 0,
"fileHeight": 0,
"userId": null,
"businesskey": "businesskey",
"filetag": "filetag",
"metadata": {
"name": "test"
}
}
}
mongodb中文件成功被写入
启动前端服务器
打开页面,
选择管理课程,打开后选择添加图片
加载完成后在idea中跳过断点,
web端显示文件为空
打开course_picture.vue
action中/api/filesystem/upload
加上name属性之后,上传成功
保存课程图片
需求分析
图片上传到文件系统之后,其他子系统如果想使用图片可以引用图片的地址,课程管理模块使用图片的方式是将图片保存到课程数据库中.
业务流程如下:
1.上传图片到文件系统服务
2.保存图片地址到课程管理服务
在课程管理服务创建保存课程与图片对应关系的表course_pic
课程管理服务端开发
API
课程管理需要使用图片则在课程管理服务中要提供保存课程图片的api
在CourseControllerApi中加入
@ApiOperation(("添加课程图片"))
public ResponseResult addCoursePic(String courseId,String pic);
CourseController:
@Override
@PostMapping("/coursepic/add")
public ResponseResult addCoursePic(@RequestParam("cousrseId") String courseId, @RequestParam("pic") String pic) {
return courseService.addCoursePic();
}
CourseService:
//像课程管理数据添加课程与图片的关联信息
@Transactional
public ResponseResult addCoursePic(String courseId, String pic) {
//有了就更新,没有就新添;
// 课程图片的信息
CoursePic coursePic = null;
// 查询课程图片
Optional<CoursePic> picOptional = coursePicRepository.findById(courseId);
if (picOptional.isPresent()) {
coursePic = picOptional.get();
}
if (coursePic==null){
coursePic = new CoursePic();
}
coursePic.setPic(pic);
coursePic.setCourseid(courseId);
coursePicRepository.save(coursePic);
return new ResponseResult(CommonCode.SUCCESS);
}
前端开发
前端需要在上传图片成功之后保存课程图片信息
Api方法
在前端的config文件中的course.js中进行方法调用
//保存课程图片地址到课程数据 库
export const addCoursePic= (courseId,pic) => {
return http.requestPost(apiUrl+'/course/coursepic/add?courseId='+courseId+"&pic="+pic)
}
图片查询
需求分析
课程图片上传成功,再次进入课程上传页面应该显示出来已上传的图片.
APi
在课程管理服务定义查询方法
页面测试
1.before-remove钩子方法
在upload组件的before-remove钩子方法中,实现删除动作.
befrore-remove说明:删除文件之前的钩子,参数为上传文文件和文件列表,若返回false或者返回Pormise,且被reject,则停止删除.
Promise对象在处理过程中有三种状态:
pending:进行中
resolved:操作成功
rejected:操作失败
Promise的构建方法如下:
定义一个Promise对象非常简单,new一个对象就可以了
function功能里面有两个参数,resolve是执行成功调用的参数,
reject是执行失败调用的参数
Promise是专门用于异步请求的操作方法
上边的构造方法function(reslve,reject)执行的流程如下:
1.方法执行一些业务逻辑.
2.如果操作成功,Promise的状态由pending变为resolved,并将操作结果传出去.
3.如果操作失败,会将Promise的状态由pending变为rejected,并将失败结果传出.
上边说的操作成功将操作结果传给谁了呢,操作失败将失败结果传给谁了呢?
通过promise的then,catch来指定