为什么使用它?
1. 在分布式集群环境下,文件上传至节点A,这时通过负载均衡算法,访问到节点B,则不能访问到文件,这时会出现有时能访问有时不能访问的问题 2. 同时要考虑为文件做冗余备份、负载均衡、线性扩容等功能,这些都是单节点文件上传所不具备的
FastDFS体系结构
上传流程
客户端上传文件后存储服务器将文件 ID 返回给客户端,此文件 ID 用于以后访问该文件的索引信息。文件索引信息
包括:组名,虚拟磁盘路径,数据两级目录,文件名。
group1/M00/00/00/rBcRCl_bcZaAFW_dAABKuOa99Po198.png
我们使用Docker安装FastDFS
拉取镜像
docker pull morunchang/fastdfs
运行tracker
docker run -d --name tracker --net=host morunchang/fastdfs sh tracker.sh
运行storage
docker run -d --name storage --net=host -e TRACKER_IP=<your tracker server address>:22122 -e GROUP_NAME=<group name> morunchang/fastdfs sh storage.sh
上面是示例,下面是实例,记得换你的公网ip (比如我的是 39.97.248.11)
docker run -d --name storage --net=host -e TRACKER_IP=39.97.248.11:22122 -e GROUP_NAME=group1 morunchang/fastdfs sh storage.sh
使用的网络模式是–net=host,host模式可以不用映射容器端口宿主机, 替换为你机器的Ip即可 是组名,即storage的组 如果想要增加新的storage服务器,再次运行该命令,注意更换 新组名
修改nginx的配置
进入storage的容器内部,修改nginx.conf
docker exec -it storage /bin/bash
进入后
vi /etc/nginx/conf/nginx.conf
添加以下内容(它默认已经存在,不用改)
location ~ /M00 { root /data/fast_data/data; ngx_fastdfs_module; }
禁止缓存:(这个一定要加,加在 ngx_fastdfs_module;下面哈)
add_header Cache-Control no-store;
(5)退出容器
exit
(6)重启storage容器
docker restart storage
查看tracker.conf和storage.conf配置文件
docker exec -it storage /bin/bash cd /etc/fdfs vim tracker.conf vim storage.conf
创建文件上传微服务upload-service,通过fastdfs-client组件实现文件上传和删除的功能
(1)在spring cloud项目中创建子工程,修改pom.xml,引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>zx-springcloud</artifactId>
<groupId>com.zx</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>upload-service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- FastDFS依赖 -->
<dependency>
<groupId>com.github.tobato</groupId>
<artifactId>fastdfs-client</artifactId>
<version>1.26.7</version>
</dependency>
</dependencies>
</project>
(2)application.yml
server:
port: 9004
spring:
application:
name: upload-service
servlet:
multipart:
enabled: true
max-file-size: 10MB #单个文件上传大小
max-request-size: 20MB #总文件上传大小
fdfs:
# 链接超时
connect-timeout: 60
# 读取时间
so-timeout: 60
# 生成缩略图参数
thumb-image:
width: 150
height: 150
tracker-list: 39.97.248.11:22122 #你的公网ip
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
instance:
#更倾向于使用ip地址,而不是主机名
prefer-ip-address: true
#ip地址
ip-address: 127.0.0.1
#续约间隔,默认30秒
lease-renewal-interval-in-seconds: 5
#服务的实效时间, 默认90秒
lease-expiration-duration-in-seconds: 5
(3)启动类UploadApplication
package com.zx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class UploadApplication {
public static void main(String[] args) {
SpringApplication.run(UploadApplication.class, args);
}
}
(4) FastDfs配置类DfsConfig
package com.zx.upload.config;
import com.github.tobato.fastdfs.FdfsClientConfig;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableMBeanExport;
import org.springframework.context.annotation.Import;
import org.springframework.jmx.support.RegistrationPolicy;
@Configuration
@Import(FdfsClientConfig.class)
public class DfsConfig {
}
工具类FileDfsUtil
调用fastdfs-client工具方法实现文件上传和删除
package com.zx.upload.config;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
@Component
public class FileDfsUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(FileDfsUtil.class);
@Resource
private FastFileStorageClient storageClient ;
/**
* 上传文件
*/
public String upload(MultipartFile multipartFile) throws Exception{
String originalFilename = multipartFile.getOriginalFilename().
substring(multipartFile.getOriginalFilename().
lastIndexOf(".") + 1);
StorePath storePath = this.storageClient.uploadImageAndCrtThumbImage(
multipartFile.getInputStream(),
multipartFile.getSize(),originalFilename , null);
return storePath.getFullPath() ;
}
/**
* 删除文件
*/
public void deleteFile(String fileUrl) {
if (StringUtils.isEmpty(fileUrl)) {
LOGGER.info("fileUrl == >>文件路径为空...");
return;
}
try {
StorePath storePath = StorePath.parseFromUrl(fileUrl);
storageClient.deleteFile(storePath.getGroup(), storePath.getPath());
} catch (Exception e) {
LOGGER.info(e.getMessage());
}
}
}
FileController
创建文件上传和删除功能的controller,实现文件删除
package com.zx.controller;
import com.zx.config.FileDfsUtil;
import io.swagger.annotations.Api;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
/**
* @Author CaesarChang张旭
* @Date 2020/12/17 9:15 上午
* @Version 1.0
*/
@RestController
@Api(tags={"文件管理类"})
public class FileController {
@Autowired
private FileDfsUtil fileDfsUtil;
/**
* 上传文件
* @param file
* @return
*/
@RequestMapping(value = "/uploadFile", method = RequestMethod.POST, headers = "content-type=multipart/form-data")
public ResponseEntity<String> uploadFile(@RequestParam(value = "file") MultipartFile file) {
String result = null;
try {
String path = fileDfsUtil.upload(file);
if (StringUtils.isEmpty(path)) {
result = "上传失败";
return new ResponseEntity<>(result, HttpStatus.resolve(400));
} else {
result=path;
return new ResponseEntity<>(result, HttpStatus.resolve(200));
}
} catch (IOException e) {
e.printStackTrace();
return new ResponseEntity<>("服务器异常", HttpStatus.resolve(400));
}
}
/**
* 删除文件
* @param filePathName
* @return
*/
@RequestMapping(value = "/deleteByPath",method = RequestMethod.GET)
public ResponseEntity<String> deleteByPath(String filePathName) {
fileDfsUtil.deleteFile(filePathName);
return new ResponseEntity<>("删除成功", HttpStatus.resolve(200));
}
}
postman测试
上传测试
删除测试
拜了个拜!么么哒