实战 FastDFS Java 客户端上传文件

彭楷淳发布于 2020-12-23
预计阅读时间 7 分钟
总计 1.3k
浏览

FastDFS 服务端安装参考文章:分布式文件系统 FastDFS 服务端的部署

部署 FastDFS Java 客户端


先从 GitHub 上将项目源码克隆下来:

1
$ git clone https://github.com/happyfish100/fastdfs-client-java.git

部署到 Maven 依赖私服:

1
$ mvn clean install deploy

Spring Boot 整合 FastDFS Java


引入依赖

pom.xml 中引入刚才部署的依赖 fastdfs-client-java,版本酌情修改:

1
2
3
4
5
6
7
<!-- FastDFS Begin -->
<dependency>
<groupId>org.csource</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27-SNAPSHOT</version>
</dependency>
<!-- FastDFS End -->

相关配置

application.yml 中添加 FastDFS 服务端配置,方便后续 Java 配置中调用:

此处假设 FastDFS 服务器地址为:http://192.168.1.100:8888

FastDFS 跟踪器 tracker 地址为:http://192.168.1.100:22122

1
2
3
4
5
6
7
8
9
fastdfs:
base:
# FastDFS 服务器地址
url: http://192.168.1.100:8888/
storage:
type: fastdfs
fastdfs:
# FastDFS 跟踪器 tracker 地址
tracker_server: http://192.168.1.100:22122

定义文件存储接口

创建 StorageService 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.antoniopeng.fastdfs.service.upload;

/**
* 文件存储服务接口
*/
public interface StorageService {

/**
* 上传文件
*
* @param data 文件的二进制内容
* @param extName 扩展名
* @return 上传成功后返回生成的文件 id;失败返回 null
*/
public String upload(byte[] data, String extName);

/**
* 删除文件
*
* @param fileId 被删除的文件id
* @return 删除成功后返回 0,失败后返回错误代码
*/
public int delete(String fileId);
}

实现文件存储接口

添加 FastDFSStorageService 实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package com.antoniopeng.fastdfs.service.upload;

import org.csource.common.NameValuePair;
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.StorageClient1;
import org.csource.fastdfs.StorageServer;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerGroup;
import org.csource.fastdfs.TrackerServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

