只粘贴重要代码,其余代码看源码地址!
1、pom.xml 依赖。FastDFS 我们使用 com.github.tobato 封装好的 fastdfs-client,同时必须加上 commons-fileupload 依赖,否则报错:ClassNotFoundException DiskFileItemFactory
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.study</groupId>
<artifactId>study-fastdfs</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
</parent>
<!-- 统一配置版本 -->
<properties>
<fastdfs.version>1.27.2</fastdfs.version>
<commons-fileupload.version>1.4</commons-fileupload.version>
<fastjson.version>1.2.75</fastjson.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>
<!-- fastdfs 依赖:https://mvnrepository.com/artifact/com.github.tobato/fastdfs-client -->
<dependency>
<groupId>com.github.tobato</groupId>
<artifactId>fastdfs-client</artifactId>
<version>${fastdfs.version}</version>
</dependency>
<!-- 要加上 commons-fileupload,否则报错:ClassNotFoundException DiskFileItemFactory-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons-fileupload.version}</version>
</dependency>
<!-- lombok 依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 阿里巴巴 fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
</dependencies>
</project>
2、controller 层
package com.study.controller;
import com.alibaba.fastjson.JSON;
import com.study.message.ResponseMsg;
import com.study.service.FileService;
import com.study.util.NetworkUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.net.URL;
/**
* @author biandan
* @description 文件上传
* @signature 让天下没有难写的代码
* @create 2021-06-09 上午 12:07
*/
@RestController
@RequestMapping("/file")
public class FastDFSController {
@Autowired
private FileService fileService;
/**
* 文件上传
*
* @param request
* @return
*/
@RequestMapping("/upload")
public ResponseMsg upload(HttpServletRequest request) {
//单纯的获取一些信息,与业务逻辑无关。如果想要这些信息搞事情,也是可以的
try {
String referer = request.getHeader("Referer");
System.out.println("referer=" + referer);
String ipAddress = NetworkUtil.getIpAddress(request);
System.out.println("ipAddress=" + ipAddress);
URL url = new URL(referer);
String host = url.getHost();
System.out.println("host=" + host);
} catch (Exception e) {
e.printStackTrace();
}
Long startTime = System.currentTimeMillis();//记录开始时间
ResponseMsg responseMsg = fileService.upload(request);
Long endTime = System.currentTimeMillis();//记录结束时间
System.out.println("上传文件共花费时间:" + ((endTime - startTime) / 1000) + " 秒");
System.out.println("responseMsg=" + JSON.toJSONString(responseMsg));
return responseMsg;
}
}
3、实现类
package com.study.service.impl;
import com.alibaba.fastjson.JSON;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.study.entity.FileResponseEntity;
import com.study.message.ResponseMsg;
import com.study.service.FileService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @author biandan
* @description
* @signature 让天下没有难写的代码
* @create 2021-06-09 上午 12:17
*/
@Service
public class FileServiceImpl implements FileService {
//FastDFS 客户端
@Autowired(required = false)
private FastFileStorageClient fastFileStorageClient;
//使用的组,这里默认是 group1
@Value("${fastdfs.group:group1}")
private String group;
//访问的域名
@Value("${fastdfs.domain}")
private String domain;
/**
* 上传文件
*
* @param request
* @return
*/
@Override
public ResponseMsg upload(HttpServletRequest request) {
ResponseMsg responseMsg = new ResponseMsg();
responseMsg.setCode("-1");
responseMsg.setMessage("upload file error!");
try {
List<FileResponseEntity> list = dealWithFile(request);
if (!CollectionUtils.isEmpty(list)) {
responseMsg.setData(list);
responseMsg.setCode("0");
responseMsg.setMessage("success");
}
} catch (Exception e) {
e.printStackTrace();
}
return responseMsg;
}
/**
* 处理文件
*
* @param request
* @return
*/
private List<FileResponseEntity> dealWithFile(HttpServletRequest request) throws Exception {
List<FileResponseEntity> list = new ArrayList<>();
//将上下文的请求给 CommonsMultipartResolver (多部分解析器)
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
//判断 form 表单中是否有多文件 enctype="multipart/form-data"
if (multipartResolver.isMultipart(request)) {
//将请求 request 变成多部分 request
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
//获取 multipartRequest 所有文件名
Iterator iterator = multipartRequest.getFileNames();
while (iterator.hasNext()) {
//一次性遍历所有文件
MultipartFile file = multipartRequest.getFile(iterator.next().toString());
if (null != file && !file.isEmpty()) {
FileResponseEntity responseEntity = new FileResponseEntity();
//使用 FastDFS 客户端上传文件
StorePath storePath = fastFileStorageClient.uploadFile(group, file.getInputStream(), file.getSize(), getFileExt(file.getOriginalFilename()));
System.out.println("storePath=" + JSON.toJSONString(storePath));
responseEntity.setOriginalName(file.getOriginalFilename());
//拼接完整路径
String fullPath = domain + storePath.getFullPath();
responseEntity.setTargetName(StringUtils.right(fullPath, fullPath.length() - fullPath.lastIndexOf("/") - 1));
responseEntity.setFullPath(fullPath);
list.add(responseEntity);
}
}
}
return list;
}
/**
* 获取文件扩展名
*
* @param fileName
* @return
*/
private String getFileExt(String fileName) {
String extName = "";
if (StringUtils.isNotBlank(fileName) && fileName.lastIndexOf(".") != -1) {
extName = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());
}
return extName;
}
}
4、解决 JMX 重复注册 Bean 的问题
package com.study.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;
/**
* @author biandan
* @description
* @signature 让天下没有难写的代码
* @create 2021-06-09 上午 1:14
*/
@Configuration
@Import(FdfsClientConfig.class)
// 处理 JMX 重复注册 Bean 的问题
@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
public class FastDfsConfig {
}
5、解决跨域请求问题
package com.study.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* @author biandan
* @description 解决跨域
* @signature 让天下没有难写的代码
* @create 2021-06-09 上午 11:08
*/
@Configuration
public class CrossConfiguration {
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true); // 允许cookies跨域
config.addAllowedOrigin("*");// #允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
config.addAllowedHeader("*");// #允许访问的头信息,*表示全部
config.setMaxAge(18000L);// 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
config.addAllowedMethod("*");// 允许提交请求的方法,*表示全部允许
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
6、前端测试代码,需要引入 jquery.min.js (直接从 IDEA 里使用浏览器打开即可进入测试页面!)
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>FastDFS分布式文件服务器</title>
<script src="js/jquery.min.js"></script>
<script type="text/javascript">
var baseUrl = "http://127.0.0.1/";
//文件上传接口
function uploadFile() {
var formData = new FormData();
formData.append("file", $("#upload")[0].files[0]);
var uploadPath = baseUrl + "file/upload/";
$.ajax({
url: uploadPath,
type: 'POST',
data: formData,
xhrFields: {
'Access-Control-Allow-Origin': '*',
withCredentials: true
},
contentType: false,
processData: false,
beforeSend: function () {
console.log("正在上传,请稍候...");
},
success: function (data) {
console.log("结果:" + JSON.stringify(data));
$("#resultArea").val(JSON.stringify(data));
if (data.code === '0') {
$("#opResult").text("");
$("#opResult").css("color", "blue");
$("#opResult").text("操作成功!");
} else {
$("#opResult").text("");
$("#opResult").css("color", "red");
$("#opResult").text("操作失败!");
}
},
error: function (data) {
console.log("结果:" + JSON.stringify(data));
$("#resultArea").val(JSON.stringify(data));
if (data.code === '0') {
$("#opResult").text("");
$("#opResult").css("color", "blue");
$("#opResult").text("操作成功!");
} else {
$("#opResult").text("");
$("#opResult").css("color", "red");
$("#opResult").text("操作失败!");
}
}
});
}
</script>
</head>
<body style="width: 960px;margin:0px 150px;">
<h1>FastDFS分布式文件上传</h1>
<br>
<div>
<input id="upload" type="file" name="file" value="选择文件"/>
<input id="materiel" type="hidden" name="materiel" value="">
<button onclick="uploadFile()">上传文件</button>
</div>
<br><br>
<span>结果:</span><span id="opResult"></span><br>
<textarea id="resultArea" cols="120" rows="6"></textarea><br><br>
</body>
</html>
7、application.yml 配置
server:
port: 80
# 将SpringBoot项目作为单实例部署调试时,不需要注册到注册中心
eureka:
client:
fetch-registry: false
register-with-eureka: false
spring:
application:
name: fastdfs-server
# servlet 相关配置
# SpringBoot 1.5版本是spring.http.multipart
# SpringBoot 2.0版本是spring.servlet.multipart
servlet:
multipart:
max-file-size: 50MB # 最大文件限制大小,默认1M
max-request-size: 50MB #最大请求大小,默认10M
enabled: true
# 分组的配置
fastdfs:
group: group1
domain: http://file.study.com/
# 分布式文件系统 FastDFS 配置
fdfs:
so-timeout: 100000 #读取时间
connect-timeout: 100000 # 连接超时时间
tracker-list: # tracker 服务配置列表
- 192.168.0.105:22122
# 连接池配置
pool:
#从连接池中借出的最大数目
max-total: 1000
#取连接时的最大等待毫秒数
max-wait-millis: 600000
OK,启动测试。
后台输出