图片统一处理
- 为什么要统一处理
微服务中服务集群后,文件夹资源不能共享
解决方案:分布式文件系统
分布式文件系统:多个文件系统通过管理软件进行管理,得到分布式文件系统.
好处:
- 海量存储
- 高可用
方案选择
方案1:租用别人已经搭建好了的.
阿里云对象存储(收费),七牛云(10G内免费)
好处:方便,小量数据可以
坏处:大量数据时,要花很多钱.
方案2:自己搭建
hdfs(hadoop),FastDfs(国产,小文件)....
我们这里使用Fastdfs
Fastdfs介绍
FastDFS 是用 c 语言编写的一款开源的分布式文件系统。FastDFS 为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用 FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
FastDFS 架构包括 Tracker server 和 Storage server。
客户端请求 Tracker server 进行文件上传、下载,通过 Tracker server 调度最终由 Storage server 完成文件上传和下载。
具体实现步骤;
- 搭建fastDfs文件系统
- 上传图片
- 回显图片
上传和下载流程
客户端上传文件后存储服务器将文件 ID 返回给客户端,此文件 ID 用于以后访问该文件的索引信息。文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名。
客户端上传文件后存储服务器将文件 ID 返回给客户端,此文件 ID 用于以后访问该文件的索引信息。文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名。
组名:文件上传后所在的 storage 组名称,在文件上传成功后有 storage 服务器返回,需要客户端自行保存。
虚拟磁盘路径:storage 配置的虚拟路径,与磁盘选项 store_path*对应。如果配置了
store_path0 则是 M00,如果配置了 store_path1 则是 M01,以此类推。
数据两级目录:storage 服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据
文件。
文件名:与文件上传时不同。是由存储服务器根据特定信息生成,文件名包含:源存储
服务器 IP 地址、文件创建时间戳、文件大小、随机数和文件拓展名等信息。
下载
Fastdfs搭建-一个运维
代码操作fastdfs-入门
需求:将本地图片上传至图片服务器,再控制台打印url
(1)pom.xml中引入
<!-- https://mvnrepository.com/artifact/cn.bestwu/fastdfs-client-java -->
<dependency>
<groupId>cn.bestwu</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27</version>
</dependency>
(2)添加配置文件fdfs_client.conf
tracker_server=122.51.119.246:22122
(3)创建java类,main方法代码如下:
// 1、加载配置文件,配置文件中的内容就是 tracker 服务的地址。
ClientGlobal.init("D:/maven_work/fastDFS-demo/src/fdfs_client.conf");
// 2、创建一个 TrackerClient 对象。直接 new 一个。
TrackerClient trackerClient = new TrackerClient();
// 3、使用 TrackerClient 对象创建连接,获得一个 TrackerServer 对象。
TrackerServer trackerServer = trackerClient.getConnection();
// 4、创建一个 StorageServer 的引用,值为 null
StorageServer storageServer = null;
// 5、创建一个 StorageClient 对象,需要两个参数 TrackerServer 对象、StorageServer 的引用
StorageClient storageClient = new StorageClient(trackerServer, storageServer);
// 6、使用 StorageClient 对象上传图片。
//扩展名不带“.”
String[] strings = storageClient.upload_file("D:/pic/benchi.jpg", "jpg",
null);
// 7、返回数组。包含组名和图片的路径。
for (String string : strings) {
System.out.println(string);
}
控制台输出如下结果:
group1
M00/00/00/wKgZhVkMP4KAZEy-AAA-tCf93Fo973.jpg
在浏览器输入:
http://122.51.119.246/group1/M00/00/00/wKgZhVkMP4KAZEy-AAA-tCf93Fo973.jp
后台上传服务
导入jar
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Eureka 客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--配置中心支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
配置yml
Bootstrap.yml
spring:
profiles:
active: dev
cloud:
config:
name: application-common #github上面名称
profile: ${spring.profiles.active} #环境 java -jar -D xxx jar
label: master #分支
discovery:
enabled: true #从eureka上面找配置服务
service-id: hrm-config-server #指定服务名
#uri: http://127.0.0.1:1299 #配置服务器 单机配置
eureka: #eureka不能放到远程配置中
client:
service-url:
defaultZone: http://localhost:1010/eureka #告诉服务提供者要把服务注册到哪儿 #单机环境
instance:
prefer-ip-address: true #显示客户端真实ip
配置库 application-common-dev.yml
server:
port: 2090
spring:
application:
name: hrm-common
通过网关访问的配置:
zuul:
routes:
sysmanage.serviceId: hrm-sysmanage #这是调用满足条件的服务名,注意要小写
sysmanage.path: /sysmanage/** #这是所有路径前的通配
common.serviceId: hrm-common #这是调用满足条件的服务名,注意要小写
common.path: /common/** #这是所有路径前的通配
course.serviceId: hrm-course #这是调用满足条件的服务名,注意要小写
course.path: /course/** #这是所有路径前的通配
ignored-services: "*" #用*来通配符,忽略从9527端口通过服务名来调用
prefix: "/services" #这是所有路径的前缀
retryable: true #是否重试
Swagger
@Configuration
@EnableSwagger2
public class Swagger2 {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
//对外暴露服务的包,以controller的方式暴露,所以就是controller的包.
.apis(RequestHandlerSelectors.basePackage("com.zhanglin.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("公共服务api")
.description("公共服务接口文档说明")
.contact(new Contact("zltest", "", "zl@itsource.cn"))
.version("1.0")
.build();
}
}
入口类
@SpringBootApplication
@EnableEurekaClient
public class CommonService2090Application {
public static void main(String[] args) {
SpringApplication.run(CommonService2090Application .class, args);
}
}
测试
http://localhost:1030/swagger-ui.html
实现fastdfs服务(配置参照上面)
@RestController
public class FastDfsController {
@RequestMapping(value = "/upload",method = RequestMethod.POST)
public AjaxResult upload(@RequestParam(value = "file",required = true)MultipartFile file){
try {
System.out.println(file.getOriginalFilename() + ":" + file.getSize());
String originalFilename = file.getOriginalFilename();
// xxx.jpg
String extName = originalFilename.substring(originalFilename.lastIndexOf(".")+1);
System.out.println(extName);
String filePath = FastDfsApiOpr.upload(file.getBytes(), extName);
return AjaxResult.me().setResultObj(filePath);
} catch (IOException e) {
e.printStackTrace();
return AjaxResult.me().setSuccess(false).setMessage("上传失败!"+e.getMessage());
}
}
// /goup1/xxxxx/yyyy
@RequestMapping(value = "/del",method = RequestMethod.DELETE)
public AjaxResult upload(@RequestParam(value = "filePath",required = true)String filePath){
String pathTmp = filePath.substring(1); // goup1/xxxxx/yyyy
String groupName = pathTmp.substring(0, pathTmp.indexOf("/")); //goup1
String remotePath = pathTmp.substring(pathTmp.indexOf("/"));// /xxxxx/yyyy
FastDfsApiOpr.delete(groupName, remotePath);
return AjaxResult.me();
}
}
前端文件上传
<el-upload
class="upload-demo"
action="http://127.0.0.1:9527/services/common/upload"
:on-preview="handlePreview"
:on-remove="handleRemove"
:file-list="fileList2"
:on-success="handleSuccess"
list-type="picture"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
</el-upload>
缩略图数据:
课程中心功能分析
课程中心-后台服务搭建
创建模块
父工程:hrm_course_parent
子工程:
hrm_course_common: domain,query,client feign接口(内部服务调用)
hrm_course_service: controller,service,mapper
common:
导入pom
<dependencies>
<!--不能直接依赖starter,有自动配置,而消费者是不需要额。-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.itsource.agiou</groupId>
<artifactId>basic_util</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
Service:
<dependencies>
<!--所有provider公共依賴-->
<dependency>
<groupId>cn.itsource.hrm</groupId>
<artifactId>course_interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Eureka 客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--配置中心支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
</dependencies>
配置yml
Bootstrap.yml
spring:
profiles:
active: dev
cloud:
config:
name: application-common #github上面名称
profile: ${spring.profiles.active} #环境 java -jar -D xxx jar
label: master #分支
discovery:
enabled: true #从eureka上面找配置服务
service-id: hrm-config-server #指定服务名
#uri: http://127.0.0.1:1299 #配置服务器 单机配置
eureka: #eureka不能放到远程配置中
client:
service-url:
defaultZone: http://localhost:1010/eureka #告诉服务提供者要把服务注册到哪儿 #单机环境
instance:
prefer-ip-address: true #显示客户端真实ip
配置库 application-common-dev.yml
server:
port: 2090
spring:
application:
name: hrm-common
配置网关,swagger,入口类
课程中心
课程类型树
树形数据无限极获取
controller
@GetMapping("/treeData")
public List<CourseType> treeData() {
return courseTypeService.treeData(0L);
}
service:
/**
* 获取多级菜单
* @param pid
* @return
*/
List<CourseType> treeData(long pid);
@Service
public class CourseTypeServiceImpl extends ServiceImpl<CourseTypeMapper, CourseType> implements ICourseTypeService {
@Autowired
private CourseTypeMapper courseTypeMapper;
@Override
public List<CourseType> treeData(long pid) {
return treeDataRecursion(pid);
}
//递归思想
private List<CourseType> treeDataRecursion(long pid) {
List<CourseType> children = courseTypeMapper.selectList(new EntityWrapper<CourseType>().eq("pid", pid));
//返回条件
if (children==null || children.size()<1){
return null;
}
for (CourseType child : children) {
//自己调用自己
List<CourseType> cTmp = treeDataRecursion(child.getId());
child.setChildren(cTmp);
}
return children;
}