/**
* 文件存储服务实现
*/
public class FastDFSStorageService implements StorageService, InitializingBean {

private static final Logger logger = LoggerFactory.getLogger(FastDFSStorageService.class);

private TrackerClient trackerClient;

@Value("${storage.fastdfs.tracker_server}")
private String trackerServer;

@Override
public String upload(byte[] data, String extName) {

TrackerServer trackerServer = null;
StorageServer storageServer = null;
StorageClient1 storageClient1 = null;
try {
NameValuePair[] meta_list = null; // new NameValuePair[0];

trackerServer = trackerClient.getConnection();
if (trackerServer == null) {
logger.error("getConnection return null");
}
storageServer = trackerClient.getStoreStorage(trackerServer);
storageClient1 = new StorageClient1(trackerServer, storageServer);
String fileid = storageClient1.upload_file1(data, extName, meta_list);
logger.debug("uploaded file <{}>", fileid);
return fileid;
} catch (Exception ex) {
logger.error("Upload fail", ex);
return null;
} finally {
if (storageServer != null) {
try {
storageServer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (trackerServer != null) {
try {
trackerServer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
storageClient1 = null;
}
}

@Override
public int delete(String fileId) {

TrackerServer trackerServer = null;
StorageServer storageServer = null;
StorageClient1 storageClient1 = null;
int index = fileId.indexOf('/');
String groupName = fileId.substring(0, index);
try {
trackerServer = trackerClient.getConnection();
if (trackerServer == null) {
logger.error("getConnection return null");
}
storageServer = trackerClient.getStoreStorage(trackerServer, groupName);
storageClient1 = new StorageClient1(trackerServer, storageServer);
int result = storageClient1.delete_file1(fileId);
return result;
} catch (Exception ex) {
logger.error("Delete fail", ex);
return 1;
} finally {
if (storageServer != null) {
try {
storageServer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (trackerServer != null) {
try {
trackerServer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
storageClient1 = null;
}
}

@Override
public void afterPropertiesSet() throws Exception {

File confFile = File.createTempFile("fastdfs", ".conf");
PrintWriter confWriter = new PrintWriter(new FileWriter(confFile));
confWriter.println("tracker_server=" + trackerServer);
confWriter.close();
ClientGlobal.init(confFile.getAbsolutePath());
confFile.delete();
TrackerGroup trackerGroup = ClientGlobal.g_tracker_group;
trackerClient = new TrackerClient(trackerGroup);

logger.info("Init FastDFS with tracker_server : {}", trackerServer);
}
}

创建文件存储工厂类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package com.antoniopeng.fastdfs.service.upload;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;

import java.util.HashMap;
import java.util.Map;

/**
* 文件存储服务工厂类
*/
public class StorageFactory implements FactoryBean<StorageService> {

@Autowired
private AutowireCapableBeanFactory acbf;

/**
* 存储服务的类型,目前仅支持fastdfs
*/
@Value("${storage.type}")
private String type;

private Map<String, Class<? extends StorageService>> classMap;

public StorageFactory() {
classMap = new HashMap<>();
classMap.put("fastdfs", FastDFSStorageService.class);
}

@Override
public StorageService getObject() throws Exception {
Class<? extends StorageService> clazz = classMap.get(type);
if (clazz == null) {
throw new RuntimeException("Unsupported storage type [" + type + "], valid are " + classMap.keySet());
}

StorageService bean = clazz.newInstance();
acbf.autowireBean(bean);
acbf.initializeBean(bean, bean.getClass().getSimpleName());
return bean;
}

@Override
public Class<?> getObjectType() {
return StorageService.class;
}

@Override
public boolean isSingleton() {
return true;
}
}

配置文件存储工厂类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.antoniopeng.fastdfs.service.upload;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* Java 配置方式定义 StorageFactory 的 Bean 使其可以被依赖注入
*/
@Configuration
public class FastDFSConfiguration {
@Bean
public StorageFactory storageFactory() {
return new StorageFactory();
}
}

创建 FastDFS Client 访问控制器


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package com.antoniopeng.fastdfs.controller.upload;

import com.antoniopeng.fastdfs.service.upload.StorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
public class UploadController {

@Value("${fastdfs.base.url}")
private String FASTDFS_BASE_URL;

@Autowired
private StorageService storageService;

/**
* 文件上传
*
* @param multipartFile 单文件
* @param multipartFiles 多文件
* @return
*/
@RequestMapping(value = "upload", method = RequestMethod.POST)
public Map<String, Object> upload(MultipartFile multipartFile, MultipartFile[] multipartFiles) {
Map<String, Object> result = new HashMap<>();

// 单文件上传
if (multipartFile != null) {
result.put("fileName", writeFile(multipartFile));
}

// 多文件上传
if (multipartFiles != null && multipartFiles.length > 0) {
List<String> fileNames = new ArrayList<>();

for (MultipartFile file : multipartFiles) {
fileNames.add(writeFile(file));
}

result.put("errno", 0);
result.put("data", fileNames);
}

return result;
}

/**
* 将图片写入指定目录
*
* @param multipartFile
* @return 返回文件完整路径
*/
private String writeFile(MultipartFile multipartFile) {
// 获取文件后缀
String oName = multipartFile.getOriginalFilename();
String extName = oName.substring(oName.lastIndexOf(".") + 1);

// 文件存放路径
String url = null;
try {
String uploadUrl = storageService.upload(multipartFile.getBytes(), extName);
url = FASTDFS_BASE_URL + uploadUrl;
} catch (IOException e) {
e.printStackTrace();
}

// 返回文件完整路径
return url;
}
}

更多干货请移步:https://antoniopeng.com


如果你喜欢这个博客或发现它对你有用,欢迎你点击右下角 “OPEN CHAT” 进行评论。也欢迎你分享这个博客,让更多的人参与进来。如果在博客中使用的图片侵犯了您的版权,请联系博主删除它们。谢谢你!