实战 FastDFS Java 客户端上传文件

Posted by 彭楷淳 on 2020-12-23
Estimated Reading Time 7 Minutes
Words 1.3k In Total
Viewed Times

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


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !