springboot整合fastdfs做文件上传

在项目开发中经常会碰到做文件上传的功能,一般来说,文件上传的步骤就那么几步,前台通过提交一个选中的文件,后端对文件做处理然后将文件上传至指定的地址,这个地址是一个真实的物理存储路径,可以是本地,也可以是fastdfs等其他的linux文件服务器。

我们通过F12可以发现,一些比较大的电商网站里的图片,直接复制图片的url是可以预览并下载下来的,从图片的URL地址规律大概可以推测,这些图片其实是存放在某个或某些文件服务器上的,比较常用的文件服务器像fastdfs,天然支持负载均衡,支持水平扩展,而且很容易做集群。

本节,结合当下比较流行的springboot,利用springboot和fastdfs进行整合,做一个简单的文件上传处理;

环境准备,centos6.5,fastdfs安装包,eclipse

fastdfs的安装过程比较复杂,但是不难,按照相关的安装步骤即可完成安装【我会将安装过程一并上传】,此处不做演示,有兴趣的童鞋可以参考学习,其主要的模块包括两部分,tracker和storage,顾名思义,一个是跟踪器,一个用于存储具体的文件位置,为了使用方便,还需要安装ngnix的模块,

下面启动fastdfs的服务器,分3步,
1、启动tracker:
/etc/init.d/fdfs_trackerd start
在这里插入图片描述
tracker已经成功启动

2、启动storage:
/etc/init.d/fdfs_storaged start
在这里插入图片描述
storage也成功启动,

3、启动ngix模块:【此处提前已安装好】
/usr/local/nginx/sbin/nginx

3个模块都启动完毕后,可以查看一下端口是否都开启了,
在这里插入图片描述

可以看到,三个模块都已经成功开启,其中,nginx此处代理的是客户端访问fastdfs时候的端口号,我这里设置的是8888,可以自定义其他的端口,22122是tracker的端口,事实上,我们在客户端访问fastdfs服务器的时候并不是直接访问的,而是通过跟踪器tracker这个模块,由tracker做监听并定位到服务器的具体位置,然后,需要把这几个端口添加到防火墙配置文件中,接着我们可以进入到fastdfs的文件数据存放目录查看一下其结构,
在这里插入图片描述

从图中可以看出,fastdfs设计的用于管理文件数据的结构非常具有层次性,像一个复杂而有序的文件夹系统,层层管理,每一个有序的节点还有子节点,很方便运维,因此,就算是单台fastdfs服务器,就可以很方便的管理巨大的文件数据,

扫描二维码关注公众号,回复: 3491183 查看本文章

接下来就是代码部分,此处,新建一个springboot的maven 工程,项目结构如图所示,
在这里插入图片描述

pom文件:

<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.1.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>

		<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>

		<!-- thymeleaf模板插件 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>

		<!-- devtools插件,热部署 -->
		<!-- <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> 
			<optional>true</optional> <scope>true</scope> </dependency> -->

		<dependency>
			<groupId>com.github.tobato</groupId>
			<artifactId>fastdfs-client</artifactId>
			<version>1.26.1-RELEASE</version>
		</dependency>
		
	</dependencies>

这里的包就是常规的几个,另外要模拟前台上传文件,项目中主要使用了两个html文件,
file.html和success.html,第一个是文件上传的页面,第二个是上传成功后跳转的页面,
file.html代码如下,

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="http://www.thymeleaf.org"
	xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<meta charset="UTF-8" />
<title>fileUpload page</title>
</head>
<body>
	<h1 th:inlines="text">文件上传</h1>
	<form action="uploadFileToFast" method="post" enctype="multipart/form-data">
		<p>
			选择文件: <input type="file" name="fileName" />
		</p>
		<p>
			<input type="submit" value="提交" />
		</p>
	</form>
</body>
</html>

success.html代码:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="http://www.thymeleaf.org"
	xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<meta charset="UTF-8" />
<title>fileUpload page</title>
</head>
<body>
	文件上传成功 !!!
</body>
</html>

上传文件的页面效果,
在这里插入图片描述

下面请看配置文件,连接fastdfs服务器的主要配置项如下,


server.port=8082

#模拟本地上传
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/

#单个文件最大尺寸
spring.servlet.multipart.max-file-size=10mb
#一个请求文件的最大尺寸
spring.servlet.multipart.max-request-size=50mb
#设置一个文件上传的临时文件目录
spring.servlet.multipart.location=E:/opt/temp/

#FastDfs的配置	====================================
#读取inputsream阻塞时间 
fdfs.connect-timeout=600
fdfs.so-timeout=1500
#tracker地址 
fdfs.trackerList=192.168.111.130:22122 
#通过nginx 访问地址 
#fdfs.webServerUrl=http://192.168.111.130:8888/ 
#缩略图配置
fdfs.thumbImage.height=150
fdfs.thumbImage.width=150
spring.jmx.enabled=false
#通过nginx 访问地址 
fdfs.resHost=192.168.111.130
fdfs.storagePort=8888
#获取连接池最大数量 
fdfs.pool.max-total=200 

接下来需要初始化fastdfs的相关配置,以便初始化的时候做全局加载,使配置生效,这里创建了两个类,
FdfsConfig,FdfsConfiguration,
FdfsConfig主要用以连接fastdfs,FdfsConfiguration使配置生效,

@Component
public class FdfsConfig {

	@Value("${fdfs.resHost}")
	private String resHost;
	
	@Value("${fdfs.storagePort}")
	private String storagePort;

	public String getResHost() {
		return resHost;
	}

	public void setResHost(String resHost) {
		this.resHost = resHost;
	}

	public String getStoragePort() {
		return storagePort;
	}

	public void setStoragePort(String storagePort) {
		this.storagePort = storagePort;
	}
	
}
@Configuration
@EnableMBeanExport(registration=RegistrationPolicy.IGNORE_EXISTING)
public class FdfsConfiguration {

}

下面是关于使用fastdfs的客户端工具封装的几个操作fastdfs服务器的工具类,这里直接贴上代码,具体的意思可以参考官方文档,工具类在这个文件里,CommonFileUtil,

@Component
public class CommonFileUtil {
	
	private final Logger logger = LoggerFactory.getLogger(FdfsConfig.class);

	@Autowired
	private FastFileStorageClient storageClient;


	/**
	 *	MultipartFile类型的文件上传ַ
	 * @param file
	 * @return
	 * @throws IOException
	 */
	public String uploadFile(MultipartFile file) throws IOException {
		StorePath storePath = storageClient.uploadFile(file.getInputStream(), file.getSize(),
				FilenameUtils.getExtension(file.getOriginalFilename()), null);
		return getResAccessUrl(storePath);
	}

	/**
	 * 普通的文件上传
	 * 
	 * @param file
	 * @return
	 * @throws IOException
	 */
	public String uploadFile(File file) throws IOException {
		FileInputStream inputStream = new FileInputStream(file);
		StorePath path = storageClient.uploadFile(inputStream, file.length(),
				FilenameUtils.getExtension(file.getName()), null);
		return getResAccessUrl(path);
	}

	/**
	 * 带输入流形式的文件上传
	 * 
	 * @param is
	 * @param size
	 * @param fileName
	 * @return
	 */
	public String uploadFileStream(InputStream is, long size, String fileName) {
		StorePath path = storageClient.uploadFile(is, size, fileName, null);
		return getResAccessUrl(path);
	}

	/**
	 * 将一段文本文件写到fastdfs的服务器上
	 * 
	 * @param content
	 * @param fileExtension
	 * @return
	 */
	public String uploadFile(String content, String fileExtension) {
		byte[] buff = content.getBytes(Charset.forName("UTF-8"));
		ByteArrayInputStream stream = new ByteArrayInputStream(buff);
		StorePath path = storageClient.uploadFile(stream, buff.length, fileExtension, null);
		return getResAccessUrl(path);
	}

	/**
	 * 返回文件上传成功后的地址名称ַ
	 * @param storePath
	 * @return
	 */
	private String getResAccessUrl(StorePath storePath) {
		String fileUrl = storePath.getFullPath();
		return fileUrl;
	}

	/**
	 * 删除文件
	 * @param fileUrl
	 */
	public void deleteFile(String fileUrl) {
		if (StringUtils.isEmpty(fileUrl)) {
			return;
		}
		try {
			StorePath storePath = StorePath.praseFromUrl(fileUrl);
			storageClient.deleteFile(storePath.getGroup(), storePath.getPath());
		} catch (FdfsUnsupportStorePathException e) {
			logger.warn(e.getMessage());
		}
	}

	public String upfileImage(InputStream is, long size, String fileExtName, Set<MateData> metaData) {
		StorePath path = storageClient.uploadImageAndCrtThumbImage(is, size, fileExtName, metaData);
		return getResAccessUrl(path);
	}
	
}

接下来写一个控制器,FileController,用以操作文件上传,这里模拟单文件上传,如果上传成功,fastdfs会返回给客户端一个地址,这个地址就是存放上传的文件的具体位置,下面看测试代码,

@Controller
public class FileController {
	
	private final static Logger logger = LoggerFactory.getLogger(FileController.class);
	
	@Autowired
	private CommonFileUtil fileUtil;
	
	@RequestMapping("/goIndex")
	public String goIndex(){
		logger.info("进入主页面");
		return "/file";
	}
	
	@RequestMapping("/fileUpload")
	public String fileUpload(@RequestParam("fileName") MultipartFile file){
		
		String targetFilePath = "E:/opt/uploads/";
		
		if(file.isEmpty()){
			logger.info("this file is empty");
		}
		
		String newFileName = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
		//获取原来文件名称
		String fileSuffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
		
		if(!fileSuffix.equals(".jpg") || !fileSuffix.equals(".png")){
			logger.info("文件格式不正确");
		}
		//拼装新的文件名
		String targetFileName = targetFilePath + newFileName + fileSuffix;
		//上传文件
		try {
			FileCopyUtils.copy(file.getInputStream(),new FileOutputStream(targetFileName));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		return "/success";
	}
	
	//使用fastdfs进行文件上传
	@RequestMapping("/uploadFileToFast")
	public String uoloadFileToFast(@RequestParam("fileName")MultipartFile file) throws IOException{
		
		if(file.isEmpty()){
			logger.info("文件不存在");
		}
		String path = fileUtil.uploadFile(file);
		System.out.println(path);
		return "success";
	}

}

这里使用的是uoloadFileToFast这个控制器,最后启动MainApp主函数,

@SpringBootApplication
@Import(FdfsClientConfig.class)
public class MainApp {
	public static void main(String[] args) {
		SpringApplication.run(MainApp.class, args);
	}
}

启动成功,在页面的输入框选择一个本地的图片,我这里提前准备好了一张图片,
在这里插入图片描述

点击提交,可以看到,文件上传成功,可以看到,控制塔打印了一长串的字符串,其实这个就是上传的文件在fastdfs中保存的路径,
在这里插入图片描述

我们进入fastdfs的文件保存目录,可以看到,在/00/00/下面多了一个文件,这个文件就是刚刚上传的图片,注意,上传的文件保存的目录可以自己手动设置的,童鞋们不妨自己研究下怎么设置,
在这里插入图片描述

我们可以将这个返回的路径拼接上文件服务器的IP和端口,新打开一个浏览器的窗口,可以看到神奇的一幕,
http://192.168.111.130:8888/group1/M00/00/00/wKhvglu4_TqAadEtAABs63TOJBQ567.jpg,这张图片不正是我们上传的图片吗

在这里插入图片描述

没错,这样一来,我们就可以直接访问到fastdfs服务器上的这张图片了,当然,需要你把防火墙中的8888端口打开。

上述就是springboot整合fastdfs做文件上传的流程,实际业务中,我们在和页面进行交互的时候,一旦上传成功,我们需要将返回的文件地址重新包装返回给前台,前台继续做其他的处理,也可以将得到的地址存入mysql,方便其他的业务使用;

另外,温馨提示一下,在整合的过程中,遇到了一个不大不小的坑,耽误了不少的时间,大家在使用springboot整合fastdfs的时候,如果你的springboot版本是2.x的,一定要使用我上面的fastdfs版本,否则请更换springboot的版本至1.5.2,遇到这个问题后,网上的一大堆解决办法就是行不通,最后采用了更换fastdfs版本才得到解决!!

本篇相关的代码和安装文件地址:
https://download.csdn.net/download/zhangcongyi420/10704404,有需要的可以免费下载,感谢各位观看!

猜你喜欢

转载自blog.csdn.net/zhangcongyi420/article/details/82958495