+ *
+ * @author 阿伟学长
+ * @version 1.0.0
+ */
+public class App {
+ /**
+ * 处理 all 情况
+ *
+ * @param tables 表名字符串
+ * @return 处理结果
+ */
+ private static List getTables(String tables) {
+ return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
+ }
+
+ public static void main(String[] args) {
+ ResourceBundle res = ResourceBundle.getBundle("database");
+ FastAutoGenerator.create(res.getString("db.url"), res.getString("db.username"), res.getString("db.password"))
+ // 全局配置
+ .globalConfig((scanner, builder) -> builder
+ .author(scanner.apply("请输入作者名称:"))
+ .fileOverride() //允许重新文件
+ .disableOpenDir() //禁止生成成功打开文件夹
+ .outputDir(res.getString("g.output.dir")) //设置输出路径
+ )
+ // 包配置
+ .packageConfig((scanner, builder) -> builder
+ .parent(res.getString("pkg.name"))
+ .moduleName(scanner.apply("输入模块名称:"))
+ .pathInfo(Collections.singletonMap(OutputFile.mapperXml, res.getString("g.output.dir") + "\\..\\resources\\" + res.getString("pkg.xml.dir")))
+ )
+ // 策略配置
+ .strategyConfig((scanner, builder) -> builder
+ .addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔,所有输入all:")))
+ .controllerBuilder().enableRestStyle().enableHyphenStyle()
+ .mapperBuilder().enableMapperAnnotation()
+ .entityBuilder().enableLombok().addTableFills(new Column("create_time", FieldFill.INSERT))
+ .build()
+ )
+ .execute();
+ }
+}
diff --git a/psi-java/code-generator/src/main/resources/database.properties b/psi-java/code-generator/src/main/resources/database.properties
new file mode 100644
index 000000000..2f0d64ade
--- /dev/null
+++ b/psi-java/code-generator/src/main/resources/database.properties
@@ -0,0 +1,6 @@
+db.url=jdbc:mysql://43.138.223.223:3306/zopsi_sys?useUnicode=true&useSSL=false&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
+db.username=root
+db.password=psitxms9527
+g.output.dir=.\\psi-prepayment\\src\\main\\java
+pkg.name=com.zeroone.star
+pkg.xml.dir=mapper
diff --git a/psi-java/fastdfs-spring-boot-starter/LICENSE b/psi-java/fastdfs-spring-boot-starter/LICENSE
new file mode 100644
index 000000000..0a041280b
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/LICENSE
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/psi-java/fastdfs-spring-boot-starter/README.md b/psi-java/fastdfs-spring-boot-starter/README.md
new file mode 100644
index 000000000..668a52755
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/README.md
@@ -0,0 +1,143 @@
+# fastdfs-spring-boot-starter
+
+SpringBoot2.x的高性能FastDFS客户端。避免手动引入jar包导致项目混乱,提供常用的API,有助于快速上手开发。
+
+- 自动添加依赖
+- 初始化配置项
+- 基于Commons Pool2 实现的高性能连接池
+- 更多操作FastDFS的API
+- 支持多Tracker多Storage多NGINX负载均衡模式
+- 基于fastdfs-client-java(1.29-SNAPSHOT)源代码构建
+
+原作者项目地址:https://github.com/bluemiaomiao/fastdfs-spring-boot-starter.git
+
+# 一、快速开始
+
+## (1)使用Maven
+
+```xml
+
+ io.github.bluemiaomiao
+ fastdfs-spring-boot-starter
+ 2.0.1-RELEASE
+
+```
+
+## (2)使用Gradle
+
+```groovy
+compile group: 'io.github.bluemiaomiao', name: 'fastdfs-spring-boot-starter', version: '2.0.0-RELEASE'
+```
+
+# 二、手动编译
+
+## (1)克隆整个仓库
+
+```bash
+git clone https://gitee.com/zero-awei/fastdfs-spring-boot-starter.git
+cd fastdfs-spring-boot-starter
+```
+
+## (2)安装到本地
+
+```bash
+mvn clean install
+mvn source:jar install
+mvn javadoc:jar install
+```
+
+## (3)添加到项目
+
+```xml
+
+ io.github.bluemiaomiao
+ fastdfs-spring-boot-starter
+ 2.0.1-RELEASE
+
+```
+
+## (4) 在主配置类上添加注解 (``@EnableFastdfsClient``)
+
+```java
+@EnableFastdfsClient
+@SpringBootApplication
+public class DemoApplication {
+
+ @Autowired
+ private FastdfsClientService fastdfsClientService;
+
+ public static void main(String[] args) {
+ SpringApplication.run(DemoApplication.class, args);
+ }
+}
+```
+## (5) 添加配置条目(application.properties)
+
+```properties
+fastdfs.nginx-servers=192.168.80.2:8000,192.168.80.3:8000,192.168.80.4:8000
+fastdfs.tracker-servers=192.168.80.2:22122,192.168.80.3:22122,192.168.80.4:22122
+fastdfs.http-secret-key=your key
+fastdfs.http-anti-steal-token=true
+fastdfs.http-tracker-http-port=8080
+fastdfs.network-timeout=30
+fastdfs.connect-timeout=5
+fastdfs.connection-pool-max-idle=18
+fastdfs.connection-pool-min-idle=2
+fastdfs.connection-pool-max-total=18
+fastdfs.charset=UTF-8
+```
+
+## (6) 添加配置条目(application.yml)
+
+```yaml
+fastdfs:
+ charset: UTF-8
+ connect-timeout: 5
+ http-secret-key: your key
+ network-timeout: 30
+ http-anti-steal-token: true
+ http-tracker-http-port: 8080
+ connection-pool-max-idle: 20
+ connection-pool-max-total: 20
+ connection-pool-min-idle: 2
+ nginx-servers: 192.168.80.2:8000,192.168.80.3:8000,192.168.80.4:8000
+ tracker-servers: 192.168.80.2:22122,192.168.80.3:22122,192.168.80.4:22122
+```
+
+## (7) 即刻享受它带来的便利
+
+```java
+@Autowired
+private FastdfsClientService remoteService;
+
+// 上传文件
+String[] remoteInfo;
+try {
+ remoteInfo = remoteService.autoUpload(image.getBytes(), type);
+ log.info("上传的服务器分组: " + remoteInfo[0]);
+ log.info("上传的服务器ID: " + remoteInfo[1]);
+} catch (Exception e) {
+ log.error("Upload file error: " + e.getMessage());
+ return HttpStatus.INTERNAL_SERVER_ERROR;
+}
+
+// 下载文件
+String group = file.getGroup();
+String storage = file.getStorageId();
+String remoteFile = "Get file error.";
+
+try {
+ remoteFile = fastdfs.autoDownloadWithToken(group, storage, remoteAddress);
+} catch (Exception e) {
+ log.error("Get file error: " + e.getMessage());
+}
+```
+
+```java
+// 当启用防盗链机制时,需要使用该方法下载文件
+FastdfsClientService.autoDownloadWithToken(String fileGroup, String remoteFileName, String clientIpAddress)
+// 当没有启用防盗链机制时,需要使用该方法下载文件
+FastdfsClientService.autoDownloadWithoutToken(String fileGroup, String remoteFileName, String clientIpAddress)
+// 上传文件的方法
+FastdfsClientService.autoUpload(byte[] buffer, String ext)
+```
diff --git a/psi-java/fastdfs-spring-boot-starter/README_en_US.md b/psi-java/fastdfs-spring-boot-starter/README_en_US.md
new file mode 100644
index 000000000..87ca9d5ea
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/README_en_US.md
@@ -0,0 +1,144 @@
+# fastdfs-spring-boot-starter
+
+A high-performance fastdfs client compatible with springboot 2. X. To avoid the confusion caused by manual introduction of jar package, provide common API, which is helpful for rapid development.
+
+- Import dependence
+- Initial configuration
+- Connection pool
+- More method
+- Support multi tracker, multi storage, multi nginx load balancing mode
+- Based on fastdfs client Java (1.29 snapshot) source code construction
+
+Original author project address: https://github.com/bluemiaomiao/fastdfs-spring-boot-starter.git
+
+# I. Quick start
+
+## (1) Use Maven
+
+```xml
+
+ io.github.bluemiaomiao
+ fastdfs-spring-boot-starter
+ 2.0.1-RELEASE
+
+```
+
+## (2) Use Gradle
+
+```groovy
+compile group: 'io.github.bluemiaomiao', name: 'fastdfs-spring-boot-starter', version: '2.0.0-RELEASE'
+```
+
+# II. Build from source
+
+## (1) Download
+
+```bash
+git clone https://gitee.com/zero-awei/fastdfs-spring-boot-starter.git
+cd fastdfs-spring-boot-starter
+```
+
+## (2) Install to local repository
+
+```bash
+mvn clean install
+mvn source:jar install
+mvn javadoc:jar install
+```
+
+## (3) Add to project
+
+```xml
+
+ io.github.bluemiaomiao
+ fastdfs-spring-boot-starter
+ 2.0.1-RELEASE
+
+```
+
+## (4) Add annotations and service (``@EnableFastdfsClient``).
+
+```java
+@EnableFastdfsClient
+@SpringBootApplication
+public class DemoApplication {
+
+ @Autowired
+ private FastdfsClientService fastdfsClientService;
+
+ public static void main(String[] args) {
+ SpringApplication.run(DemoApplication.class, args);
+ }
+}
+```
+
+## (5) Add configuration entries(application.properties).
+
+```properties
+fastdfs.nginx-servers=192.168.80.2:8000,192.168.80.3:8000,192.168.80.4:8000
+fastdfs.tracker-servers=192.168.80.2:22122,192.168.80.3:22122,192.168.80.4:22122
+fastdfs.http-secret-key=your key
+fastdfs.http-anti-steal-token=true
+fastdfs.http-tracker-http-port=8080
+fastdfs.network-timeout=30
+fastdfs.connect-timeout=5
+fastdfs.connection-pool-max-idle=18
+fastdfs.connection-pool-min-idle=2
+fastdfs.connection-pool-max-total=18
+fastdfs.charset=UTF-8
+```
+
+## (6) Add configuration entries(application.yml).
+
+```yaml
+fastdfs:
+ charset: UTF-8
+ connect-timeout: 5
+ http-secret-key: your key
+ network-timeout: 30
+ http-anti-steal-token: true
+ http-tracker-http-port: 8080
+ connection-pool-max-idle: 20
+ connection-pool-max-total: 20
+ connection-pool-min-idle: 2
+ nginx-servers: 192.168.80.2:8000,192.168.80.3:8000,192.168.80.4:8000
+ tracker-servers: 192.168.80.2:22122,192.168.80.3:22122,192.168.80.4:22122
+```
+
+## (7) Enjoy it.
+
+```java
+@Autowired
+private FastdfsClientService remoteService;
+
+// Upload File
+String[] remoteInfo;
+try {
+ remoteInfo = remoteService.autoUpload(image.getBytes(), type);
+ log.info("File Server Group: " + remoteInfo[0]);
+ log.info("File Server ID: " + remoteInfo[1]);
+} catch (Exception e) {
+ log.error("Upload file error: " + e.getMessage());
+ return HttpStatus.INTERNAL_SERVER_ERROR;
+}
+
+// Download File
+String group = file.getGroup();
+String storage = file.getStorageId();
+String remoteFile = "Get file error.";
+
+try {
+ remoteFile = fastdfs.autoDownloadWithToken(group, storage, remoteAddress);
+} catch (Exception e) {
+ log.error("Get file error: " + e.getMessage());
+}
+```
+
+```java
+// If you use anti-hotlinking
+FastdfsClientService.autoDownloadWithToken(String fileGroup, String remoteFileName, String clientIpAddress)
+// If hotlinking is not used
+FastdfsClientService.autoDownloadWithoutToken(String fileGroup, String remoteFileName, String clientIpAddress)
+// upload file
+FastdfsClientService.autoUpload(byte[] buffer, String ext)
+```
diff --git a/psi-java/fastdfs-spring-boot-starter/_config.yml b/psi-java/fastdfs-spring-boot-starter/_config.yml
new file mode 100644
index 000000000..c4192631f
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/_config.yml
@@ -0,0 +1 @@
+theme: jekyll-theme-cayman
\ No newline at end of file
diff --git a/psi-java/fastdfs-spring-boot-starter/pom.xml b/psi-java/fastdfs-spring-boot-starter/pom.xml
new file mode 100644
index 000000000..dbd6b8090
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/pom.xml
@@ -0,0 +1,72 @@
+
+
+ 4.0.0
+
+ com.zeroone.star
+ psi-java
+ ${revision}
+ ../pom.xml
+
+ fastdfs-spring-boot-starter
+
+
+ The Apache Software License, Version 2.0
+ https://www.apache.org/licenses/LICENSE-2.0.txt
+
+
+
+ scm:git:https://github.com/bluemiaomiao/fastdfs-spring-boot-starter.git
+ scm:git:https://github.com/bluemiaomiao/fastdfs-spring-boot-starter.git
+
+ git:https://github.com/bluemiaomiao/fastdfs-spring-boot-starter.git
+
+
+
+ bluemiaomiao
+ xv2017@outlook.com
+ bluemiaomiao
+
+
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+ org.apache.commons
+ commons-pool2
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ ${maven-javadoc-plugin.version}
+
+ none
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ ${java.version}
+ ${java.version}
+ UTF-8
+
+
+
+
+
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/io/github/bluemiaomiao/annotation/EnableFastdfsClient.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/io/github/bluemiaomiao/annotation/EnableFastdfsClient.java
new file mode 100644
index 000000000..f0578b113
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/io/github/bluemiaomiao/annotation/EnableFastdfsClient.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 BlueMiaomiao
+ * FastDFS Java Client(for SpringBoot1.x & SpringBoot 2.x) may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ */
+
+package io.github.bluemiaomiao.annotation;
+
+import io.github.bluemiaomiao.autoconfiguration.FastdfsAutoConfiguration;
+import org.springframework.context.annotation.Import;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Import(FastdfsAutoConfiguration.class)
+@Documented
+public @interface EnableFastdfsClient {
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/io/github/bluemiaomiao/autoconfiguration/FastdfsAutoConfiguration.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/io/github/bluemiaomiao/autoconfiguration/FastdfsAutoConfiguration.java
new file mode 100644
index 000000000..b1abfb2e2
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/io/github/bluemiaomiao/autoconfiguration/FastdfsAutoConfiguration.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 BlueMiaomiao
+ * FastDFS Java Client(for SpringBoot1.x & SpringBoot 2.x) may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ */
+
+package io.github.bluemiaomiao.autoconfiguration;
+
+import io.github.bluemiaomiao.properties.FastdfsProperties;
+import io.github.bluemiaomiao.service.FastdfsClientService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@EnableConfigurationProperties(FastdfsProperties.class)
+public class FastdfsAutoConfiguration {
+ @Autowired
+ private FastdfsProperties fastdfsProperties;
+
+ @Bean
+ @ConditionalOnMissingBean(FastdfsClientService.class)
+ public FastdfsClientService fastdfsClientService() throws Exception {
+ return new FastdfsClientService(fastdfsProperties);
+ }
+}
\ No newline at end of file
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/io/github/bluemiaomiao/factory/StorageClientFactory.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/io/github/bluemiaomiao/factory/StorageClientFactory.java
new file mode 100644
index 000000000..c269c16ce
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/io/github/bluemiaomiao/factory/StorageClientFactory.java
@@ -0,0 +1,39 @@
+package io.github.bluemiaomiao.factory;
+
+import org.apache.commons.pool2.PooledObject;
+import org.apache.commons.pool2.PooledObjectFactory;
+import org.apache.commons.pool2.impl.DefaultPooledObject;
+import org.csource.fastdfs.StorageClient;
+import org.csource.fastdfs.TrackerClient;
+import org.csource.fastdfs.TrackerServer;
+
+// 用于创建连接对象的工厂类
+public class StorageClientFactory implements PooledObjectFactory {
+
+ @Override
+ public PooledObject makeObject() throws Exception {
+ TrackerClient client = new TrackerClient();
+ TrackerServer server = client.getConnection();
+ return new DefaultPooledObject<>(new StorageClient(server, null));
+ }
+
+ @Override
+ public void destroyObject(PooledObject p) throws Exception {
+ p.getObject().getTrackerServer().close();
+ }
+
+ @Override
+ public boolean validateObject(PooledObject p) {
+ return false;
+ }
+
+ @Override
+ public void activateObject(PooledObject p) throws Exception {
+
+ }
+
+ @Override
+ public void passivateObject(PooledObject p) throws Exception {
+
+ }
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/io/github/bluemiaomiao/properties/FastdfsProperties.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/io/github/bluemiaomiao/properties/FastdfsProperties.java
new file mode 100644
index 000000000..d4062d035
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/io/github/bluemiaomiao/properties/FastdfsProperties.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2019 BlueMiaomiao
+ * FastDFS Java Client(for SpringBoot1.x & SpringBoot 2.x) may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ */
+
+package io.github.bluemiaomiao.properties;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties(prefix = "fastdfs")
+public class FastdfsProperties {
+ // 连接超时时间
+ // 网络超时时间
+ // 字符集编码
+ // 是否使用Token
+ // Token加密密钥
+ // 跟踪器IP地址,多个使用分号隔开
+ // 连接池的连接对象最大个数
+ // 连接池的最大空闲对象个数
+ // 连接池的最小空闲对象个数
+ // Nginx服务器IP,多个使用分号分割
+ // 获取连接对象时可忍受的等待时长(毫秒)
+ private String connectTimeout = "5";
+ private String networkTimeout = "30";
+ private String charset = "UTF-8";
+ private String httpAntiStealToken = "false";
+ private String httpSecretKey = "";
+ private String httpTrackerHttpPort = "";
+ private String trackerServers = "";
+ private String connectionPoolMaxTotal = "18";
+ private String connectionPoolMaxIdle = "18";
+ private String connectionPoolMinIdle = "2";
+ private String nginxServers = "";
+
+ public void setConnectTimeout(String connectTimeout) {
+ this.connectTimeout = connectTimeout;
+ }
+
+ public void setNetworkTimeout(String networkTimeout) {
+ this.networkTimeout = networkTimeout;
+ }
+
+ public void setCharset(String charset) {
+ this.charset = charset;
+ }
+
+ public void setHttpAntiStealToken(String httpAntiStealToken) {
+ this.httpAntiStealToken = httpAntiStealToken;
+ }
+
+ public void setHttpSecretKey(String httpSecretKey) {
+ this.httpSecretKey = httpSecretKey;
+ }
+
+ public void setHttpTrackerHttpPort(String httpTrackerHttpPort) {
+ this.httpTrackerHttpPort = httpTrackerHttpPort;
+ }
+
+ public void setTrackerServers(String trackerServers) {
+ this.trackerServers = trackerServers;
+ }
+
+ public void setConnectionPoolMaxTotal(String connectionPoolMaxTotal) {
+ this.connectionPoolMaxTotal = connectionPoolMaxTotal;
+ }
+
+ public void setConnectionPoolMaxIdle(String connectionPoolMaxIdle) {
+ this.connectionPoolMaxIdle = connectionPoolMaxIdle;
+ }
+
+ public void setConnectionPoolMinIdle(String connectionPoolMinIdle) {
+ this.connectionPoolMinIdle = connectionPoolMinIdle;
+ }
+
+ public void setNginxServers(String nginxServers) {
+ this.nginxServers = nginxServers;
+ }
+
+ public String getConnectTimeout() {
+ return connectTimeout;
+ }
+
+ public String getNetworkTimeout() {
+ return networkTimeout;
+ }
+
+ public String getCharset() {
+ return charset;
+ }
+
+ public String getHttpAntiStealToken() {
+ return httpAntiStealToken;
+ }
+
+ public String getHttpSecretKey() {
+ return httpSecretKey;
+ }
+
+ public String getHttpTrackerHttpPort() {
+ return httpTrackerHttpPort;
+ }
+
+ public String getTrackerServers() {
+ return trackerServers;
+ }
+
+ public String getConnectionPoolMaxTotal() {
+ return connectionPoolMaxTotal;
+ }
+
+ public String getConnectionPoolMaxIdle() {
+ return connectionPoolMaxIdle;
+ }
+
+ public String getConnectionPoolMinIdle() {
+ return connectionPoolMinIdle;
+ }
+
+ public String getNginxServers() {
+ return nginxServers;
+ }
+}
\ No newline at end of file
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/io/github/bluemiaomiao/service/FastdfsClientService.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/io/github/bluemiaomiao/service/FastdfsClientService.java
new file mode 100644
index 000000000..cdcd6bfde
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/io/github/bluemiaomiao/service/FastdfsClientService.java
@@ -0,0 +1,1389 @@
+/*
+ * Copyright (C) 2019 BlueMiaomiao
+ * General Public License (LGPL)
+ * 为SpringBoot 1.x 和 SpringBoot2.x构建的fastDFS场景启动器
+ * 进一步分封装fastDFS中的方法,实现带有Token的方法
+ * 加入Nginx负载均衡策略
+ * 可以直接调用方法,直接返回文件请求地址
+ */
+
+package io.github.bluemiaomiao.service;
+
+import io.github.bluemiaomiao.factory.StorageClientFactory;
+import io.github.bluemiaomiao.properties.FastdfsProperties;
+import org.apache.commons.pool2.impl.GenericObjectPool;
+import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+import org.csource.common.NameValuePair;
+import org.csource.fastdfs.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+import java.util.Properties;
+
+import static java.lang.Math.abs;
+
+public class FastdfsClientService {
+
+ // SpringBoot加载的配置文件
+ // 连接池配置项
+ // 转换后的配置条目
+ // 连接池
+ // Nginx服务器地址
+ private FastdfsProperties fdfsProp;
+ private GenericObjectPoolConfig config;
+ private Properties prop;
+ private GenericObjectPool pool;
+ private String[] nginxServers;
+ private Logger logger;
+
+ public FastdfsClientService(FastdfsProperties fdfsProp) throws Exception {
+ this.fdfsProp = fdfsProp;
+ this.logger = LoggerFactory.getLogger(getClass());
+ init();
+ create();
+ info();
+ }
+
+ /**
+ * 初始化全局客户端
+ */
+ private void init() throws Exception {
+ this.prop = new Properties();
+ this.logger.info("FastDFS: reading config file...");
+ this.logger.info("FastDFS: fastdfs.connect_timeout_in_seconds=" + this.fdfsProp.getConnectTimeout());
+ this.logger.info("FastDFS: fastdfs.network_timeout_in_seconds=" + this.fdfsProp.getNetworkTimeout());
+ this.logger.info("FastDFS: fastdfs.charset=" + this.fdfsProp.getCharset());
+ this.logger.info("FastDFS: fastdfs.http_anti_steal_token=" + this.fdfsProp.getHttpAntiStealToken());
+ this.logger.info("FastDFS: fastdfs.http_secret_key=" + this.fdfsProp.getHttpSecretKey());
+ this.logger.info("FastDFS: fastdfs.http_tracker_http_port=" + this.fdfsProp.getHttpTrackerHttpPort());
+ this.logger.info("FastDFS: fastdfs.tracker_servers=" + this.fdfsProp.getTrackerServers());
+ this.logger.info("FastDFS: fastdfs.connection_pool_max_total=" + this.fdfsProp.getConnectionPoolMaxTotal());
+ this.logger.info("FastDFS: fastdfs.connection_pool_max_idle=" + this.fdfsProp.getConnectionPoolMaxIdle());
+ this.logger.info("FastDFS: fastdfs.connection_pool_min_idle=" + this.fdfsProp.getConnectionPoolMinIdle());
+ this.logger.info("FastDFS: fastdfs.nginx_servers=" + this.fdfsProp.getNginxServers());
+
+ this.prop.put("fastdfs.connect_timeout_in_seconds", this.fdfsProp.getConnectTimeout());
+ this.prop.put("fastdfs.network_timeout_in_seconds", this.fdfsProp.getNetworkTimeout());
+ this.prop.put("fastdfs.charset", this.fdfsProp.getCharset());
+ this.prop.put("fastdfs.http_anti_steal_token", this.fdfsProp.getHttpAntiStealToken());
+ this.prop.put("fastdfs.http_secret_key", this.fdfsProp.getHttpSecretKey());
+ this.prop.put("fastdfs.http_tracker_http_port", this.fdfsProp.getHttpTrackerHttpPort());
+ this.prop.put("fastdfs.tracker_servers", this.fdfsProp.getTrackerServers());
+ ClientGlobal.initByProperties(this.prop);
+ }
+
+ /**
+ * 显示初始化信息
+ */
+ private void info() {
+ this.logger.info("FastDFS parameter: ConnectionPoolMaxTotal ==> " + this.pool.getMaxTotal());
+ this.logger.info("FastDFS parameter: ConnectionPoolMaxIdle ==> " + this.pool.getMaxIdle());
+ this.logger.info("FastDFS parameter: ConnectionPoolMinIdle ==> " + this.pool.getMinIdle());
+ this.logger.info("FastDFS parameter: NginxServer ==> " + Arrays.toString(this.nginxServers));
+ this.logger.info(ClientGlobal.configInfo());
+ }
+
+ /**
+ * 创建连接池
+ */
+ private void create() {
+ this.config = new GenericObjectPoolConfig();
+ this.logger.info("FastDFS Client: Creating connection pool...");
+ this.config.setMaxTotal(Integer.parseInt(this.fdfsProp.getConnectionPoolMaxTotal()));
+ this.config.setMaxIdle(Integer.parseInt(this.fdfsProp.getConnectionPoolMaxIdle()));
+ this.config.setMinIdle(Integer.parseInt(this.fdfsProp.getConnectionPoolMinIdle()));
+ StorageClientFactory factory = new StorageClientFactory();
+ this.pool = new GenericObjectPool(factory, this.config);
+ this.nginxServers = this.fdfsProp.getNginxServers().split(",");
+ }
+
+ /**
+ * Nginx服务器负载均衡算法
+ *
+ * @param servers 服务器地址
+ * @param address 客户端IP地址
+ * @return 可用的服务器地址
+ */
+ private String getNginxServer(String[] servers, String address) {
+ int size = servers.length;
+ int i = address.hashCode();
+ int index = abs(i % size);
+ return servers[index];
+ }
+
+ /**
+ * 带有防盗链的下载
+ *
+ * @param fileGroup 文件组名
+ * @param remoteFileName 远程文件名称
+ * @param clientIpAddress 客户端IP地址
+ * @return 完整的URL地址
+ */
+ public String autoDownloadWithToken(String fileGroup, String remoteFileName, String clientIpAddress) throws Exception {
+ int ts = (int) (System.currentTimeMillis() / 1000);
+ String token = ProtoCommon.getToken(remoteFileName, ts, ClientGlobal.getG_secret_key());
+ String nginx = this.getNginxServer(this.nginxServers, clientIpAddress);
+ return "http://" + nginx + "/" + fileGroup + "/" + remoteFileName + "?token=" + token + "&ts=" + ts;
+ }
+
+ /**
+ * 上传文件,适合上传图片
+ *
+ * @param buffer 字节数组
+ * @param ext 扩展名
+ * @return 文件组名和ID
+ */
+ public String[] autoUpload(byte[] buffer, String ext) throws Exception {
+ String[] upload = this.upload(buffer, ext, null);
+ return upload;
+ }
+
+ /**
+ * 不带防盗链的下载,如果开启防盗链会导致该方法抛出异常
+ *
+ * @param fileGroup 文件组名
+ * @param remoteFileName 远程文件ID
+ * @param clientIpAddress 客户端IP地址,根据客户端IP来分配Nginx服务器
+ * @return 完整的URL地址
+ */
+ public String autoDownloadWithoutToken(String fileGroup, String remoteFileName, String clientIpAddress) throws Exception {
+ if (ClientGlobal.getG_anti_steal_token()) {
+ this.logger.error("FastDFS Client: You've turned on Token authentication.");
+ throw new Exception("You've turned on Token authentication.");
+ }
+ String nginx = this.getNginxServer(this.nginxServers, clientIpAddress);
+ return "http://" + nginx + "/" +fileGroup + "/" + remoteFileName;
+ }
+
+
+ /**
+ * 通过本地文件上传
+ *
+ * @param localFileName 本地文件名称
+ * @param fileExtName 文件扩展名
+ * @param metadata 文件的元数据
+ * @return 文件组名和ID
+ */
+ public String[] upload(String localFileName, String fileExtName, NameValuePair[] metadata) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ final String[] strings = client.upload_file(localFileName, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过本地文件上传
+ *
+ * @param localFileName 本地文件名称
+ * @param fileExtName 文件扩展名
+ * @param metadata 文件的元数据
+ * @param waitTimeMillis 获取连接等待时间
+ * @return 文件组名和ID
+ */
+ public String[] upload(String localFileName, String fileExtName, NameValuePair[] metadata, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ final String[] strings = client.upload_file(localFileName, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过字节数组上传文件
+ *
+ * @param buff 文件的字节数组
+ * @param offset 偏移量
+ * @param len 长度
+ * @param fileExtName 文件扩展名
+ * @param metadata 文件的元数据
+ * @return 文件组名和ID
+ */
+ public String[] upload(byte[] buff, int offset, int len, String fileExtName, NameValuePair[] metadata) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ String[] strings = client.upload_file(buff, offset, len, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过字节数组上传文件
+ *
+ * @param buff 文件的字节数组
+ * @param offset 偏移量
+ * @param len 长度
+ * @param fileExtName 文件扩展名
+ * @param metadata 文件的元数据
+ * @param waitTimeMillis 获取连接的等待时间
+ * @return 文件组名和ID
+ */
+ public String[] upload(byte[] buff, int offset, int len, String fileExtName, NameValuePair[] metadata, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ String[] strings = client.upload_file(buff, offset, len, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过字节数组上传
+ *
+ * @param groupName 文件的组名
+ * @param buff 文件的字节数组
+ * @param offset 偏移量
+ * @param len 长度
+ * @param fileExtName 文件扩展名
+ * @param metadata 文件的元数据
+ * @return 文件组名和ID
+ */
+ public String[] upload(String groupName, byte[] buff, int offset, int len, String fileExtName, NameValuePair[] metadata) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ String[] strings = client.upload_file(groupName, buff, offset, len, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过字节数组上传文件
+ *
+ * @param groupName 文件的组名
+ * @param buff 文件的字节数组
+ * @param offset 偏移量
+ * @param len 长度
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @param waitTimeMillis 获取连接的等待时间
+ * @return 文件组名和ID
+ */
+ public String[] upload(String groupName, byte[] buff, int offset, int len, String fileExtName, NameValuePair[] metadata, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ String[] strings = client.upload_file(groupName, buff, offset, len, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过字节数组上传
+ *
+ * @param fileBuff 文件的字节数组
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @return 文件组名和ID
+ */
+ public String[] upload(byte[] fileBuff, String fileExtName, NameValuePair[] metadata) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ String[] strings = client.upload_file(fileBuff, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过字节数组上传文件
+ *
+ * @param fileBuff 文件的字节数组
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @param waitTimeMillis 获取连接的等待时间
+ * @return 文件组名和ID
+ */
+ public String[] upload(byte[] fileBuff, String fileExtName, NameValuePair[] metadata, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ String[] strings = client.upload_file(fileBuff, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过字节数组上传文件
+ *
+ * @param groupName 文件组名
+ * @param fileBuff 文件的字节数组
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @return 文件的组名和ID
+ */
+ public String[] upload(String groupName, byte[] fileBuff, String fileExtName, NameValuePair[] metadata) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ String[] strings = client.upload_file(groupName, fileBuff, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过字节数组上传文件
+ *
+ * @param groupName 文件组名
+ * @param fileBuff 文件的字节数组
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @param waitTimeMillis 等待连接的时长
+ * @return 文件组名和ID
+ */
+ public String[] upload(String groupName, byte[] fileBuff, String fileExtName, NameValuePair[] metadata, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ String[] strings = client.upload_file(groupName, fileBuff, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过回调接口上传文件
+ *
+ * @param groupName 文件组名
+ * @param fileSize 文件大小
+ * @param callback 回调函数
+ * @param fileExtName 文件扩展名
+ * @param metadata 元数据
+ * @return 文件组名和ID
+ */
+ public String[] upload(String groupName, long fileSize, UploadCallback callback, String fileExtName, NameValuePair[] metadata) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ String[] strings = client.upload_file(groupName, fileSize, callback, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过回调接口上传文件
+ *
+ * @param groupName 文件组名
+ * @param fileSize 文件大小
+ * @param callback 回调接口
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @param waitTimeMillis 等待连接的时长
+ * @return 文件组名和ID
+ */
+ public String[] upload(String groupName, long fileSize, UploadCallback callback, String fileExtName, NameValuePair[] metadata, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ String[] strings = client.upload_file(groupName, fileSize, callback, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过本地上传文件
+ *
+ * @param groupName 文件组名
+ * @param masterFileName 生成副本的文件名
+ * @param prefixName 生成副本的文件名前缀
+ * @param localFileName 本地文件名称
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @return 文件组名和ID
+ */
+ public String[] upload(String groupName, String masterFileName, String prefixName, String localFileName, String fileExtName, NameValuePair[] metadata) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ String[] strings = client.upload_file(groupName, masterFileName, prefixName, localFileName, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过本地上传文件
+ *
+ * @param groupName 文件组名
+ * @param masterFileName 生成副本的文件名
+ * @param prefixName 生成副本的文件名前缀
+ * @param localFileName 本地文件名称
+ * @param fileExtName 文件扩展名
+ * @param metadata 元数据
+ * @param waitTimeMillis 等待连接的时长
+ * @return 文件组名和ID
+ */
+ public String[] upload(String groupName, String masterFileName, String prefixName, String localFileName, String fileExtName, NameValuePair[] metadata, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ String[] strings = client.upload_file(groupName, masterFileName, prefixName, localFileName, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过字节数组上传
+ *
+ * @param groupName 文件组名
+ * @param masterFileName 生成副本的文件名
+ * @param prefixName 生成副本的文件名前缀
+ * @param buff 文件的字节数组
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @return 文件组名和ID
+ */
+ public String[] upload(String groupName, String masterFileName, String prefixName, byte[] buff, String fileExtName, NameValuePair[] metadata) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ String[] strings = client.upload_file(groupName, masterFileName, prefixName, buff, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过字节数组上传
+ *
+ * @param groupName 组名
+ * @param masterFileName 生成副本的文件名
+ * @param prefixName 生成副本的文件名前缀
+ * @param buff 文件的字节数组
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @param waitTimeMillis 等待连接的时长
+ * @return 文件组名和ID
+ */
+ public String[] upload(String groupName, String masterFileName, String prefixName, byte[] buff, String fileExtName, NameValuePair[] metadata, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ String[] strings = client.upload_file(groupName, masterFileName, prefixName, buff, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过字节数组上传文件
+ *
+ * @param groupName 组名
+ * @param masterFileName 生成副本的文件名
+ * @param prefixName 生成副本的文件名前缀
+ * @param buff 文件的字节数组
+ * @param offset 偏移量
+ * @param len 长度
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @return 文件组名和ID
+ */
+ public String[] upload(String groupName, String masterFileName, String prefixName, byte[] buff, int offset, int len, String fileExtName, NameValuePair[] metadata) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ String[] strings = client.upload_file(groupName, masterFileName, prefixName, buff, offset, len, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过字节数组上传文件
+ *
+ * @param groupName 组名
+ * @param masterFileName 生成副本的文件名
+ * @param prefixName 生成副本的文件名前缀
+ * @param buff 文件的字节数组
+ * @param offset 偏移量
+ * @param len 长度
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @param waitTimeMillis 等待连接的时长
+ * @return 文件组名和ID
+ */
+ public String[] upload(String groupName, String masterFileName, String prefixName, byte[] buff, int offset, int len, String fileExtName, NameValuePair[] metadata, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ String[] strings = client.upload_file(groupName, masterFileName, prefixName, buff, offset, len, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过回调函数上传文件
+ *
+ * @param groupName 组名
+ * @param masterFileName 生成副本的文件名
+ * @param prefixName 生成副本的文件名前缀
+ * @param fileSize 文件的大小
+ * @param callback 回调接口
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @return 文件组名和ID
+ */
+ public String[] upload(String groupName, String masterFileName, String prefixName, long fileSize, UploadCallback callback, String fileExtName, NameValuePair[] metadata) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ String[] strings = client.upload_file(groupName, masterFileName, prefixName, fileSize, callback, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过回调函数上传文件
+ *
+ * @param groupName 组名
+ * @param masterFileName 生成副本的文件名
+ * @param prefixName 生成副本的文件名前缀
+ * @param fileSize 文件的大小
+ * @param callback 回调接口
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @param waitTimeMillis 等待连接的时长
+ * @return 文件组名和ID
+ */
+ public String[] upload(String groupName, String masterFileName, String prefixName, long fileSize, UploadCallback callback, String fileExtName, NameValuePair[] metadata, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ String[] strings = client.upload_file(groupName, masterFileName, prefixName, fileSize, callback, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 将本地附加文件上传
+ *
+ * @param localFileName 本地文件名称
+ * @param fileExtName 文件扩展名
+ * @param metadata 元数据
+ * @return 文件组名和ID
+ */
+ public String[] uploadAppenderFile(String localFileName, String fileExtName, NameValuePair[] metadata) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ String[] strings = client.upload_appender_file(localFileName, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 将附加文件上传
+ *
+ * @param localFileName 文件名称
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @param waitTimeMillis 获取连接等待时长
+ * @return 文件组名和ID
+ */
+ public String[] uploadAppenderFile(String localFileName, String fileExtName, NameValuePair[] metadata, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ String[] strings = client.upload_appender_file(localFileName, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过字节数组上传附加文件
+ *
+ * @param buff 字节数组
+ * @param offset 偏移量
+ * @param len 长度
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @return 文件组名和ID
+ */
+ public String[] uploadAppenderFile(byte[] buff, int offset, int len, String fileExtName, NameValuePair[] metadata) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ String[] strings = client.upload_appender_file(buff, offset, len, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过字节数组上传附加文件
+ *
+ * @param buff 字节数组
+ * @param offset 偏移量
+ * @param len 长度
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @param waitTimeMillis 等待获取连接的时长
+ * @return 文件组名和ID
+ */
+ public String[] uploadAppenderFile(byte[] buff, int offset, int len, String fileExtName, NameValuePair[] metadata, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ String[] strings = client.upload_appender_file(buff, offset, len, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过字节数组上传附加文件
+ *
+ * @param groupName 组名
+ * @param buff 字节数组
+ * @param offset 偏移量
+ * @param len 长度
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @return 文件组名和ID
+ */
+ public String[] uploadAppenderFile(String groupName, byte[] buff, int offset, int len, String fileExtName, NameValuePair[] metadata) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ String[] strings = client.upload_appender_file(groupName, buff, offset, len, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过字节数组上传附加文件
+ *
+ * @param groupName 组名
+ * @param buff 字节数组
+ * @param offset 偏移量
+ * @param len 长度
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @param waitTimeMillis 获取连接的等待时长
+ * @return 文件组名和ID
+ */
+ public String[] uploadAppenderFile(String groupName, byte[] buff, int offset, int len, String fileExtName, NameValuePair[] metadata, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ String[] strings = client.upload_appender_file(groupName, buff, offset, len, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过字节数组上传附加文件
+ *
+ * @param buff 字节数组
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @return 文件组名和ID
+ */
+ public String[] uploadAppenderFile(byte[] buff, String fileExtName, NameValuePair[] metadata) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ String[] strings = client.upload_appender_file(buff, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过字节数组上传附加文件
+ *
+ * @param buff 字节数组
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @param waitTimeMillis 等待获取连接的时长
+ * @return 文件组名和ID
+ */
+ public String[] uploadAppenderFile(byte[] buff, String fileExtName, NameValuePair[] metadata, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ String[] strings = client.upload_appender_file(buff, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过字节数组上传
+ *
+ * @param groupName 组名
+ * @param buff 字节数组
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @return 文件组名和ID
+ */
+ public String[] uploadAppenderFile(String groupName, byte[] buff, String fileExtName, NameValuePair[] metadata) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ String[] strings = client.upload_appender_file(groupName, buff, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过字节数组上传附加文件
+ *
+ * @param groupName 组名
+ * @param buff 字节数组
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @param waitTimeMillis 获取连接的等待时长
+ * @return 文件组名和ID
+ */
+ public String[] uploadAppenderFile(String groupName, byte[] buff, String fileExtName, NameValuePair[] metadata, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ String[] strings = client.upload_appender_file(groupName, buff, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过回调上传附加文件
+ *
+ * @param groupName 组名
+ * @param fileSize 文件大小
+ * @param callback 回调函数
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @return 文件组名和ID
+ */
+ public String[] uploadAppenderFile(String groupName, long fileSize, UploadCallback callback, String fileExtName, NameValuePair[] metadata) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ String[] strings = client.upload_appender_file(groupName, fileSize, callback, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 通过回调上传附加文件
+ *
+ * @param groupName 组名
+ * @param fileSize 文件大小
+ * @param callback 回调函数
+ * @param fileExtName 扩展名
+ * @param metadata 元数据
+ * @param waitTimeMillis 等待时间
+ * @return 文件组名和ID
+ */
+ public String[] uploadAppenderFile(String groupName, long fileSize, UploadCallback callback, String fileExtName, NameValuePair[] metadata, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ String[] strings = client.upload_appender_file(groupName, fileSize, callback, fileExtName, metadata);
+ this.pool.returnObject(client);
+ return strings;
+ }
+
+ /**
+ * 追加文件
+ *
+ * @param groupName 组名
+ * @param appenderFileName 追加文件名称
+ * @param localFileName 本地文件名称
+ * @return 返回0表示成功
+ */
+ public int appendFile(String groupName, String appenderFileName, String localFileName) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ int i = client.append_file(groupName, appenderFileName, localFileName);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 追加文件
+ *
+ * @param groupName 组名
+ * @param appenderFileName 追加文件名称
+ * @param localFileName 本地文件名称
+ * @param waitTimeMillis 等待时间时长
+ * @return 返回0表示成功
+ */
+ public int appendFile(String groupName, String appenderFileName, String localFileName, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ int i = client.append_file(groupName, appenderFileName, localFileName);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 追加文件
+ *
+ * @param groupName 组名
+ * @param appenderFileName 追加文件名称
+ * @param buff 文件字节数组
+ * @return 返回0表示成功
+ */
+ public int appendFile(String groupName, String appenderFileName, byte[] buff) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ int i = client.append_file(groupName, appenderFileName, buff);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 追加文件
+ *
+ * @param groupName 组名
+ * @param appenderFileName 追加文件名称
+ * @param buff 文件字节数组
+ * @param waitTimeMillis 等待时长
+ * @return 返回0表示成功
+ */
+ public int appendFile(String groupName, String appenderFileName, byte[] buff, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ int i = client.append_file(groupName, appenderFileName, buff);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 追加文件
+ *
+ * @param groupName 组名
+ * @param appenderFileName 追加文件名称
+ * @param buff 字节数组
+ * @param offset 偏移量
+ * @param len 长度
+ * @return 返回0表示成功
+ */
+ public int appendFile(String groupName, String appenderFileName, byte[] buff, int offset, int len) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ int i = client.append_file(groupName, appenderFileName, buff, offset, len);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 追加文件
+ *
+ * @param groupName 组名
+ * @param appenderFileName 追加文件名称
+ * @param buff 字节数组
+ * @param offset 偏移量
+ * @param len 长度
+ * @param waitTimeMillis 等待时长
+ * @return 文件组名和ID
+ */
+ public int appendFile(String groupName, String appenderFileName, byte[] buff, int offset, int len, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ int i = client.append_file(groupName, appenderFileName, buff, offset, len);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+
+ /**
+ * 追加文件
+ *
+ * @param groupName 组名
+ * @param appenderFileName 追加文件名称
+ * @param fileSize 文件大小
+ * @param callback 回调函数
+ * @return 返回0表示成功
+ */
+ public int appendFile(String groupName, String appenderFileName, long fileSize, UploadCallback callback) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ int i = client.append_file(groupName, appenderFileName, fileSize, callback);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 上传文件
+ *
+ * @param groupName 组名
+ * @param appenderFileName 追加文件名称
+ * @param fileSize 文件大小
+ * @param callback 回调函数
+ * @param waitTimeMillis 等待时间
+ * @return 返回0表示成功
+ */
+ public int appendFile(String groupName, String appenderFileName, long fileSize, UploadCallback callback, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ int i = client.append_file(groupName, appenderFileName, fileSize, callback);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 修改文件
+ *
+ * @param groupName 组名
+ * @param appenderFileName 追加文件名称
+ * @param fileOffset 偏移量
+ * @param localFileName 本地文件名称
+ * @return 返回0表示成功
+ */
+ public int modify(String groupName, String appenderFileName, long fileOffset, String localFileName) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ int i = client.modify_file(groupName, appenderFileName, fileOffset, localFileName);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 修改文件
+ *
+ * @param groupName 组名
+ * @param appenderFileName 追加文件名称
+ * @param fileOffset 偏移量
+ * @param localFileName 本地文件名称
+ * @param waitTimeMillis 等待时长
+ * @return 返回0表示成功
+ */
+ public int modify(String groupName, String appenderFileName, long fileOffset, String localFileName, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ int i = client.modify_file(groupName, appenderFileName, fileOffset, localFileName);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 修改文件
+ *
+ * @param groupName 组名
+ * @param appenderFileName 追加文件名称
+ * @param fileOffset 偏移量
+ * @param buff 字节数组
+ * @return 返回0表示成功
+ */
+ public int modify(String groupName, String appenderFileName, long fileOffset, byte[] buff) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ int i = client.modify_file(groupName, appenderFileName, fileOffset, buff);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 修改文件
+ *
+ * @param groupName 组名
+ * @param appenderFileName 追加文件名称
+ * @param fileOffset 偏移量
+ * @param buff 字节数组
+ * @param waitTimeMillis 等待时间
+ * @return 返回0表示成功
+ */
+ public int modify(String groupName, String appenderFileName, long fileOffset, byte[] buff, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ int i = client.modify_file(groupName, appenderFileName, fileOffset, buff);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 修改文件
+ *
+ * @param groupName 组名
+ * @param appenderFileName 追加文件名称
+ * @param fileOffset 偏移量
+ * @param buff 字节数组
+ * @param bufferOffset 偏移量
+ * @param bufferLen 长度
+ * @return 返回0表示成功
+ */
+ public int modify(String groupName, String appenderFileName, long fileOffset, byte[] buff, int bufferOffset, int bufferLen) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ int i = client.modify_file(groupName, appenderFileName, fileOffset, buff, bufferOffset, bufferLen);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 修改文件
+ *
+ * @param groupName 组名
+ * @param appenderFileName 追加文件名称
+ * @param fileOffset 文件偏移量
+ * @param buff 字节数组
+ * @param bufferOffset 字节偏移量
+ * @param bufferLen 字节长度
+ * @param waitTimeMillis 等待时间
+ * @return 返回0表示成功
+ */
+ public int modify(String groupName, String appenderFileName, long fileOffset, byte[] buff, int bufferOffset, int bufferLen, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ int i = client.modify_file(groupName, appenderFileName, fileOffset, buff, bufferOffset, bufferLen);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 修改文件
+ *
+ * @param groupName 组名
+ * @param appenderFileName 追加文件名称
+ * @param fileOffset 偏移量
+ * @param modifySize 修改大小
+ * @param callback 回调函数
+ * @return 文件组名和ID
+ */
+ public int modify(String groupName, String appenderFileName, long fileOffset, long modifySize, UploadCallback callback) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ int i = client.modify_file(groupName, appenderFileName, fileOffset, modifySize, callback);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 修改文件
+ *
+ * @param groupName 组名
+ * @param appenderFileName 追加文件名称
+ * @param fileOffset 偏移量
+ * @param modifySize 修改大小
+ * @param callback 回调函数
+ * @param waitTimeMillis 等待时间
+ * @return 文件组名和ID
+ */
+ public int modify(String groupName, String appenderFileName, long fileOffset, long modifySize, UploadCallback callback, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ int i = client.modify_file(groupName, appenderFileName, fileOffset, modifySize, callback);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 删除文件
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件名称
+ * @return 返回0表示成功
+ */
+ public int delete(String groupName, String remoteFileName) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ int i = client.delete_file(groupName, remoteFileName);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 删除文件
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件名称
+ * @param waitTimeMillis 等待时间
+ * @return 返回0表示成功
+ */
+ public int delete(String groupName, String remoteFileName, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ int i = client.delete_file(groupName, remoteFileName);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 文件分片
+ *
+ * @param groupName 组名
+ * @param appenderName 输出源文件名称
+ * @return 返回0表示成功
+ */
+ public int truncate(String groupName, String appenderName) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ int i = client.truncate_file(groupName, appenderName);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 文件分片
+ *
+ * @param groupName 组名
+ * @param appenderName 输出源文件名称
+ * @param waitTimeMillis 等待时间
+ * @return 返回0表示成功
+ */
+ public int truncate(String groupName, String appenderName, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ client.truncate_file(groupName, appenderName);
+ this.pool.returnObject(client);
+ return 0;
+ }
+
+ /**
+ * 文件分片
+ *
+ * @param truncatedFileSize 分片大小
+ * @param groupName 组名
+ * @param appenderName 输出源文件名称
+ * @return 返回0表示成功
+ */
+ public int truncate(long truncatedFileSize, String groupName, String appenderName) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ int i = client.truncate_file(groupName, appenderName, truncatedFileSize);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 文件分片
+ *
+ * @param truncatedFileSize 分片大小
+ * @param groupName 组名
+ * @param appenderName 输出源文件
+ * @param waitTimeMillis 等待时间
+ * @return 返回0表示成功
+ */
+ public int truncate(long truncatedFileSize, String groupName, String appenderName, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ int i = client.truncate_file(groupName, appenderName, truncatedFileSize);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 下载文件
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件名称
+ * @return 返回0表示成功
+ */
+ public byte[] download(String groupName, String remoteFileName) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ byte[] bytes = client.download_file(groupName, remoteFileName);
+ this.pool.returnObject(client);
+ return bytes;
+ }
+
+ /**
+ * 下载文件
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件名称
+ * @param waitTimeMillis 等待时间
+ * @return 返回0表示成功
+ */
+ public byte[] download(String groupName, String remoteFileName, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ byte[] bytes = client.download_file(groupName, remoteFileName);
+ this.pool.returnObject(client);
+ return bytes;
+ }
+
+ /**
+ * 下载文件
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件名称
+ * @param offset 偏移量
+ * @param bytes 字节数组
+ * @return 返回0表示成功
+ */
+ public byte[] download(String groupName, String remoteFileName, long offset, long bytes) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ byte[] bytes1 = client.download_file(groupName, remoteFileName, offset, bytes);
+ this.pool.returnObject(client);
+ return bytes1;
+ }
+
+ /**
+ * 下载文件
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件名称
+ * @param offset 偏移量
+ * @param bytes 字节数组
+ * @param waitTimeMillis 等待时长
+ * @return 返回0表示成功
+ */
+ public byte[] download(String groupName, String remoteFileName, long offset, long bytes, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ byte[] bytes1 = client.download_file(groupName, remoteFileName, offset, bytes);
+ this.pool.returnObject(client);
+ return bytes1;
+ }
+
+ /**
+ * 下载文件
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件名称
+ * @param localFileName 本地文件名称
+ * @return 返回0表示成功
+ */
+ public int download(String groupName, String remoteFileName, String localFileName) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ int i = client.download_file(groupName, remoteFileName, localFileName);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 下载文件
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件名称
+ * @param localFileName 本地文件名称
+ * @param waitTimeMillis 等待时间
+ * @return 返回0表示成功
+ */
+ public int download(String groupName, String remoteFileName, String localFileName, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ int i = client.download_file(groupName, remoteFileName, localFileName);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 下载文件
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件名称
+ * @param offset 偏移量
+ * @param bytes 字节数组
+ * @param localFileName 本地文件名称
+ * @return 返回0表示成功
+ */
+ public int download(String groupName, String remoteFileName, long offset, long bytes, String localFileName) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ int i = client.download_file(groupName, remoteFileName, offset, bytes, localFileName);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 下载文件
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件名称
+ * @param offset 偏移量
+ * @param bytes 字节数组
+ * @param localFileName 本地文件名称
+ * @param waitTimeMillis 等待时间
+ * @return 返回0表示成功
+ */
+ public int download(String groupName, String remoteFileName, long offset, long bytes, String localFileName, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ int i = client.download_file(groupName, remoteFileName, offset, bytes, localFileName);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 下载文件
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件名称
+ * @param callback 回调函数
+ * @return 文件分组和ID
+ */
+ public int download(String groupName, String remoteFileName, DownloadCallback callback) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ int i = client.download_file(groupName, remoteFileName, callback);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 下载文件
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件名称
+ * @param callback 回调函数
+ * @param waitTimeMillis 等待时间
+ * @return 返回0表示成功
+ */
+ public int download(String groupName, String remoteFileName, DownloadCallback callback, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ int i = client.download_file(groupName, remoteFileName, callback);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 下载文件
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件名称
+ * @param offset 偏移量
+ * @param bytes 字节数组
+ * @param callback 回调函数
+ * @return 返回0表示成功
+ */
+ public int download(String groupName, String remoteFileName, long offset, long bytes, DownloadCallback callback) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ int i = client.download_file(groupName, remoteFileName, offset, bytes, callback);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 下载文件
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件名称
+ * @param offset 偏移量
+ * @param bytes 字节数组
+ * @param callback 回调函数
+ * @param waitTimeMillis 等待时间
+ * @return 返回0表示成功
+ */
+ public int download(String groupName, String remoteFileName, long offset, long bytes, DownloadCallback callback, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ int i = client.download_file(groupName, remoteFileName, offset, bytes, callback);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 获取元数据
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件名称
+ * @return 键值对数组
+ */
+ public NameValuePair[] getMetadata(String groupName, String remoteFileName) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ NameValuePair[] metadata = client.get_metadata(groupName, remoteFileName);
+ this.pool.returnObject(client);
+ return metadata;
+ }
+
+ /**
+ * 获取元数据
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件名称
+ * @param waitTimeMillis 等待时间
+ * @return 键值对数组
+ */
+ public NameValuePair[] getMetadata(String groupName, String remoteFileName, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ NameValuePair[] metadata = client.get_metadata(groupName, remoteFileName);
+ this.pool.returnObject(client);
+ return metadata;
+ }
+
+ /**
+ * 设置元数据
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件名称
+ * @param metadata 元数据
+ * @param flag 更新设置:ProtoCommon.STORAGE_SET_METADATA_FLAG_OVERWRITE 表示重写所有; ProtoCommon.STORAGE_SET_METADATA_FLAG_MERGE 表示没有就插入,有就更新
+ * @return 返回0表示成功
+ */
+ public int setMetadata(String groupName, String remoteFileName, NameValuePair[] metadata, byte flag) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ int i = client.set_metadata(groupName, remoteFileName, metadata, flag);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 设置元数据
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件名称
+ * @param metadata 元数据
+ * @param flag 更新设置:ProtoCommon.STORAGE_SET_METADATA_FLAG_OVERWRITE 表示重写所有; ProtoCommon.STORAGE_SET_METADATA_FLAG_MERGE 表示没有就插入,有就更新
+ * @param waitTimeMillis 等待时长
+ * @return 返回0表示成功
+ */
+ public int setMetadata(String groupName, String remoteFileName, NameValuePair[] metadata, byte flag, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ int i = client.set_metadata(groupName, remoteFileName, metadata, flag);
+ this.pool.returnObject(client);
+ return i;
+ }
+
+ /**
+ * 获取文件信息
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件名称
+ * @return 返回文件信息
+ */
+ public FileInfo getFileInfo(String groupName, String remoteFileName) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ FileInfo file_info = client.get_file_info(groupName, remoteFileName);
+ this.pool.returnObject(client);
+ return file_info;
+ }
+
+ /**
+ * 获取文件名称
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件名称
+ * @param waitTimeMillis 等待时长
+ * @return 返回文件信息
+ */
+ public FileInfo getFileInfo(String groupName, String remoteFileName, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ FileInfo file_info = client.get_file_info(groupName, remoteFileName);
+ this.pool.returnObject(client);
+ return file_info;
+ }
+
+ /**
+ * 查询文件信息
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件信息
+ * @return 返回文件信息
+ */
+ public FileInfo queryFileInfo(String groupName, String remoteFileName) throws Exception {
+ StorageClient client = this.pool.borrowObject();
+ FileInfo fileInfo = client.query_file_info(groupName, remoteFileName);
+ this.pool.returnObject(client);
+ return fileInfo;
+ }
+
+ /**
+ * 查询文件信息
+ *
+ * @param groupName 组名
+ * @param remoteFileName 远程文件信息
+ * @param waitTimeMillis 等待时间
+ * @return 返回文件信息
+ */
+ public FileInfo queryFileInfo(String groupName, String remoteFileName, long waitTimeMillis) throws Exception {
+ StorageClient client = this.pool.borrowObject(waitTimeMillis);
+ FileInfo fileInfo = client.query_file_info(groupName, remoteFileName);
+ this.pool.returnObject(client);
+ return fileInfo;
+ }
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/common/Base64.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/common/Base64.java
new file mode 100644
index 000000000..4f248ec5b
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/common/Base64.java
@@ -0,0 +1,491 @@
+package org.csource.common;
+
+import java.io.IOException;
+
+/**
+ * Freeware from:
+ * Roedy Green
+ * Canadian Mind Products
+ * #327 - 964 Heywood Avenue
+ * Victoria, BC Canada V8V 2Y5
+ * tel:(250) 361-9093
+ * mailto:roedy@mindprod.com
+ */
+
+/**
+ * Encode arbitrary binary into printable ASCII using BASE64 encoding.
+ * very loosely based on the Base64 Reader by
+ * Dr. Mark Thornton
+ * Optrak Distribution Software Ltd.
+ * http://www.optrak.co.uk
+ * and Kevin Kelley's http://www.ruralnet.net/~kelley/java/Base64.java
+ *
+ * Base64 is a way of encoding 8-bit characters using only ASCII printable
+ * characters similar to UUENCODE. UUENCODE includes a filename where BASE64 does not.
+ * The spec is described in RFC 2045. Base64 is a scheme where
+ * 3 bytes are concatenated, then split to form 4 groups of 6-bits each; and
+ * each 6-bits gets translated to an encoded printable ASCII character, via a
+ * table lookup. An encoded string is therefore longer than the original by
+ * about 1/3. The "=" character is used to pad the end. Base64 is used,
+ * among other things, to encode the user:password string in an
+ * Authorization: header for HTTP. Don't confuse Base64 with
+ * x-www-form-urlencoded which is handled by
+ * Java.net.URLEncoder.encode/decode
+ * If you don't like this code, there is another implementation at http://www.ruffboy.com/download.htm
+ * Sun has an undocumented method called sun.misc.Base64Encoder.encode.
+ * You could use hex, simpler to code, but not as compact.
+ *
+ * If you wanted to encode a giant file, you could do it in large chunks that
+ * are even multiples of 3 bytes, except for the last chunk, and append the outputs.
+ *
+ * To encode a string, rather than binary data java.net.URLEncoder may be better. See
+ * printable characters in the Java glossary for a discussion of the differences.
+ *
+ * version 1.4 2002 February 15 -- correct bugs with uneven line lengths,
+ * allow you to configure line separator.
+ * now need Base64 object and instance methods.
+ * new mailing address.
+ * version 1.3 2000 September 12 -- fix problems with estimating output length in encode
+ * version 1.2 2000 September 09 -- now handles decode as well.
+ * version 1.1 1999 December 04 -- more symmetrical encoding algorithm.
+ * more accurate StringBuffer allocation size.
+ * version 1.0 1999 December 03 -- posted in comp.lang.java.programmer.
+ * Futures Streams or files.
+ */
+
+public class Base64 {
+
+ /**
+ * Marker value for chars we just ignore, e.g. \n \r high ascii
+ */
+ static final int IGNORE = -1;
+ /**
+ * Marker for = trailing pad
+ */
+ static final int PAD = -2;
+ /**
+ * used to disable test driver
+ */
+ private static final boolean debug = true;
+ /**
+ * how we separate lines, e.g. \n, \r\n, \r etc.
+ */
+ private String lineSeparator = System.getProperty("line.separator");
+ /**
+ * max chars per line, excluding lineSeparator. A multiple of 4.
+ */
+ private int lineLength = 72;
+ private char[] valueToChar = new char[64];
+ /**
+ * binary value encoded by a given letter of the alphabet 0..63
+ */
+ private int[] charToValue = new int[256];
+ private int[] charToPad = new int[4];
+
+ /* constructor */
+ public Base64() {
+ this.init('+', '/', '=');
+ }
+
+ /* constructor */
+ public Base64(char chPlus, char chSplash, char chPad, int lineLength) {
+ this.init(chPlus, chSplash, chPad);
+ this.lineLength = lineLength;
+ }
+
+ public Base64(int lineLength) {
+ this.lineLength = lineLength;
+ }
+
+ /**
+ * debug display array
+ */
+ public static void show(byte[] b) {
+ int count = 0;
+ int rows = 0;
+
+
+ for (int i = 0; i < b.length; i++) {
+ if (count == 8) {
+ System.out.print(" ");
+ } else if (count == 16) {
+ System.out.println("");
+ count = 0;
+ continue;
+ }
+ System.out.print(Integer.toHexString(b[i] & 0xFF).toUpperCase() + " ");
+ count++;
+
+ }
+ System.out.println();
+ }
+
+ /**
+ * debug display array
+ */
+ public static void display(byte[] b) {
+ for (int i = 0; i < b.length; i++) {
+ System.out.print((char) b[i]);
+ }
+ System.out.println();
+ }
+
+ /**
+ * test driver
+ */
+ public static void main(String[] args) {
+ test();
+ System.exit(1);
+
+ if (debug) {
+ try {
+ Base64 b64 = new Base64();
+ String str = "agfrtu¿¦etʲ1234¼Ù´óerty¿Õ234·¢¿¦2344ʲµÄ";
+ String str64 = "";
+
+ //encode
+ str64 = b64.encode(str.getBytes());
+ System.out.println(str64);
+
+ //decode
+ byte[] theBytes = b64.decode(str64);
+ show(theBytes);
+ String rst = new String(theBytes);
+ System.out.println(rst);
+ System.out.println(str);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ //getBytes(String charsetName);
+/*
+ byte[] a = { (byte)0xfc, (byte)0x0f, (byte)0xc0};
+ byte[] b = { (byte)0x03, (byte)0xf0, (byte)0x3f};
+ byte[] c = { (byte)0x00, (byte)0x00, (byte)0x00};
+ byte[] d = { (byte)0xff, (byte)0xff, (byte)0xff};
+ byte[] e = { (byte)0xfc, (byte)0x0f, (byte)0xc0, (byte)1};
+ byte[] f = { (byte)0xfc, (byte)0x0f, (byte)0xc0, (byte)1, (byte)2};
+ byte[] g = { (byte)0xfc, (byte)0x0f, (byte)0xc0, (byte)1, (byte)2, (byte)3};
+ byte[] h = "AAAAAAAAAAB".getBytes();
+
+
+
+ show(a);
+ show(b);
+ show(c);
+ show(d);
+ show(e);
+ show(f);
+ show(g);
+ show(h);
+ Base64 b64 = new Base64();
+ show(b64.decode(b64.encode(a)));
+ show(b64.decode(b64.encode(b)));
+ show(b64.decode(b64.encode(c)));
+ show(b64.decode(b64.encode(d)));
+ show(b64.decode(b64.encode(e)));
+ show(b64.decode(b64.encode(f)));
+ show(b64.decode(b64.encode(g)));
+ show(b64.decode(b64.encode(h)));
+ b64.setLineLength(8);
+ show((b64.encode(h)).getBytes());
+*/
+ }
+ }// end main
+
+ public static void test() {
+ try {
+ Base64 b64 = new Base64();
+
+ //encode
+ //str64 = b64.encode(str.getBytes());
+ //System.out.println(str64);
+
+ String str64 = "CwUEFYoAAAADjQMC7ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267ELJiY6w05267EI=";
+ //decode
+ byte[] theBytes = b64.decode(str64);
+ show(theBytes);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /* initialise defaultValueToChar and defaultCharToValue tables */
+ private void init(char chPlus, char chSplash, char chPad) {
+ int index = 0;
+ // build translate this.valueToChar table only once.
+ // 0..25 -> 'A'..'Z'
+ for (int i = 'A'; i <= 'Z'; i++) {
+ this.valueToChar[index++] = (char) i;
+ }
+
+ // 26..51 -> 'a'..'z'
+ for (int i = 'a'; i <= 'z'; i++) {
+ this.valueToChar[index++] = (char) i;
+ }
+
+ // 52..61 -> '0'..'9'
+ for (int i = '0'; i <= '9'; i++) {
+ this.valueToChar[index++] = (char) i;
+ }
+
+ this.valueToChar[index++] = chPlus;
+ this.valueToChar[index++] = chSplash;
+
+ // build translate defaultCharToValue table only once.
+ for (int i = 0; i < 256; i++) {
+ this.charToValue[i] = IGNORE; // default is to ignore
+ }
+
+ for (int i = 0; i < 64; i++) {
+ this.charToValue[this.valueToChar[i]] = i;
+ }
+
+ this.charToValue[chPad] = PAD;
+ java.util.Arrays.fill(this.charToPad, chPad);
+ }
+
+ /**
+ * Encode an arbitrary array of bytes as Base64 printable ASCII.
+ * It will be broken into lines of 72 chars each. The last line is not
+ * terminated with a line separator.
+ * The output will always have an even multiple of data characters,
+ * exclusive of \n. It is padded out with =.
+ */
+ public String encode(byte[] b) throws IOException {
+ // Each group or partial group of 3 bytes becomes four chars
+ // covered quotient
+ int outputLength = ((b.length + 2) / 3) * 4;
+
+ // account for trailing newlines, on all but the very last line
+ if (lineLength != 0) {
+ int lines = (outputLength + lineLength - 1) / lineLength - 1;
+ if (lines > 0) {
+ outputLength += lines * lineSeparator.length();
+ }
+ }
+
+ // must be local for recursion to work.
+ StringBuffer sb = new StringBuffer(outputLength);
+
+ // must be local for recursion to work.
+ int linePos = 0;
+
+ // first deal with even multiples of 3 bytes.
+ int len = (b.length / 3) * 3;
+ int leftover = b.length - len;
+ for (int i = 0; i < len; i += 3) {
+ // Start a new line if next 4 chars won't fit on the current line
+ // We can't encapsulete the following code since the variable need to
+ // be local to this incarnation of encode.
+ linePos += 4;
+ if (linePos > lineLength) {
+ if (lineLength != 0) {
+ sb.append(lineSeparator);
+ }
+ linePos = 4;
+ }
+
+ // get next three bytes in unsigned form lined up,
+ // in big-endian order
+ int combined = b[i + 0] & 0xff;
+ combined <<= 8;
+ combined |= b[i + 1] & 0xff;
+ combined <<= 8;
+ combined |= b[i + 2] & 0xff;
+
+ // break those 24 bits into a 4 groups of 6 bits,
+ // working LSB to MSB.
+ int c3 = combined & 0x3f;
+ combined >>>= 6;
+ int c2 = combined & 0x3f;
+ combined >>>= 6;
+ int c1 = combined & 0x3f;
+ combined >>>= 6;
+ int c0 = combined & 0x3f;
+
+ // Translate into the equivalent alpha character
+ // emitting them in big-endian order.
+ sb.append(valueToChar[c0]);
+ sb.append(valueToChar[c1]);
+ sb.append(valueToChar[c2]);
+ sb.append(valueToChar[c3]);
+ }
+
+ // deal with leftover bytes
+ switch (leftover) {
+ case 0:
+ default:
+ // nothing to do
+ break;
+
+ case 1:
+ // One leftover byte generates xx==
+ // Start a new line if next 4 chars won't fit on the current line
+ linePos += 4;
+ if (linePos > lineLength) {
+
+ if (lineLength != 0) {
+ sb.append(lineSeparator);
+ }
+ linePos = 4;
+ }
+
+ // Handle this recursively with a faked complete triple.
+ // Throw away last two chars and replace with ==
+ sb.append(encode(new byte[]{b[len], 0, 0}
+ ).substring(0, 2));
+ sb.append("==");
+ break;
+
+ case 2:
+ // Two leftover bytes generates xxx=
+ // Start a new line if next 4 chars won't fit on the current line
+ linePos += 4;
+ if (linePos > lineLength) {
+ if (lineLength != 0) {
+ sb.append(lineSeparator);
+ }
+ linePos = 4;
+ }
+ // Handle this recursively with a faked complete triple.
+ // Throw away last char and replace with =
+ sb.append(encode(new byte[]{b[len], b[len + 1], 0}
+ ).substring(0, 3));
+ sb.append("=");
+ break;
+
+ } // end switch;
+
+ if (outputLength != sb.length()) {
+ System.out.println("oops: minor program flaw: output length mis-estimated");
+ System.out.println("estimate:" + outputLength);
+ System.out.println("actual:" + sb.length());
+ }
+ return sb.toString();
+ }// end encode
+
+ /**
+ * decode a well-formed complete Base64 string back into an array of bytes.
+ * It must have an even multiple of 4 data characters (not counting \n),
+ * padded out with = as needed.
+ */
+ public byte[] decodeAuto(String s) {
+ int nRemain = s.length() % 4;
+ if (nRemain == 0) {
+ return this.decode(s);
+ } else {
+ return this.decode(s + new String(this.charToPad, 0, 4 - nRemain));
+ }
+ }
+
+ /**
+ * decode a well-formed complete Base64 string back into an array of bytes.
+ * It must have an even multiple of 4 data characters (not counting \n),
+ * padded out with = as needed.
+ */
+ public byte[] decode(String s) {
+
+ // estimate worst case size of output array, no embedded newlines.
+ byte[] b = new byte[(s.length() / 4) * 3];
+
+ // tracks where we are in a cycle of 4 input chars.
+ int cycle = 0;
+
+ // where we combine 4 groups of 6 bits and take apart as 3 groups of 8.
+ int combined = 0;
+
+ // how many bytes we have prepared.
+ int j = 0;
+ // will be an even multiple of 4 chars, plus some embedded \n
+ int len = s.length();
+ int dummies = 0;
+ for (int i = 0; i < len; i++) {
+
+ int c = s.charAt(i);
+ int value = (c <= 255) ? charToValue[c] : IGNORE;
+ // there are two magic values PAD (=) and IGNORE.
+ switch (value) {
+ case IGNORE:
+ // e.g. \n, just ignore it.
+ break;
+
+ case PAD:
+ value = 0;
+ dummies++;
+ // fallthrough
+ default:
+ /* regular value character */
+ switch (cycle) {
+ case 0:
+ combined = value;
+ cycle = 1;
+ break;
+
+ case 1:
+ combined <<= 6;
+ combined |= value;
+ cycle = 2;
+ break;
+
+ case 2:
+ combined <<= 6;
+ combined |= value;
+ cycle = 3;
+ break;
+
+ case 3:
+ combined <<= 6;
+ combined |= value;
+ // we have just completed a cycle of 4 chars.
+ // the four 6-bit values are in combined in big-endian order
+ // peel them off 8 bits at a time working lsb to msb
+ // to get our original 3 8-bit bytes back
+
+ b[j + 2] = (byte) combined;
+ combined >>>= 8;
+ b[j + 1] = (byte) combined;
+ combined >>>= 8;
+ b[j] = (byte) combined;
+ j += 3;
+ cycle = 0;
+ break;
+ }
+ break;
+ }
+ } // end for
+ if (cycle != 0) {
+ throw new ArrayIndexOutOfBoundsException("Input to decode not an even multiple of 4 characters; pad with =.");
+ }
+ j -= dummies;
+ if (b.length != j) {
+ byte[] b2 = new byte[j];
+ System.arraycopy(b, 0, b2, 0, j);
+ b = b2;
+ }
+ return b;
+
+ }// end decode
+
+ /**
+ * determines how long the lines are that are generated by encode.
+ * Ignored by decode.
+ *
+ * @param length 0 means no newlines inserted. Must be a multiple of 4.
+ */
+ public void setLineLength(int length) {
+ this.lineLength = (length / 4) * 4;
+ }
+
+ /**
+ * How lines are separated.
+ * Ignored by decode.
+ *
+ * @param lineSeparator may be "" but not null.
+ * Usually contains only a combination of chars \n and \r.
+ * Could be any chars not in set A-Z a-z 0-9 + /.
+ */
+ public void setLineSeparator(String lineSeparator) {
+ this.lineSeparator = lineSeparator;
+ }
+} // end Base64
+
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/common/FastdfsException.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/common/FastdfsException.java
new file mode 100644
index 000000000..428e5326c
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/common/FastdfsException.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2008 Happy Fish / YuQing
+ *
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package org.csource.common;
+
+/**
+ * My Exception
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.0
+ */
+public class FastdfsException extends Exception {
+ public FastdfsException() {
+ }
+
+ public FastdfsException(String message) {
+ super(message);
+ }
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/common/IniFileReader.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/common/IniFileReader.java
new file mode 100644
index 000000000..5b373f83c
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/common/IniFileReader.java
@@ -0,0 +1,216 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ *
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ **/
+
+package org.csource.common;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Hashtable;
+
+/**
+ * ini file reader / parser
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.0
+ */
+public class IniFileReader {
+ private Hashtable paramTable;
+ private String conf_filename;
+
+ /**
+ * @param conf_filename config filename
+ */
+ public IniFileReader(String conf_filename) throws IOException {
+ this.conf_filename = conf_filename;
+ loadFromFile(conf_filename);
+ }
+
+ public static ClassLoader classLoader() {
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ if (loader == null) {
+ loader = ClassLoader.getSystemClassLoader();
+ }
+ return loader;
+ }
+
+ public static InputStream loadFromOsFileSystemOrClasspathAsStream(String filePath) {
+ InputStream in = null;
+ try {
+ // 优先从文件系统路径加载
+ if (new File(filePath).exists()) {
+ in = new FileInputStream(filePath);
+ //System.out.println("loadFrom...file path done");
+ }
+ // 从类路径加载
+ else {
+ in = classLoader().getResourceAsStream(filePath);
+ //System.out.println("loadFrom...class path done");
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ return in;
+ }
+
+ /**
+ * get the config filename
+ *
+ * @return config filename
+ */
+ public String getConfFilename() {
+ return this.conf_filename;
+ }
+
+ /**
+ * get string value from config file
+ *
+ * @param name item name in config file
+ * @return string value
+ */
+ public String getStrValue(String name) {
+ Object obj;
+ obj = this.paramTable.get(name);
+ if (obj == null) {
+ return null;
+ }
+
+ if (obj instanceof String) {
+ return (String) obj;
+ }
+
+ return (String) ((ArrayList) obj).get(0);
+ }
+
+ /**
+ * get int value from config file
+ *
+ * @param name item name in config file
+ * @param default_value the default value
+ * @return int value
+ */
+ public int getIntValue(String name, int default_value) {
+ String szValue = this.getStrValue(name);
+ if (szValue == null) {
+ return default_value;
+ }
+
+ return Integer.parseInt(szValue);
+ }
+
+ /**
+ * get boolean value from config file
+ *
+ * @param name item name in config file
+ * @param default_value the default value
+ * @return boolean value
+ */
+ public boolean getBoolValue(String name, boolean default_value) {
+ String szValue = this.getStrValue(name);
+ if (szValue == null) {
+ return default_value;
+ }
+
+ return szValue.equalsIgnoreCase("yes") || szValue.equalsIgnoreCase("on") ||
+ szValue.equalsIgnoreCase("true") || szValue.equals("1");
+ }
+
+ /**
+ * get all values from config file
+ *
+ * @param name item name in config file
+ * @return string values (array)
+ */
+ public String[] getValues(String name) {
+ Object obj;
+ String[] values;
+
+ obj = this.paramTable.get(name);
+ if (obj == null) {
+ return null;
+ }
+
+ if (obj instanceof String) {
+ values = new String[1];
+ values[0] = (String) obj;
+ return values;
+ }
+
+ Object[] objs = ((ArrayList) obj).toArray();
+ values = new String[objs.length];
+ System.arraycopy(objs, 0, values, 0, objs.length);
+ return values;
+ }
+
+ private void loadFromFile(String confFilePath) throws IOException {
+ InputStream in = loadFromOsFileSystemOrClasspathAsStream(confFilePath);
+ try {
+ readToParamTable(in);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ } finally {
+ try {
+ if (in != null) in.close();
+ //System.out.println("loadFrom...finally...in.close(); done");
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ private void readToParamTable(InputStream in) throws IOException {
+ this.paramTable = new Hashtable();
+ if (in == null) return;
+ String line;
+ String[] parts;
+ String name;
+ String value;
+ Object obj;
+ ArrayList valueList;
+ InputStreamReader inReader = null;
+ BufferedReader bufferedReader = null;
+ try {
+ inReader = new InputStreamReader(in);
+ bufferedReader = new BufferedReader(inReader);
+ while ((line = bufferedReader.readLine()) != null) {
+ line = line.trim();
+ if (line.length() == 0 || line.charAt(0) == '#') {
+ continue;
+ }
+ parts = line.split("=", 2);
+ if (parts.length != 2) {
+ continue;
+ }
+ name = parts[0].trim();
+ value = parts[1].trim();
+ obj = this.paramTable.get(name);
+ if (obj == null) {
+ this.paramTable.put(name, value);
+ } else if (obj instanceof String) {
+ valueList = new ArrayList();
+ valueList.add(obj);
+ valueList.add(value);
+ this.paramTable.put(name, valueList);
+ } else {
+ valueList = (ArrayList) obj;
+ valueList.add(value);
+ }
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ } finally {
+ try {
+ if (bufferedReader != null) bufferedReader.close();
+ if (inReader != null) inReader.close();
+ //System.out.println("readToParamTable...finally...bufferedReader.close();inReader.close(); done");
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/common/NameValuePair.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/common/NameValuePair.java
new file mode 100644
index 000000000..dab62b79a
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/common/NameValuePair.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 Happy Fish / YuQing
+ *
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package org.csource.common;
+
+/**
+ * name(key) and value pair model
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.0
+ */
+public class NameValuePair {
+ protected String name;
+ protected String value;
+
+ public NameValuePair() {
+ }
+
+ public NameValuePair(String name) {
+ this.name = name;
+ }
+
+ public NameValuePair(String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getValue() {
+ return this.value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/ClientGlobal.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/ClientGlobal.java
new file mode 100644
index 000000000..030a8c163
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/ClientGlobal.java
@@ -0,0 +1,305 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ *
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ **/
+
+package org.csource.fastdfs;
+
+import org.csource.common.IniFileReader;
+import org.csource.common.FastdfsException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * Global variables
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.11
+ */
+public class ClientGlobal {
+
+ public static final String CONF_KEY_CONNECT_TIMEOUT = "connect_timeout";
+ public static final String CONF_KEY_NETWORK_TIMEOUT = "network_timeout";
+ public static final String CONF_KEY_CHARSET = "charset";
+ public static final String CONF_KEY_HTTP_ANTI_STEAL_TOKEN = "http.anti_steal_token";
+ public static final String CONF_KEY_HTTP_SECRET_KEY = "http.secret_key";
+ public static final String CONF_KEY_HTTP_TRACKER_HTTP_PORT = "http.tracker_http_port";
+ public static final String CONF_KEY_TRACKER_SERVER = "tracker_server";
+
+ public static final String PROP_KEY_CONNECT_TIMEOUT_IN_SECONDS = "fastdfs.connect_timeout_in_seconds";
+ public static final String PROP_KEY_NETWORK_TIMEOUT_IN_SECONDS = "fastdfs.network_timeout_in_seconds";
+ public static final String PROP_KEY_CHARSET = "fastdfs.charset";
+ public static final String PROP_KEY_HTTP_ANTI_STEAL_TOKEN = "fastdfs.http_anti_steal_token";
+ public static final String PROP_KEY_HTTP_SECRET_KEY = "fastdfs.http_secret_key";
+ public static final String PROP_KEY_HTTP_TRACKER_HTTP_PORT = "fastdfs.http_tracker_http_port";
+ public static final String PROP_KEY_TRACKER_SERVERS = "fastdfs.tracker_servers";
+
+ public static final int DEFAULT_CONNECT_TIMEOUT = 5; //second
+ public static final int DEFAULT_NETWORK_TIMEOUT = 30; //second
+ public static final String DEFAULT_CHARSET = "UTF-8";
+ public static final boolean DEFAULT_HTTP_ANTI_STEAL_TOKEN = false;
+ public static final String DEFAULT_HTTP_SECRET_KEY = "FastDFS1234567890";
+ public static final int DEFAULT_HTTP_TRACKER_HTTP_PORT = 80;
+
+ public static int g_connect_timeout = DEFAULT_CONNECT_TIMEOUT * 1000; //millisecond
+ public static int g_network_timeout = DEFAULT_NETWORK_TIMEOUT * 1000; //millisecond
+ public static String g_charset = DEFAULT_CHARSET;
+ public static boolean g_anti_steal_token = DEFAULT_HTTP_ANTI_STEAL_TOKEN; //if anti-steal token
+ public static String g_secret_key = DEFAULT_HTTP_SECRET_KEY; //generage token secret key
+ public static int g_tracker_http_port = DEFAULT_HTTP_TRACKER_HTTP_PORT;
+
+ public static TrackerGroup g_tracker_group;
+
+ private ClientGlobal() {
+ }
+
+ /**
+ * load global variables
+ *
+ * @param conf_filename config filename
+ */
+ public static void init(String conf_filename) throws IOException, FastdfsException {
+ IniFileReader iniReader;
+ String[] szTrackerServers;
+ String[] parts;
+
+ iniReader = new IniFileReader(conf_filename);
+
+ g_connect_timeout = iniReader.getIntValue("connect_timeout", DEFAULT_CONNECT_TIMEOUT);
+ if (g_connect_timeout < 0) {
+ g_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
+ }
+ g_connect_timeout *= 1000; //millisecond
+
+ g_network_timeout = iniReader.getIntValue("network_timeout", DEFAULT_NETWORK_TIMEOUT);
+ if (g_network_timeout < 0) {
+ g_network_timeout = DEFAULT_NETWORK_TIMEOUT;
+ }
+ g_network_timeout *= 1000; //millisecond
+
+ g_charset = iniReader.getStrValue("charset");
+ if (g_charset == null || g_charset.length() == 0) {
+ g_charset = "ISO8859-1";
+ }
+
+ szTrackerServers = iniReader.getValues("tracker_server");
+ if (szTrackerServers == null) {
+ throw new FastdfsException("item \"tracker_server\" in " + conf_filename + " not found");
+ }
+
+ InetSocketAddress[] tracker_servers = new InetSocketAddress[szTrackerServers.length];
+ for (int i = 0; i < szTrackerServers.length; i++) {
+ parts = szTrackerServers[i].split("\\:", 2);
+ if (parts.length != 2) {
+ throw new FastdfsException("the value of item \"tracker_server\" is invalid, the correct format is host:port");
+ }
+
+ tracker_servers[i] = new InetSocketAddress(parts[0].trim(), Integer.parseInt(parts[1].trim()));
+ }
+ g_tracker_group = new TrackerGroup(tracker_servers);
+
+ g_tracker_http_port = iniReader.getIntValue("http.tracker_http_port", 80);
+ g_anti_steal_token = iniReader.getBoolValue("http.anti_steal_token", false);
+ if (g_anti_steal_token) {
+ g_secret_key = iniReader.getStrValue("http.secret_key");
+ }
+ }
+
+ /**
+ * load from properties file
+ *
+ * @param propsFilePath properties file path, eg:
+ * "fastdfs-client.properties"
+ * "config/fastdfs-client.properties"
+ * "/opt/fastdfs-client.properties"
+ * "C:\\Users\\James\\config\\fastdfs-client.properties"
+ * properties文件至少包含一个配置项 fastdfs.tracker_servers 例如:
+ * fastdfs.tracker_servers = 10.0.11.245:22122,10.0.11.246:22122
+ * server的IP和端口用冒号':'分隔
+ * server之间用逗号','分隔
+ */
+ public static void initByProperties(String propsFilePath) throws IOException, FastdfsException {
+ Properties props = new Properties();
+ InputStream in = IniFileReader.loadFromOsFileSystemOrClasspathAsStream(propsFilePath);
+ if (in != null) {
+ props.load(in);
+ }
+ initByProperties(props);
+ }
+
+ public static void initByProperties(Properties props) throws IOException, FastdfsException {
+ String trackerServersConf = props.getProperty(PROP_KEY_TRACKER_SERVERS);
+ if (trackerServersConf == null || trackerServersConf.trim().length() == 0) {
+ throw new FastdfsException(String.format("configure item %s is required", PROP_KEY_TRACKER_SERVERS));
+ }
+ initByTrackers(trackerServersConf.trim());
+
+ String connectTimeoutInSecondsConf = props.getProperty(PROP_KEY_CONNECT_TIMEOUT_IN_SECONDS);
+ String networkTimeoutInSecondsConf = props.getProperty(PROP_KEY_NETWORK_TIMEOUT_IN_SECONDS);
+ String charsetConf = props.getProperty(PROP_KEY_CHARSET);
+ String httpAntiStealTokenConf = props.getProperty(PROP_KEY_HTTP_ANTI_STEAL_TOKEN);
+ String httpSecretKeyConf = props.getProperty(PROP_KEY_HTTP_SECRET_KEY);
+ String httpTrackerHttpPortConf = props.getProperty(PROP_KEY_HTTP_TRACKER_HTTP_PORT);
+ if (connectTimeoutInSecondsConf != null && connectTimeoutInSecondsConf.trim().length() != 0) {
+ g_connect_timeout = Integer.parseInt(connectTimeoutInSecondsConf.trim()) * 1000;
+ }
+ if (networkTimeoutInSecondsConf != null && networkTimeoutInSecondsConf.trim().length() != 0) {
+ g_network_timeout = Integer.parseInt(networkTimeoutInSecondsConf.trim()) * 1000;
+ }
+ if (charsetConf != null && charsetConf.trim().length() != 0) {
+ g_charset = charsetConf.trim();
+ }
+ if (httpAntiStealTokenConf != null && httpAntiStealTokenConf.trim().length() != 0) {
+ g_anti_steal_token = Boolean.parseBoolean(httpAntiStealTokenConf);
+ }
+ if (httpSecretKeyConf != null && httpSecretKeyConf.trim().length() != 0) {
+ g_secret_key = httpSecretKeyConf.trim();
+ }
+ if (httpTrackerHttpPortConf != null && httpTrackerHttpPortConf.trim().length() != 0) {
+ g_tracker_http_port = Integer.parseInt(httpTrackerHttpPortConf);
+ }
+ }
+
+ /**
+ * load from properties file
+ *
+ * @param trackerServers 例如:"10.0.11.245:22122,10.0.11.246:22122"
+ * server的IP和端口用冒号':'分隔
+ * server之间用逗号','分隔
+ */
+ public static void initByTrackers(String trackerServers) throws IOException, FastdfsException {
+ List list = new ArrayList();
+ String spr1 = ",";
+ String spr2 = ":";
+ String[] arr1 = trackerServers.trim().split(spr1);
+ for (String addrStr : arr1) {
+ String[] arr2 = addrStr.trim().split(spr2);
+ String host = arr2[0].trim();
+ int port = Integer.parseInt(arr2[1].trim());
+ list.add(new InetSocketAddress(host, port));
+ }
+ InetSocketAddress[] trackerAddresses = list.toArray(new InetSocketAddress[list.size()]);
+ initByTrackers(trackerAddresses);
+ }
+
+ public static void initByTrackers(InetSocketAddress[] trackerAddresses) throws IOException, FastdfsException {
+ g_tracker_group = new TrackerGroup(trackerAddresses);
+ }
+
+ /**
+ * construct Socket object
+ *
+ * @param ip_addr ip address or hostname
+ * @param port port number
+ * @return connected Socket object
+ */
+ public static Socket getSocket(String ip_addr, int port) throws IOException {
+ Socket sock = new Socket();
+ sock.setSoTimeout(ClientGlobal.g_network_timeout);
+ sock.connect(new InetSocketAddress(ip_addr, port), ClientGlobal.g_connect_timeout);
+ return sock;
+ }
+
+ /**
+ * construct Socket object
+ *
+ * @param addr InetSocketAddress object, including ip address and port
+ * @return connected Socket object
+ */
+ public static Socket getSocket(InetSocketAddress addr) throws IOException {
+ Socket sock = new Socket();
+ sock.setSoTimeout(ClientGlobal.g_network_timeout);
+ sock.connect(addr, ClientGlobal.g_connect_timeout);
+ return sock;
+ }
+
+ public static int getG_connect_timeout() {
+ return g_connect_timeout;
+ }
+
+ public static void setG_connect_timeout(int connect_timeout) {
+ ClientGlobal.g_connect_timeout = connect_timeout;
+ }
+
+ public static int getG_network_timeout() {
+ return g_network_timeout;
+ }
+
+ public static void setG_network_timeout(int network_timeout) {
+ ClientGlobal.g_network_timeout = network_timeout;
+ }
+
+ public static String getG_charset() {
+ return g_charset;
+ }
+
+ public static void setG_charset(String charset) {
+ ClientGlobal.g_charset = charset;
+ }
+
+ public static int getG_tracker_http_port() {
+ return g_tracker_http_port;
+ }
+
+ public static void setG_tracker_http_port(int tracker_http_port) {
+ ClientGlobal.g_tracker_http_port = tracker_http_port;
+ }
+
+ public static boolean getG_anti_steal_token() {
+ return g_anti_steal_token;
+ }
+
+ public static boolean isG_anti_steal_token() {
+ return g_anti_steal_token;
+ }
+
+ public static void setG_anti_steal_token(boolean anti_steal_token) {
+ ClientGlobal.g_anti_steal_token = anti_steal_token;
+ }
+
+ public static String getG_secret_key() {
+ return g_secret_key;
+ }
+
+ public static void setG_secret_key(String secret_key) {
+ ClientGlobal.g_secret_key = secret_key;
+ }
+
+ public static TrackerGroup getG_tracker_group() {
+ return g_tracker_group;
+ }
+
+ public static void setG_tracker_group(TrackerGroup tracker_group) {
+ ClientGlobal.g_tracker_group = tracker_group;
+ }
+
+ public static String configInfo() {
+ String trackerServers = "";
+ if (g_tracker_group != null) {
+ InetSocketAddress[] trackerAddresses = g_tracker_group.tracker_servers;
+ for (InetSocketAddress inetSocketAddress : trackerAddresses) {
+ if (trackerServers.length() > 0) trackerServers += ",";
+ trackerServers += inetSocketAddress.toString().substring(1);
+ }
+ }
+ return "{"
+ + "\n g_connect_timeout(ms) = " + g_connect_timeout
+ + "\n g_network_timeout(ms) = " + g_network_timeout
+ + "\n g_charset = " + g_charset
+ + "\n g_anti_steal_token = " + g_anti_steal_token
+ + "\n g_secret_key = " + g_secret_key
+ + "\n g_tracker_http_port = " + g_tracker_http_port
+ + "\n trackerServers = " + trackerServers
+ + "\n}";
+ }
+
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/DownloadCallback.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/DownloadCallback.java
new file mode 100644
index 000000000..5f31dffdd
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/DownloadCallback.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ *
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package org.csource.fastdfs;
+
+/**
+ * Download file callback interface
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.4
+ */
+public interface DownloadCallback {
+ /**
+ * recv file content callback function, may be called more than once when the file downloaded
+ *
+ * @param file_size file size
+ * @param data data buff
+ * @param bytes data bytes
+ * @return 0 success, return none zero(errno) if fail
+ */
+ public int recv(long file_size, byte[] data, int bytes);
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/DownloadStream.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/DownloadStream.java
new file mode 100644
index 000000000..2432339f1
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/DownloadStream.java
@@ -0,0 +1,44 @@
+package org.csource.fastdfs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Download file by stream (download callback class)
+ *
+ * @author zhouzezhong & Happy Fish / YuQing
+ * @version Version 1.11
+ */
+public class DownloadStream implements DownloadCallback {
+ private OutputStream out;
+ private long currentBytes = 0;
+
+ public DownloadStream(OutputStream out) {
+ super();
+ this.out = out;
+ }
+
+ /**
+ * recv file content callback function, may be called more than once when the file downloaded
+ *
+ * @param fileSize file size
+ * @param data data buff
+ * @param bytes data bytes
+ * @return 0 success, return none zero(errno) if fail
+ */
+ public int recv(long fileSize, byte[] data, int bytes) {
+ try {
+ out.write(data, 0, bytes);
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ return -1;
+ }
+
+ currentBytes += bytes;
+ if (this.currentBytes == fileSize) {
+ this.currentBytes = 0;
+ }
+
+ return 0;
+ }
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/FileInfo.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/FileInfo.java
new file mode 100644
index 000000000..e70d6de6d
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/FileInfo.java
@@ -0,0 +1,125 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ *
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package org.csource.fastdfs;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Server Info
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.23
+ */
+public class FileInfo {
+ protected String source_ip_addr;
+ protected long file_size;
+ protected Date create_timestamp;
+ protected int crc32;
+
+ /**
+ * Constructor
+ *
+ * @param file_size the file size
+ * @param create_timestamp create timestamp in seconds
+ * @param crc32 the crc32 signature
+ * @param source_ip_addr the source storage ip address
+ */
+ public FileInfo(long file_size, int create_timestamp, int crc32, String source_ip_addr) {
+ this.file_size = file_size;
+ this.create_timestamp = new Date(create_timestamp * 1000L);
+ this.crc32 = crc32;
+ this.source_ip_addr = source_ip_addr;
+ }
+
+ /**
+ * get the source ip address of the file uploaded to
+ *
+ * @return the source ip address of the file uploaded to
+ */
+ public String getSourceIpAddr() {
+ return this.source_ip_addr;
+ }
+
+ /**
+ * set the source ip address of the file uploaded to
+ *
+ * @param source_ip_addr the source ip address
+ */
+ public void setSourceIpAddr(String source_ip_addr) {
+ this.source_ip_addr = source_ip_addr;
+ }
+
+ /**
+ * get the file size
+ *
+ * @return the file size
+ */
+ public long getFileSize() {
+ return this.file_size;
+ }
+
+ /**
+ * set the file size
+ *
+ * @param file_size the file size
+ */
+ public void setFileSize(long file_size) {
+ this.file_size = file_size;
+ }
+
+ /**
+ * get the create timestamp of the file
+ *
+ * @return the create timestamp of the file
+ */
+ public Date getCreateTimestamp() {
+ return this.create_timestamp;
+ }
+
+ /**
+ * set the create timestamp of the file
+ *
+ * @param create_timestamp create timestamp in seconds
+ */
+ public void setCreateTimestamp(int create_timestamp) {
+ this.create_timestamp = new Date(create_timestamp * 1000L);
+ }
+
+ /**
+ * get the file CRC32 signature
+ *
+ * @return the file CRC32 signature
+ */
+ public long getCrc32() {
+ return this.crc32;
+ }
+
+ /**
+ * set the create timestamp of the file
+ *
+ * @param crc32 the crc32 signature
+ */
+ public void setCrc32(int crc32) {
+ this.crc32 = crc32;
+ }
+
+ /**
+ * to string
+ *
+ * @return string
+ */
+ public String toString() {
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ return "source_ip_addr = " + this.source_ip_addr + ", " +
+ "file_size = " + this.file_size + ", " +
+ "create_timestamp = " + df.format(this.create_timestamp) + ", " +
+ "crc32 = " + this.crc32;
+ }
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/ProtoCommon.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/ProtoCommon.java
new file mode 100644
index 000000000..e197c278b
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/ProtoCommon.java
@@ -0,0 +1,503 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ *
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ **/
+
+package org.csource.fastdfs;
+
+import org.csource.common.FastdfsException;
+import org.csource.common.NameValuePair;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.Socket;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+/**
+ * protocol common functions
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.18
+ */
+public class ProtoCommon {
+ public static final byte FDFS_PROTO_CMD_QUIT = 82;
+ public static final byte TRACKER_PROTO_CMD_SERVER_LIST_GROUP = 91;
+ public static final byte TRACKER_PROTO_CMD_SERVER_LIST_STORAGE = 92;
+ public static final byte TRACKER_PROTO_CMD_SERVER_DELETE_STORAGE = 93;
+ public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE = 101;
+ public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE = 102;
+ public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE = 103;
+ public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ONE = 104;
+ public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ALL = 105;
+ public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ALL = 106;
+ public static final byte TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ALL = 107;
+ public static final byte TRACKER_PROTO_CMD_RESP = 100;
+ public static final byte FDFS_PROTO_CMD_ACTIVE_TEST = 111;
+ public static final byte STORAGE_PROTO_CMD_UPLOAD_FILE = 11;
+ public static final byte STORAGE_PROTO_CMD_DELETE_FILE = 12;
+ public static final byte STORAGE_PROTO_CMD_SET_METADATA = 13;
+ public static final byte STORAGE_PROTO_CMD_DOWNLOAD_FILE = 14;
+ public static final byte STORAGE_PROTO_CMD_GET_METADATA = 15;
+ public static final byte STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE = 21;
+ public static final byte STORAGE_PROTO_CMD_QUERY_FILE_INFO = 22;
+ public static final byte STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE = 23; //create appender file
+ public static final byte STORAGE_PROTO_CMD_APPEND_FILE = 24; //append file
+ public static final byte STORAGE_PROTO_CMD_MODIFY_FILE = 34; //modify appender file
+ public static final byte STORAGE_PROTO_CMD_TRUNCATE_FILE = 36; //truncate appender file
+ public static final byte STORAGE_PROTO_CMD_RESP = TRACKER_PROTO_CMD_RESP;
+ public static final byte FDFS_STORAGE_STATUS_INIT = 0;
+ public static final byte FDFS_STORAGE_STATUS_WAIT_SYNC = 1;
+ public static final byte FDFS_STORAGE_STATUS_SYNCING = 2;
+ public static final byte FDFS_STORAGE_STATUS_IP_CHANGED = 3;
+ public static final byte FDFS_STORAGE_STATUS_DELETED = 4;
+ public static final byte FDFS_STORAGE_STATUS_OFFLINE = 5;
+ public static final byte FDFS_STORAGE_STATUS_ONLINE = 6;
+ public static final byte FDFS_STORAGE_STATUS_ACTIVE = 7;
+ public static final byte FDFS_STORAGE_STATUS_NONE = 99;
+ /**
+ * for overwrite all old metadata
+ */
+ public static final byte STORAGE_SET_METADATA_FLAG_OVERWRITE = 'O';
+ /**
+ * for replace, insert when the meta item not exist, otherwise update it
+ */
+ public static final byte STORAGE_SET_METADATA_FLAG_MERGE = 'M';
+ public static final int FDFS_PROTO_PKG_LEN_SIZE = 8;
+ public static final int FDFS_PROTO_CMD_SIZE = 1;
+ public static final int FDFS_GROUP_NAME_MAX_LEN = 16;
+ public static final int FDFS_IPADDR_SIZE = 16;
+ public static final int FDFS_DOMAIN_NAME_MAX_SIZE = 128;
+ public static final int FDFS_VERSION_SIZE = 6;
+ public static final int FDFS_STORAGE_ID_MAX_SIZE = 16;
+ public static final String FDFS_RECORD_SEPERATOR = "\u0001";
+ public static final String FDFS_FIELD_SEPERATOR = "\u0002";
+ public static final int TRACKER_QUERY_STORAGE_FETCH_BODY_LEN = FDFS_GROUP_NAME_MAX_LEN
+ + FDFS_IPADDR_SIZE - 1 + FDFS_PROTO_PKG_LEN_SIZE;
+ public static final int TRACKER_QUERY_STORAGE_STORE_BODY_LEN = FDFS_GROUP_NAME_MAX_LEN
+ + FDFS_IPADDR_SIZE + FDFS_PROTO_PKG_LEN_SIZE;
+ public static final byte FDFS_FILE_EXT_NAME_MAX_LEN = 6;
+ public static final byte FDFS_FILE_PREFIX_MAX_LEN = 16;
+ public static final byte FDFS_FILE_PATH_LEN = 10;
+ public static final byte FDFS_FILENAME_BASE64_LENGTH = 27;
+ public static final byte FDFS_TRUNK_FILE_INFO_LEN = 16;
+ public static final byte ERR_NO_ENOENT = 2;
+ public static final byte ERR_NO_EIO = 5;
+ public static final byte ERR_NO_EBUSY = 16;
+ public static final byte ERR_NO_EINVAL = 22;
+ public static final byte ERR_NO_ENOSPC = 28;
+ public static final byte ECONNREFUSED = 61;
+ public static final byte ERR_NO_EALREADY = 114;
+ public static final long INFINITE_FILE_SIZE = 256 * 1024L * 1024 * 1024 * 1024 * 1024L;
+ public static final long APPENDER_FILE_SIZE = INFINITE_FILE_SIZE;
+ public static final long TRUNK_FILE_MARK_SIZE = 512 * 1024L * 1024 * 1024 * 1024 * 1024L;
+ public static final long NORMAL_LOGIC_FILENAME_LENGTH = FDFS_FILE_PATH_LEN + FDFS_FILENAME_BASE64_LENGTH + FDFS_FILE_EXT_NAME_MAX_LEN + 1;
+ public static final long TRUNK_LOGIC_FILENAME_LENGTH = NORMAL_LOGIC_FILENAME_LENGTH + FDFS_TRUNK_FILE_INFO_LEN;
+ protected static final int PROTO_HEADER_CMD_INDEX = FDFS_PROTO_PKG_LEN_SIZE;
+ protected static final int PROTO_HEADER_STATUS_INDEX = FDFS_PROTO_PKG_LEN_SIZE + 1;
+
+ private ProtoCommon() {
+ }
+
+ public static String getStorageStatusCaption(byte status) {
+ switch (status) {
+ case FDFS_STORAGE_STATUS_INIT:
+ return "INIT";
+ case FDFS_STORAGE_STATUS_WAIT_SYNC:
+ return "WAIT_SYNC";
+ case FDFS_STORAGE_STATUS_SYNCING:
+ return "SYNCING";
+ case FDFS_STORAGE_STATUS_IP_CHANGED:
+ return "IP_CHANGED";
+ case FDFS_STORAGE_STATUS_DELETED:
+ return "DELETED";
+ case FDFS_STORAGE_STATUS_OFFLINE:
+ return "OFFLINE";
+ case FDFS_STORAGE_STATUS_ONLINE:
+ return "ONLINE";
+ case FDFS_STORAGE_STATUS_ACTIVE:
+ return "ACTIVE";
+ case FDFS_STORAGE_STATUS_NONE:
+ return "NONE";
+ default:
+ return "UNKOWN";
+ }
+ }
+
+ /**
+ * pack header by FastDFS transfer protocol
+ *
+ * @param cmd which command to send
+ * @param pkg_len package body length
+ * @param errno status code, should be (byte)0
+ * @return packed byte buffer
+ */
+ public static byte[] packHeader(byte cmd, long pkg_len, byte errno) throws UnsupportedEncodingException {
+ byte[] header;
+ byte[] hex_len;
+
+ header = new byte[FDFS_PROTO_PKG_LEN_SIZE + 2];
+ Arrays.fill(header, (byte) 0);
+
+ hex_len = ProtoCommon.long2buff(pkg_len);
+ System.arraycopy(hex_len, 0, header, 0, hex_len.length);
+ header[PROTO_HEADER_CMD_INDEX] = cmd;
+ header[PROTO_HEADER_STATUS_INDEX] = errno;
+ return header;
+ }
+
+ /**
+ * receive pack header
+ *
+ * @param in input stream
+ * @param expect_cmd expect response command
+ * @param expect_body_len expect response package body length
+ * @return RecvHeaderInfo: errno and pkg body length
+ */
+ public static RecvHeaderInfo recvHeader(InputStream in, byte expect_cmd, long expect_body_len) throws IOException {
+ byte[] header;
+ int bytes;
+ long pkg_len;
+
+ header = new byte[FDFS_PROTO_PKG_LEN_SIZE + 2];
+
+ if ((bytes = in.read(header)) != header.length) {
+ throw new IOException("recv package size " + bytes + " != " + header.length);
+ }
+
+ if (header[PROTO_HEADER_CMD_INDEX] != expect_cmd) {
+ throw new IOException("recv cmd: " + header[PROTO_HEADER_CMD_INDEX] + " is not correct, expect cmd: " + expect_cmd);
+ }
+
+ if (header[PROTO_HEADER_STATUS_INDEX] != 0) {
+ return new RecvHeaderInfo(header[PROTO_HEADER_STATUS_INDEX], 0);
+ }
+
+ pkg_len = ProtoCommon.buff2long(header, 0);
+ if (pkg_len < 0) {
+ throw new IOException("recv body length: " + pkg_len + " < 0!");
+ }
+
+ if (expect_body_len >= 0 && pkg_len != expect_body_len) {
+ throw new IOException("recv body length: " + pkg_len + " is not correct, expect length: " + expect_body_len);
+ }
+
+ return new RecvHeaderInfo((byte) 0, pkg_len);
+ }
+
+ /**
+ * receive whole pack
+ *
+ * @param in input stream
+ * @param expect_cmd expect response command
+ * @param expect_body_len expect response package body length
+ * @return RecvPackageInfo: errno and reponse body(byte buff)
+ */
+ public static RecvPackageInfo recvPackage(InputStream in, byte expect_cmd, long expect_body_len) throws IOException {
+ RecvHeaderInfo header = recvHeader(in, expect_cmd, expect_body_len);
+ if (header.errno != 0) {
+ return new RecvPackageInfo(header.errno, null);
+ }
+
+ byte[] body = new byte[(int) header.body_len];
+ int totalBytes = 0;
+ int remainBytes = (int) header.body_len;
+ int bytes;
+
+ while (totalBytes < header.body_len) {
+ if ((bytes = in.read(body, totalBytes, remainBytes)) < 0) {
+ break;
+ }
+
+ totalBytes += bytes;
+ remainBytes -= bytes;
+ }
+
+ if (totalBytes != header.body_len) {
+ throw new IOException("recv package size " + totalBytes + " != " + header.body_len);
+ }
+
+ return new RecvPackageInfo((byte) 0, body);
+ }
+
+ /**
+ * split metadata to name value pair array
+ *
+ * @param meta_buff metadata
+ * @return name value pair array
+ */
+ public static NameValuePair[] split_metadata(String meta_buff) {
+ return split_metadata(meta_buff, FDFS_RECORD_SEPERATOR, FDFS_FIELD_SEPERATOR);
+ }
+
+ /**
+ * split metadata to name value pair array
+ *
+ * @param meta_buff metadata
+ * @param recordSeperator record/row seperator
+ * @param filedSeperator field/column seperator
+ * @return name value pair array
+ */
+ public static NameValuePair[] split_metadata(String meta_buff,
+ String recordSeperator, String filedSeperator) {
+ String[] rows;
+ String[] cols;
+ NameValuePair[] meta_list;
+
+ rows = meta_buff.split(recordSeperator);
+ meta_list = new NameValuePair[rows.length];
+ for (int i = 0; i < rows.length; i++) {
+ cols = rows[i].split(filedSeperator, 2);
+ meta_list[i] = new NameValuePair(cols[0]);
+ if (cols.length == 2) {
+ meta_list[i].setValue(cols[1]);
+ }
+ }
+
+ return meta_list;
+ }
+
+ /**
+ * pack metadata array to string
+ *
+ * @param meta_list metadata array
+ * @return packed metadata
+ */
+ public static String pack_metadata(NameValuePair[] meta_list) {
+ if (meta_list.length == 0) {
+ return "";
+ }
+
+ StringBuffer sb = new StringBuffer(32 * meta_list.length);
+ sb.append(meta_list[0].getName()).append(FDFS_FIELD_SEPERATOR).append(meta_list[0].getValue());
+ for (int i = 1; i < meta_list.length; i++) {
+ sb.append(FDFS_RECORD_SEPERATOR);
+ sb.append(meta_list[i].getName()).append(FDFS_FIELD_SEPERATOR).append(meta_list[i].getValue());
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * send quit command to server and close socket
+ *
+ * @param sock the Socket object
+ */
+ public static void closeSocket(Socket sock) throws IOException {
+ byte[] header;
+ header = packHeader(FDFS_PROTO_CMD_QUIT, 0, (byte) 0);
+ sock.getOutputStream().write(header);
+ sock.close();
+ }
+
+ /**
+ * send ACTIVE_TEST command to server, test if network is ok and the server is alive
+ *
+ * @param sock the Socket object
+ */
+ public static boolean activeTest(Socket sock) throws IOException {
+ byte[] header;
+ header = packHeader(FDFS_PROTO_CMD_ACTIVE_TEST, 0, (byte) 0);
+ sock.getOutputStream().write(header);
+
+ RecvHeaderInfo headerInfo = recvHeader(sock.getInputStream(), TRACKER_PROTO_CMD_RESP, 0);
+ return headerInfo.errno == 0 ? true : false;
+ }
+
+ /**
+ * long convert to buff (big-endian)
+ *
+ * @param n long number
+ * @return 8 bytes buff
+ */
+ public static byte[] long2buff(long n) {
+ byte[] bs;
+
+ bs = new byte[8];
+ bs[0] = (byte) ((n >> 56) & 0xFF);
+ bs[1] = (byte) ((n >> 48) & 0xFF);
+ bs[2] = (byte) ((n >> 40) & 0xFF);
+ bs[3] = (byte) ((n >> 32) & 0xFF);
+ bs[4] = (byte) ((n >> 24) & 0xFF);
+ bs[5] = (byte) ((n >> 16) & 0xFF);
+ bs[6] = (byte) ((n >> 8) & 0xFF);
+ bs[7] = (byte) (n & 0xFF);
+
+ return bs;
+ }
+
+ /**
+ * buff convert to long
+ *
+ * @param bs the buffer (big-endian)
+ * @param offset the start position based 0
+ * @return long number
+ */
+ public static long buff2long(byte[] bs, int offset) {
+ return (((long) (bs[offset] >= 0 ? bs[offset] : 256 + bs[offset])) << 56) |
+ (((long) (bs[offset + 1] >= 0 ? bs[offset + 1] : 256 + bs[offset + 1])) << 48) |
+ (((long) (bs[offset + 2] >= 0 ? bs[offset + 2] : 256 + bs[offset + 2])) << 40) |
+ (((long) (bs[offset + 3] >= 0 ? bs[offset + 3] : 256 + bs[offset + 3])) << 32) |
+ (((long) (bs[offset + 4] >= 0 ? bs[offset + 4] : 256 + bs[offset + 4])) << 24) |
+ (((long) (bs[offset + 5] >= 0 ? bs[offset + 5] : 256 + bs[offset + 5])) << 16) |
+ (((long) (bs[offset + 6] >= 0 ? bs[offset + 6] : 256 + bs[offset + 6])) << 8) |
+ ((long) (bs[offset + 7] >= 0 ? bs[offset + 7] : 256 + bs[offset + 7]));
+ }
+
+ /**
+ * buff convert to int
+ *
+ * @param bs the buffer (big-endian)
+ * @param offset the start position based 0
+ * @return int number
+ */
+ public static int buff2int(byte[] bs, int offset) {
+ return (((int) (bs[offset] >= 0 ? bs[offset] : 256 + bs[offset])) << 24) |
+ (((int) (bs[offset + 1] >= 0 ? bs[offset + 1] : 256 + bs[offset + 1])) << 16) |
+ (((int) (bs[offset + 2] >= 0 ? bs[offset + 2] : 256 + bs[offset + 2])) << 8) |
+ ((int) (bs[offset + 3] >= 0 ? bs[offset + 3] : 256 + bs[offset + 3]));
+ }
+
+ /**
+ * buff convert to ip address
+ *
+ * @param bs the buffer (big-endian)
+ * @param offset the start position based 0
+ * @return ip address
+ */
+ public static String getIpAddress(byte[] bs, int offset) {
+ if (bs[0] == 0 || bs[3] == 0) //storage server ID
+ {
+ return "";
+ }
+
+ int n;
+ StringBuilder sbResult = new StringBuilder(16);
+ for (int i = offset; i < offset + 4; i++) {
+ n = (bs[i] >= 0) ? bs[i] : 256 + bs[i];
+ if (sbResult.length() > 0) {
+ sbResult.append(".");
+ }
+ sbResult.append(String.valueOf(n));
+ }
+
+ return sbResult.toString();
+ }
+
+ /**
+ * md5 function
+ *
+ * @param source the input buffer
+ * @return md5 string
+ */
+ public static String md5(byte[] source) throws NoSuchAlgorithmException {
+ char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
+ md.update(source);
+ byte tmp[] = md.digest();
+ char str[] = new char[32];
+ int k = 0;
+ for (int i = 0; i < 16; i++) {
+ str[k++] = hexDigits[tmp[i] >>> 4 & 0xf];
+ str[k++] = hexDigits[tmp[i] & 0xf];
+ }
+
+ return new String(str);
+ }
+
+ /**
+ * get token for file URL
+ *
+ * @param remote_filename the filename return by FastDFS server
+ * @param ts unix timestamp, unit: second
+ * @param secret_key the secret key
+ * @return token string
+ */
+ public static String getToken(String remote_filename, int ts, String secret_key) throws UnsupportedEncodingException, NoSuchAlgorithmException, FastdfsException {
+ byte[] bsFilename = remote_filename.getBytes(ClientGlobal.g_charset);
+ byte[] bsKey = secret_key.getBytes(ClientGlobal.g_charset);
+ byte[] bsTimestamp = (new Integer(ts)).toString().getBytes(ClientGlobal.g_charset);
+
+ byte[] buff = new byte[bsFilename.length + bsKey.length + bsTimestamp.length];
+ System.arraycopy(bsFilename, 0, buff, 0, bsFilename.length);
+ System.arraycopy(bsKey, 0, buff, bsFilename.length, bsKey.length);
+ System.arraycopy(bsTimestamp, 0, buff, bsFilename.length + bsKey.length, bsTimestamp.length);
+
+ return md5(buff);
+ }
+
+ /**
+ * generate slave filename
+ *
+ * @param master_filename the master filename to generate the slave filename
+ * @param prefix_name the prefix name to generate the slave filename
+ * @param ext_name the extension name of slave filename, null for same as the master extension name
+ * @return slave filename string
+ */
+ public static String genSlaveFilename(String master_filename,
+ String prefix_name, String ext_name) throws FastdfsException {
+ String true_ext_name;
+ int dotIndex;
+
+ if (master_filename.length() < 28 + FDFS_FILE_EXT_NAME_MAX_LEN) {
+ throw new FastdfsException("master filename \"" + master_filename + "\" is invalid");
+ }
+
+ dotIndex = master_filename.indexOf('.', master_filename.length() - (FDFS_FILE_EXT_NAME_MAX_LEN + 1));
+ if (ext_name != null) {
+ if (ext_name.length() == 0) {
+ true_ext_name = "";
+ } else if (ext_name.charAt(0) == '.') {
+ true_ext_name = ext_name;
+ } else {
+ true_ext_name = "." + ext_name;
+ }
+ } else {
+ if (dotIndex < 0) {
+ true_ext_name = "";
+ } else {
+ true_ext_name = master_filename.substring(dotIndex);
+ }
+ }
+
+ if (true_ext_name.length() == 0 && prefix_name.equals("-m")) {
+ throw new FastdfsException("prefix_name \"" + prefix_name + "\" is invalid");
+ }
+
+ if (dotIndex < 0) {
+ return master_filename + prefix_name + true_ext_name;
+ } else {
+ return master_filename.substring(0, dotIndex) + prefix_name + true_ext_name;
+ }
+ }
+
+ /**
+ * receive package info
+ */
+ public static class RecvPackageInfo {
+ public byte errno;
+ public byte[] body;
+
+ public RecvPackageInfo(byte errno, byte[] body) {
+ this.errno = errno;
+ this.body = body;
+ }
+ }
+
+ /**
+ * receive header info
+ */
+ public static class RecvHeaderInfo {
+ public byte errno;
+ public long body_len;
+
+ public RecvHeaderInfo(byte errno, long body_len) {
+ this.errno = errno;
+ this.body_len = body_len;
+ }
+ }
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/ProtoStructDecoder.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/ProtoStructDecoder.java
new file mode 100644
index 000000000..df7ec6c78
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/ProtoStructDecoder.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ *
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package org.csource.fastdfs;
+
+import java.io.IOException;
+import java.lang.reflect.Array;
+
+/**
+ * C struct body decoder
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.17
+ */
+public class ProtoStructDecoder {
+ /**
+ * Constructor
+ */
+ public ProtoStructDecoder() {
+ }
+
+ /**
+ * decode byte buffer
+ */
+ public T[] decode(byte[] bs, Class clazz, int fieldsTotalSize) throws Exception {
+ if (bs.length % fieldsTotalSize != 0) {
+ throw new IOException("byte array length: " + bs.length + " is invalid!");
+ }
+
+ int count = bs.length / fieldsTotalSize;
+ int offset;
+ T[] results = (T[]) Array.newInstance(clazz, count);
+
+ offset = 0;
+ for (int i = 0; i < results.length; i++) {
+ results[i] = clazz.newInstance();
+ results[i].setFields(bs, offset);
+ offset += fieldsTotalSize;
+ }
+
+ return results;
+ }
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/ServerInfo.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/ServerInfo.java
new file mode 100644
index 000000000..f1c43a620
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/ServerInfo.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ *
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package org.csource.fastdfs;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+/**
+ * Server Info
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.7
+ */
+public class ServerInfo {
+ protected String ip_addr;
+ protected int port;
+
+ /**
+ * Constructor
+ *
+ * @param ip_addr address of the server
+ * @param port the port of the server
+ */
+ public ServerInfo(String ip_addr, int port) {
+ this.ip_addr = ip_addr;
+ this.port = port;
+ }
+
+ /**
+ * return the ip address
+ *
+ * @return the ip address
+ */
+ public String getIpAddr() {
+ return this.ip_addr;
+ }
+
+ /**
+ * return the port of the server
+ *
+ * @return the port of the server
+ */
+ public int getPort() {
+ return this.port;
+ }
+
+ /**
+ * connect to server
+ *
+ * @return connected Socket object
+ */
+ public Socket connect() throws IOException {
+ Socket sock = new Socket();
+ sock.setReuseAddress(true);
+ sock.setSoTimeout(ClientGlobal.g_network_timeout);
+ sock.connect(new InetSocketAddress(this.ip_addr, this.port), ClientGlobal.g_connect_timeout);
+ return sock;
+ }
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/StorageClient.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/StorageClient.java
new file mode 100644
index 000000000..ff5c636d0
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/StorageClient.java
@@ -0,0 +1,1791 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ *
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package org.csource.fastdfs;
+
+import org.csource.common.Base64;
+import org.csource.common.FastdfsException;
+import org.csource.common.NameValuePair;
+
+import java.io.*;
+import java.net.Socket;
+import java.util.Arrays;
+
+/**
+ * Storage client for 2 fields file id: group name and filename
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.24
+ */
+public class StorageClient {
+ public final static Base64 base64 = new Base64('-', '_', '.', 0);
+ protected TrackerServer trackerServer;
+ protected StorageServer storageServer;
+ protected byte errno;
+
+ /**
+ * constructor using global settings in class ClientGlobal
+ */
+ public StorageClient() {
+ this.trackerServer = null;
+ this.storageServer = null;
+ }
+
+ // 增加获取TrackerServer的方法
+ public TrackerServer getTrackerServer() {
+ return this.trackerServer;
+ }
+
+ /**
+ * constructor with tracker server and storage server
+ *
+ * @param trackerServer the tracker server, can be null
+ * @param storageServer the storage server, can be null
+ */
+ public StorageClient(TrackerServer trackerServer, StorageServer storageServer) {
+ this.trackerServer = trackerServer;
+ this.storageServer = storageServer;
+ }
+
+ /**
+ * get the error code of last call
+ *
+ * @return the error code of last call
+ */
+ public byte getErrorCode() {
+ return this.errno;
+ }
+
+ /**
+ * upload file to storage server (by file name)
+ *
+ * @param local_filename local filename to upload
+ * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename
+ * @param meta_list meta info array
+ * @return 2 elements string array if success:
+ *
results[0]: the group name to store the file
+ *
results[1]: the new created filename
+ * return null if fail
+ */
+ public String[] upload_file(String local_filename, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ final String group_name = null;
+ return this.upload_file(group_name, local_filename, file_ext_name, meta_list);
+ }
+
+ /**
+ * upload file to storage server (by file name)
+ *
+ * @param group_name the group name to upload file to, can be empty
+ * @param local_filename local filename to upload
+ * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename
+ * @param meta_list meta info array
+ * @return 2 elements string array if success:
+ *
results[0]: the group name to store the file
+ *
results[1]: the new created filename
+ * return null if fail
+ */
+ protected String[] upload_file(String group_name, String local_filename, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ final byte cmd = ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_FILE;
+ return this.upload_file(cmd, group_name, local_filename, file_ext_name, meta_list);
+ }
+
+ /**
+ * upload file to storage server (by file name)
+ *
+ * @param cmd the command
+ * @param group_name the group name to upload file to, can be empty
+ * @param local_filename local filename to upload
+ * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename
+ * @param meta_list meta info array
+ * @return 2 elements string array if success:
+ *
results[0]: the group name to store the file
+ *
results[1]: the new created filename
+ * return null if fail
+ */
+ protected String[] upload_file(byte cmd, String group_name, String local_filename, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ File f = new File(local_filename);
+ FileInputStream fis = new FileInputStream(f);
+
+ if (file_ext_name == null) {
+ int nPos = local_filename.lastIndexOf('.');
+ if (nPos > 0 && local_filename.length() - nPos <= ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + 1) {
+ file_ext_name = local_filename.substring(nPos + 1);
+ }
+ }
+
+ try {
+ return this.do_upload_file(cmd, group_name, null, null, file_ext_name,
+ f.length(), new UploadStream(fis, f.length()), meta_list);
+ } finally {
+ fis.close();
+ }
+ }
+
+ /**
+ * upload file to storage server (by file buff)
+ *
+ * @param file_buff file content/buff
+ * @param offset start offset of the buff
+ * @param length the length of buff to upload
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return 2 elements string array if success:
+ *
results[0]: the group name to store the file
+ *
results[1]: the new created filename
+ * return null if fail
+ */
+ public String[] upload_file(byte[] file_buff, int offset, int length, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ final String group_name = null;
+ return this.upload_file(group_name, file_buff, offset, length, file_ext_name, meta_list);
+ }
+
+ /**
+ * upload file to storage server (by file buff)
+ *
+ * @param group_name the group name to upload file to, can be empty
+ * @param file_buff file content/buff
+ * @param offset start offset of the buff
+ * @param length the length of buff to upload
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return 2 elements string array if success:
+ *
results[0]: the group name to store the file
+ *
results[1]: the new created filename
+ * return null if fail
+ */
+ public String[] upload_file(String group_name, byte[] file_buff, int offset, int length,
+ String file_ext_name, NameValuePair[] meta_list) throws IOException, FastdfsException {
+ return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_FILE, group_name, null, null, file_ext_name,
+ length, new UploadBuff(file_buff, offset, length), meta_list);
+ }
+
+ /**
+ * upload file to storage server (by file buff)
+ *
+ * @param file_buff file content/buff
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return 2 elements string array if success:
+ *
results[0]: the group name to store the file
+ *
results[1]: the new created filename
+ * return null if fail
+ */
+ public String[] upload_file(byte[] file_buff, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ final String group_name = null;
+ return this.upload_file(group_name, file_buff, 0, file_buff.length, file_ext_name, meta_list);
+ }
+
+ /**
+ * upload file to storage server (by file buff)
+ *
+ * @param group_name the group name to upload file to, can be empty
+ * @param file_buff file content/buff
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return 2 elements string array if success:
+ *
results[0]: the group name to store the file
+ *
results[1]: the new created filename
+ * return null if fail
+ */
+ public String[] upload_file(String group_name, byte[] file_buff,
+ String file_ext_name, NameValuePair[] meta_list) throws IOException, FastdfsException {
+ return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_FILE, group_name, null, null, file_ext_name,
+ file_buff.length, new UploadBuff(file_buff, 0, file_buff.length), meta_list);
+ }
+
+ /**
+ * upload file to storage server (by callback)
+ *
+ * @param group_name the group name to upload file to, can be empty
+ * @param file_size the file size
+ * @param callback the write data callback object
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return 2 elements string array if success:
+ *
results[0]: the group name to store the file
+ *
results[1]: the new created filename
+ * return null if fail
+ */
+ public String[] upload_file(String group_name, long file_size, UploadCallback callback,
+ String file_ext_name, NameValuePair[] meta_list) throws IOException, FastdfsException {
+ final String master_filename = null;
+ final String prefix_name = null;
+
+ return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_FILE, group_name, master_filename, prefix_name,
+ file_ext_name, file_size, callback, meta_list);
+ }
+
+ /**
+ * upload file to storage server (by file name, slave file mode)
+ *
+ * @param group_name the group name of master file
+ * @param master_filename the master file name to generate the slave file
+ * @param prefix_name the prefix name to generate the slave file
+ * @param local_filename local filename to upload
+ * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename
+ * @param meta_list meta info array
+ * @return 2 elements string array if success:
+ *
results[0]: the group name to store the file
+ *
results[1]: the new created filename
+ * return null if fail
+ */
+ public String[] upload_file(String group_name, String master_filename, String prefix_name,
+ String local_filename, String file_ext_name, NameValuePair[] meta_list) throws IOException, FastdfsException {
+ if ((group_name == null || group_name.length() == 0) ||
+ (master_filename == null || master_filename.length() == 0) ||
+ (prefix_name == null)) {
+ throw new FastdfsException("invalid arguement");
+ }
+
+ File f = new File(local_filename);
+ FileInputStream fis = new FileInputStream(f);
+
+ if (file_ext_name == null) {
+ int nPos = local_filename.lastIndexOf('.');
+ if (nPos > 0 && local_filename.length() - nPos <= ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + 1) {
+ file_ext_name = local_filename.substring(nPos + 1);
+ }
+ }
+
+ try {
+ return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE, group_name, master_filename, prefix_name,
+ file_ext_name, f.length(), new UploadStream(fis, f.length()), meta_list);
+ } finally {
+ fis.close();
+ }
+ }
+
+ /**
+ * upload file to storage server (by file buff, slave file mode)
+ *
+ * @param group_name the group name of master file
+ * @param master_filename the master file name to generate the slave file
+ * @param prefix_name the prefix name to generate the slave file
+ * @param file_buff file content/buff
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return 2 elements string array if success:
+ *
results[0]: the group name to store the file
+ *
results[1]: the new created filename
+ * return null if fail
+ */
+ public String[] upload_file(String group_name, String master_filename, String prefix_name,
+ byte[] file_buff, String file_ext_name, NameValuePair[] meta_list) throws IOException, FastdfsException {
+ if ((group_name == null || group_name.length() == 0) ||
+ (master_filename == null || master_filename.length() == 0) ||
+ (prefix_name == null)) {
+ throw new FastdfsException("invalid arguement");
+ }
+
+ return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE, group_name, master_filename, prefix_name,
+ file_ext_name, file_buff.length, new UploadBuff(file_buff, 0, file_buff.length), meta_list);
+ }
+
+ /**
+ * upload file to storage server (by file buff, slave file mode)
+ *
+ * @param group_name the group name of master file
+ * @param master_filename the master file name to generate the slave file
+ * @param prefix_name the prefix name to generate the slave file
+ * @param file_buff file content/buff
+ * @param offset start offset of the buff
+ * @param length the length of buff to upload
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return 2 elements string array if success:
+ *
results[0]: the group name to store the file
+ *
results[1]: the new created filename
+ * return null if fail
+ */
+ public String[] upload_file(String group_name, String master_filename, String prefix_name,
+ byte[] file_buff, int offset, int length, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ if ((group_name == null || group_name.length() == 0) ||
+ (master_filename == null || master_filename.length() == 0) ||
+ (prefix_name == null)) {
+ throw new FastdfsException("invalid arguement");
+ }
+
+ return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE, group_name, master_filename, prefix_name,
+ file_ext_name, length, new UploadBuff(file_buff, offset, length), meta_list);
+ }
+
+ /**
+ * upload file to storage server (by callback, slave file mode)
+ *
+ * @param group_name the group name to upload file to, can be empty
+ * @param master_filename the master file name to generate the slave file
+ * @param prefix_name the prefix name to generate the slave file
+ * @param file_size the file size
+ * @param callback the write data callback object
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return 2 elements string array if success:
+ *
results[0]: the group name to store the file
+ *
results[1]: the new created filename
+ * return null if fail
+ */
+ public String[] upload_file(String group_name, String master_filename,
+ String prefix_name, long file_size, UploadCallback callback,
+ String file_ext_name, NameValuePair[] meta_list) throws IOException, FastdfsException {
+ return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_SLAVE_FILE, group_name, master_filename, prefix_name,
+ file_ext_name, file_size, callback, meta_list);
+ }
+
+ /**
+ * upload appender file to storage server (by file name)
+ *
+ * @param local_filename local filename to upload
+ * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename
+ * @param meta_list meta info array
+ * @return 2 elements string array if success:
+ *
results[0]: the group name to store the file
+ *
results[1]: the new created filename
+ * return null if fail
+ */
+ public String[] upload_appender_file(String local_filename, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ final String group_name = null;
+ return this.upload_appender_file(group_name, local_filename, file_ext_name, meta_list);
+ }
+
+ /**
+ * upload appender file to storage server (by file name)
+ *
+ * @param group_name the group name to upload file to, can be empty
+ * @param local_filename local filename to upload
+ * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename
+ * @param meta_list meta info array
+ * @return 2 elements string array if success:
+ *
results[0]: the group name to store the file
+ *
results[1]: the new created filename
+ * return null if fail
+ */
+ protected String[] upload_appender_file(String group_name, String local_filename, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ final byte cmd = ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE;
+ return this.upload_file(cmd, group_name, local_filename, file_ext_name, meta_list);
+ }
+
+ /**
+ * upload appender file to storage server (by file buff)
+ *
+ * @param file_buff file content/buff
+ * @param offset start offset of the buff
+ * @param length the length of buff to upload
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return 2 elements string array if success:
+ *
results[0]: the group name to store the file
+ *
results[1]: the new created filename
+ * return null if fail
+ */
+ public String[] upload_appender_file(byte[] file_buff, int offset, int length, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ final String group_name = null;
+ return this.upload_appender_file(group_name, file_buff, offset, length, file_ext_name, meta_list);
+ }
+
+ /**
+ * upload appender file to storage server (by file buff)
+ *
+ * @param group_name the group name to upload file to, can be empty
+ * @param file_buff file content/buff
+ * @param offset start offset of the buff
+ * @param length the length of buff to upload
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return 2 elements string array if success:
+ *
results[0]: the group name to store the file
+ *
results[1]: the new created filename
+ * return null if fail
+ */
+ public String[] upload_appender_file(String group_name, byte[] file_buff, int offset, int length,
+ String file_ext_name, NameValuePair[] meta_list) throws IOException, FastdfsException {
+ return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, group_name, null, null, file_ext_name,
+ length, new UploadBuff(file_buff, offset, length), meta_list);
+ }
+
+ /**
+ * upload appender file to storage server (by file buff)
+ *
+ * @param file_buff file content/buff
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return 2 elements string array if success:
+ *
results[0]: the group name to store the file
+ *
results[1]: the new created filename
+ * return null if fail
+ */
+ public String[] upload_appender_file(byte[] file_buff, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ final String group_name = null;
+ return this.upload_appender_file(group_name, file_buff, 0, file_buff.length, file_ext_name, meta_list);
+ }
+
+ /**
+ * upload appender file to storage server (by file buff)
+ *
+ * @param group_name the group name to upload file to, can be empty
+ * @param file_buff file content/buff
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return 2 elements string array if success:
+ *
results[0]: the group name to store the file
+ *
results[1]: the new created filename
+ * return null if fail
+ */
+ public String[] upload_appender_file(String group_name, byte[] file_buff,
+ String file_ext_name, NameValuePair[] meta_list) throws IOException, FastdfsException {
+ return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, group_name, null, null, file_ext_name,
+ file_buff.length, new UploadBuff(file_buff, 0, file_buff.length), meta_list);
+ }
+
+ /**
+ * upload appender file to storage server (by callback)
+ *
+ * @param group_name the group name to upload file to, can be empty
+ * @param file_size the file size
+ * @param callback the write data callback object
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return 2 elements string array if success:
+ *
results[0]: the group name to store the file
+ *
results[1]: the new created filename
+ * return null if fail
+ */
+ public String[] upload_appender_file(String group_name, long file_size, UploadCallback callback,
+ String file_ext_name, NameValuePair[] meta_list) throws IOException, FastdfsException {
+ final String master_filename = null;
+ final String prefix_name = null;
+
+ return this.do_upload_file(ProtoCommon.STORAGE_PROTO_CMD_UPLOAD_APPENDER_FILE, group_name, master_filename, prefix_name,
+ file_ext_name, file_size, callback, meta_list);
+ }
+
+ /**
+ * append file to storage server (by file name)
+ *
+ * @param group_name the group name of appender file
+ * @param appender_filename the appender filename
+ * @param local_filename local filename to append
+ * @return 0 for success, != 0 for error (error no)
+ */
+ public int append_file(String group_name, String appender_filename, String local_filename) throws IOException, FastdfsException {
+ File f = new File(local_filename);
+ FileInputStream fis = new FileInputStream(f);
+
+ try {
+ return this.do_append_file(group_name, appender_filename, f.length(), new UploadStream(fis, f.length()));
+ } finally {
+ fis.close();
+ }
+ }
+
+ /**
+ * append file to storage server (by file buff)
+ *
+ * @param group_name the group name of appender file
+ * @param appender_filename the appender filename
+ * @param file_buff file content/buff
+ * @return 0 for success, != 0 for error (error no)
+ */
+ public int append_file(String group_name, String appender_filename, byte[] file_buff) throws IOException, FastdfsException {
+ return this.do_append_file(group_name, appender_filename, file_buff.length, new UploadBuff(file_buff, 0, file_buff.length));
+ }
+
+ /**
+ * append file to storage server (by file buff)
+ *
+ * @param group_name the group name of appender file
+ * @param appender_filename the appender filename
+ * @param file_buff file content/buff
+ * @param offset start offset of the buff
+ * @param length the length of buff to append
+ * @return 0 for success, != 0 for error (error no)
+ */
+ public int append_file(String group_name, String appender_filename,
+ byte[] file_buff, int offset, int length) throws IOException, FastdfsException {
+ return this.do_append_file(group_name, appender_filename, length, new UploadBuff(file_buff, offset, length));
+ }
+
+ /**
+ * append file to storage server (by callback)
+ *
+ * @param group_name the group name to append file to
+ * @param appender_filename the appender filename
+ * @param file_size the file size
+ * @param callback the write data callback object
+ * @return 0 for success, != 0 for error (error no)
+ */
+ public int append_file(String group_name, String appender_filename,
+ long file_size, UploadCallback callback) throws IOException, FastdfsException {
+ return this.do_append_file(group_name, appender_filename, file_size, callback);
+ }
+
+ /**
+ * modify appender file to storage server (by file name)
+ *
+ * @param group_name the group name of appender file
+ * @param appender_filename the appender filename
+ * @param file_offset the offset of appender file
+ * @param local_filename local filename to append
+ * @return 0 for success, != 0 for error (error no)
+ */
+ public int modify_file(String group_name, String appender_filename,
+ long file_offset, String local_filename) throws IOException, FastdfsException {
+ File f = new File(local_filename);
+ FileInputStream fis = new FileInputStream(f);
+
+ try {
+ return this.do_modify_file(group_name, appender_filename, file_offset,
+ f.length(), new UploadStream(fis, f.length()));
+ } finally {
+ fis.close();
+ }
+ }
+
+ /**
+ * modify appender file to storage server (by file buff)
+ *
+ * @param group_name the group name of appender file
+ * @param appender_filename the appender filename
+ * @param file_offset the offset of appender file
+ * @param file_buff file content/buff
+ * @return 0 for success, != 0 for error (error no)
+ */
+ public int modify_file(String group_name, String appender_filename,
+ long file_offset, byte[] file_buff) throws IOException, FastdfsException {
+ return this.do_modify_file(group_name, appender_filename, file_offset,
+ file_buff.length, new UploadBuff(file_buff, 0, file_buff.length));
+ }
+
+ /**
+ * modify appender file to storage server (by file buff)
+ *
+ * @param group_name the group name of appender file
+ * @param appender_filename the appender filename
+ * @param file_offset the offset of appender file
+ * @param file_buff file content/buff
+ * @param buffer_offset start offset of the buff
+ * @param buffer_length the length of buff to modify
+ * @return 0 for success, != 0 for error (error no)
+ */
+ public int modify_file(String group_name, String appender_filename,
+ long file_offset, byte[] file_buff, int buffer_offset, int buffer_length) throws IOException, FastdfsException {
+ return this.do_modify_file(group_name, appender_filename, file_offset,
+ buffer_length, new UploadBuff(file_buff, buffer_offset, buffer_length));
+ }
+
+ /**
+ * modify appender file to storage server (by callback)
+ *
+ * @param group_name the group name to modify file to
+ * @param appender_filename the appender filename
+ * @param file_offset the offset of appender file
+ * @param modify_size the modify size
+ * @param callback the write data callback object
+ * @return 0 for success, != 0 for error (error no)
+ */
+ public int modify_file(String group_name, String appender_filename,
+ long file_offset, long modify_size, UploadCallback callback) throws IOException, FastdfsException {
+ return this.do_modify_file(group_name, appender_filename, file_offset,
+ modify_size, callback);
+ }
+
+ /**
+ * upload file to storage server
+ *
+ * @param cmd the command code
+ * @param group_name the group name to upload file to, can be empty
+ * @param master_filename the master file name to generate the slave file
+ * @param prefix_name the prefix name to generate the slave file
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param file_size the file size
+ * @param callback the write data callback object
+ * @param meta_list meta info array
+ * @return 2 elements string array if success:
+ *
ProtoCommon.STORAGE_SET_METADATA_FLAG_OVERWRITE: overwrite all old
+ * metadata items
+ *
ProtoCommon.STORAGE_SET_METADATA_FLAG_MERGE: merge, insert when
+ * the metadata item not exist, otherwise update it
+ * @return 0 for success, !=0 fail (error code)
+ */
+ public int set_metadata(String group_name, String remote_filename,
+ NameValuePair[] meta_list, byte op_flag) throws IOException, FastdfsException {
+ boolean bNewConnection = this.newUpdatableStorageConnection(group_name, remote_filename);
+ Socket storageSocket = this.storageServer.getSocket();
+
+ try {
+ byte[] header;
+ byte[] groupBytes;
+ byte[] filenameBytes;
+ byte[] meta_buff;
+ byte[] bs;
+ int groupLen;
+ byte[] sizeBytes;
+ ProtoCommon.RecvPackageInfo pkgInfo;
+
+ if (meta_list == null) {
+ meta_buff = new byte[0];
+ } else {
+ meta_buff = ProtoCommon.pack_metadata(meta_list).getBytes(ClientGlobal.g_charset);
+ }
+
+ filenameBytes = remote_filename.getBytes(ClientGlobal.g_charset);
+ sizeBytes = new byte[2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE];
+ Arrays.fill(sizeBytes, (byte) 0);
+
+ bs = ProtoCommon.long2buff(filenameBytes.length);
+ System.arraycopy(bs, 0, sizeBytes, 0, bs.length);
+ bs = ProtoCommon.long2buff(meta_buff.length);
+ System.arraycopy(bs, 0, sizeBytes, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE, bs.length);
+
+ groupBytes = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN];
+ bs = group_name.getBytes(ClientGlobal.g_charset);
+
+ Arrays.fill(groupBytes, (byte) 0);
+ if (bs.length <= groupBytes.length) {
+ groupLen = bs.length;
+ } else {
+ groupLen = groupBytes.length;
+ }
+ System.arraycopy(bs, 0, groupBytes, 0, groupLen);
+
+ header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_SET_METADATA,
+ 2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE + 1 + groupBytes.length
+ + filenameBytes.length + meta_buff.length, (byte) 0);
+ OutputStream out = storageSocket.getOutputStream();
+ byte[] wholePkg = new byte[header.length + sizeBytes.length + 1 + groupBytes.length + filenameBytes.length];
+ System.arraycopy(header, 0, wholePkg, 0, header.length);
+ System.arraycopy(sizeBytes, 0, wholePkg, header.length, sizeBytes.length);
+ wholePkg[header.length + sizeBytes.length] = op_flag;
+ System.arraycopy(groupBytes, 0, wholePkg, header.length + sizeBytes.length + 1, groupBytes.length);
+ System.arraycopy(filenameBytes, 0, wholePkg, header.length + sizeBytes.length + 1 + groupBytes.length, filenameBytes.length);
+ out.write(wholePkg);
+ if (meta_buff.length > 0) {
+ out.write(meta_buff);
+ }
+
+ pkgInfo = ProtoCommon.recvPackage(storageSocket.getInputStream(),
+ ProtoCommon.STORAGE_PROTO_CMD_RESP, 0);
+
+ this.errno = pkgInfo.errno;
+ return pkgInfo.errno;
+ } catch (IOException ex) {
+ if (!bNewConnection) {
+ try {
+ this.storageServer.close();
+ } catch (IOException ex1) {
+ ex1.printStackTrace();
+ } finally {
+ this.storageServer = null;
+ }
+ }
+
+ throw ex;
+ } finally {
+ if (bNewConnection) {
+ try {
+ this.storageServer.close();
+ } catch (IOException ex1) {
+ ex1.printStackTrace();
+ } finally {
+ this.storageServer = null;
+ }
+ }
+ }
+ }
+
+ /**
+ * get file info decoded from the filename, fetch from the storage if necessary
+ *
+ * @param group_name the group name
+ * @param remote_filename the filename
+ * @return FileInfo object for success, return null for fail
+ */
+ public FileInfo get_file_info(String group_name, String remote_filename) throws IOException, FastdfsException {
+ if (remote_filename.length() < ProtoCommon.FDFS_FILE_PATH_LEN + ProtoCommon.FDFS_FILENAME_BASE64_LENGTH
+ + ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + 1) {
+ this.errno = ProtoCommon.ERR_NO_EINVAL;
+ return null;
+ }
+
+ byte[] buff = base64.decodeAuto(remote_filename.substring(ProtoCommon.FDFS_FILE_PATH_LEN,
+ ProtoCommon.FDFS_FILE_PATH_LEN + ProtoCommon.FDFS_FILENAME_BASE64_LENGTH));
+
+ long file_size = ProtoCommon.buff2long(buff, 4 * 2);
+ if (((remote_filename.length() > ProtoCommon.TRUNK_LOGIC_FILENAME_LENGTH) ||
+ ((remote_filename.length() > ProtoCommon.NORMAL_LOGIC_FILENAME_LENGTH) && ((file_size & ProtoCommon.TRUNK_FILE_MARK_SIZE) == 0))) ||
+ ((file_size & ProtoCommon.APPENDER_FILE_SIZE) != 0)) { //slave file or appender file
+ FileInfo fi = this.query_file_info(group_name, remote_filename);
+ if (fi == null) {
+ return null;
+ }
+ return fi;
+ }
+
+ FileInfo fileInfo = new FileInfo(file_size, 0, 0, ProtoCommon.getIpAddress(buff, 0));
+ fileInfo.setCreateTimestamp(ProtoCommon.buff2int(buff, 4));
+ if ((file_size >> 63) != 0) {
+ file_size &= 0xFFFFFFFFL; //low 32 bits is file size
+ fileInfo.setFileSize(file_size);
+ }
+ fileInfo.setCrc32(ProtoCommon.buff2int(buff, 4 * 4));
+
+ return fileInfo;
+ }
+
+ /**
+ * get file info from storage server
+ *
+ * @param group_name the group name of storage server
+ * @param remote_filename filename on storage server
+ * @return FileInfo object for success, return null for fail
+ */
+ public FileInfo query_file_info(String group_name, String remote_filename) throws IOException, FastdfsException {
+ boolean bNewConnection = this.newUpdatableStorageConnection(group_name, remote_filename);
+ Socket storageSocket = this.storageServer.getSocket();
+
+ try {
+ byte[] header;
+ byte[] groupBytes;
+ byte[] filenameBytes;
+ byte[] bs;
+ int groupLen;
+ ProtoCommon.RecvPackageInfo pkgInfo;
+
+ filenameBytes = remote_filename.getBytes(ClientGlobal.g_charset);
+ groupBytes = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN];
+ bs = group_name.getBytes(ClientGlobal.g_charset);
+
+ Arrays.fill(groupBytes, (byte) 0);
+ if (bs.length <= groupBytes.length) {
+ groupLen = bs.length;
+ } else {
+ groupLen = groupBytes.length;
+ }
+ System.arraycopy(bs, 0, groupBytes, 0, groupLen);
+
+ header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_QUERY_FILE_INFO,
+ +groupBytes.length + filenameBytes.length, (byte) 0);
+ OutputStream out = storageSocket.getOutputStream();
+ byte[] wholePkg = new byte[header.length + groupBytes.length + filenameBytes.length];
+ System.arraycopy(header, 0, wholePkg, 0, header.length);
+ System.arraycopy(groupBytes, 0, wholePkg, header.length, groupBytes.length);
+ System.arraycopy(filenameBytes, 0, wholePkg, header.length + groupBytes.length, filenameBytes.length);
+ out.write(wholePkg);
+
+ pkgInfo = ProtoCommon.recvPackage(storageSocket.getInputStream(),
+ ProtoCommon.STORAGE_PROTO_CMD_RESP,
+ 3 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE +
+ ProtoCommon.FDFS_IPADDR_SIZE);
+
+ this.errno = pkgInfo.errno;
+ if (pkgInfo.errno != 0) {
+ return null;
+ }
+
+ long file_size = ProtoCommon.buff2long(pkgInfo.body, 0);
+ int create_timestamp = (int) ProtoCommon.buff2long(pkgInfo.body, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ int crc32 = (int) ProtoCommon.buff2long(pkgInfo.body, 2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ String source_ip_addr = (new String(pkgInfo.body, 3 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE, ProtoCommon.FDFS_IPADDR_SIZE)).trim();
+ return new FileInfo(file_size, create_timestamp, crc32, source_ip_addr);
+ } catch (IOException ex) {
+ if (!bNewConnection) {
+ try {
+ this.storageServer.close();
+ } catch (IOException ex1) {
+ ex1.printStackTrace();
+ } finally {
+ this.storageServer = null;
+ }
+ }
+
+ throw ex;
+ } finally {
+ if (bNewConnection) {
+ try {
+ this.storageServer.close();
+ } catch (IOException ex1) {
+ ex1.printStackTrace();
+ } finally {
+ this.storageServer = null;
+ }
+ }
+ }
+ }
+
+ /**
+ * check storage socket, if null create a new connection
+ *
+ * @param group_name the group name to upload file to, can be empty
+ * @return true if create a new connection
+ */
+ protected boolean newWritableStorageConnection(String group_name) throws IOException, FastdfsException {
+ if (this.storageServer != null) {
+ return false;
+ } else {
+ TrackerClient tracker = new TrackerClient();
+ this.storageServer = tracker.getStoreStorage(this.trackerServer, group_name);
+ if (this.storageServer == null) {
+ throw new FastdfsException("getStoreStorage fail, errno code: " + tracker.getErrorCode());
+ }
+ return true;
+ }
+ }
+
+ /**
+ * check storage socket, if null create a new connection
+ *
+ * @param group_name the group name of storage server
+ * @param remote_filename filename on storage server
+ * @return true if create a new connection
+ */
+ protected boolean newReadableStorageConnection(String group_name, String remote_filename) throws IOException, FastdfsException {
+ if (this.storageServer != null) {
+ return false;
+ } else {
+ TrackerClient tracker = new TrackerClient();
+ this.storageServer = tracker.getFetchStorage(this.trackerServer, group_name, remote_filename);
+ if (this.storageServer == null) {
+ throw new FastdfsException("getStoreStorage fail, errno code: " + tracker.getErrorCode());
+ }
+ return true;
+ }
+ }
+
+ /**
+ * check storage socket, if null create a new connection
+ *
+ * @param group_name the group name of storage server
+ * @param remote_filename filename on storage server
+ * @return true if create a new connection
+ */
+ protected boolean newUpdatableStorageConnection(String group_name, String remote_filename) throws IOException, FastdfsException {
+ if (this.storageServer != null) {
+ return false;
+ } else {
+ TrackerClient tracker = new TrackerClient();
+ this.storageServer = tracker.getUpdateStorage(this.trackerServer, group_name, remote_filename);
+ if (this.storageServer == null) {
+ throw new FastdfsException("getStoreStorage fail, errno code: " + tracker.getErrorCode());
+ }
+ return true;
+ }
+ }
+
+ /**
+ * send package to storage server
+ *
+ * @param cmd which command to send
+ * @param group_name the group name of storage server
+ * @param remote_filename filename on storage server
+ */
+ protected void send_package(byte cmd, String group_name, String remote_filename) throws IOException {
+ byte[] header;
+ byte[] groupBytes;
+ byte[] filenameBytes;
+ byte[] bs;
+ int groupLen;
+
+ groupBytes = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN];
+ bs = group_name.getBytes(ClientGlobal.g_charset);
+ filenameBytes = remote_filename.getBytes(ClientGlobal.g_charset);
+
+ Arrays.fill(groupBytes, (byte) 0);
+ if (bs.length <= groupBytes.length) {
+ groupLen = bs.length;
+ } else {
+ groupLen = groupBytes.length;
+ }
+ System.arraycopy(bs, 0, groupBytes, 0, groupLen);
+
+ header = ProtoCommon.packHeader(cmd, groupBytes.length + filenameBytes.length, (byte) 0);
+ byte[] wholePkg = new byte[header.length + groupBytes.length + filenameBytes.length];
+ System.arraycopy(header, 0, wholePkg, 0, header.length);
+ System.arraycopy(groupBytes, 0, wholePkg, header.length, groupBytes.length);
+ System.arraycopy(filenameBytes, 0, wholePkg, header.length + groupBytes.length, filenameBytes.length);
+ this.storageServer.getSocket().getOutputStream().write(wholePkg);
+ }
+
+ /**
+ * send package to storage server
+ *
+ * @param group_name the group name of storage server
+ * @param remote_filename filename on storage server
+ * @param file_offset the start offset of the file
+ * @param download_bytes download bytes
+ */
+ protected void send_download_package(String group_name, String remote_filename, long file_offset, long download_bytes) throws IOException {
+ byte[] header;
+ byte[] bsOffset;
+ byte[] bsDownBytes;
+ byte[] groupBytes;
+ byte[] filenameBytes;
+ byte[] bs;
+ int groupLen;
+
+ bsOffset = ProtoCommon.long2buff(file_offset);
+ bsDownBytes = ProtoCommon.long2buff(download_bytes);
+ groupBytes = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN];
+ bs = group_name.getBytes(ClientGlobal.g_charset);
+ filenameBytes = remote_filename.getBytes(ClientGlobal.g_charset);
+
+ Arrays.fill(groupBytes, (byte) 0);
+ if (bs.length <= groupBytes.length) {
+ groupLen = bs.length;
+ } else {
+ groupLen = groupBytes.length;
+ }
+ System.arraycopy(bs, 0, groupBytes, 0, groupLen);
+
+ header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_DOWNLOAD_FILE,
+ bsOffset.length + bsDownBytes.length + groupBytes.length + filenameBytes.length, (byte) 0);
+ byte[] wholePkg = new byte[header.length + bsOffset.length + bsDownBytes.length + groupBytes.length + filenameBytes.length];
+ System.arraycopy(header, 0, wholePkg, 0, header.length);
+ System.arraycopy(bsOffset, 0, wholePkg, header.length, bsOffset.length);
+ System.arraycopy(bsDownBytes, 0, wholePkg, header.length + bsOffset.length, bsDownBytes.length);
+ System.arraycopy(groupBytes, 0, wholePkg, header.length + bsOffset.length + bsDownBytes.length, groupBytes.length);
+ System.arraycopy(filenameBytes, 0, wholePkg, header.length + bsOffset.length + bsDownBytes.length + groupBytes.length, filenameBytes.length);
+ this.storageServer.getSocket().getOutputStream().write(wholePkg);
+ }
+
+ /**
+ * Upload file by file buff
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.12
+ */
+ public static class UploadBuff implements UploadCallback {
+ private byte[] fileBuff;
+ private int offset;
+ private int length;
+
+ /**
+ * constructor
+ *
+ * @param fileBuff the file buff for uploading
+ */
+ public UploadBuff(byte[] fileBuff, int offset, int length) {
+ super();
+ this.fileBuff = fileBuff;
+ this.offset = offset;
+ this.length = length;
+ }
+
+ /**
+ * send file content callback function, be called only once when the file uploaded
+ *
+ * @param out output stream for writing file content
+ * @return 0 success, return none zero(errno) if fail
+ */
+ public int send(OutputStream out) throws IOException {
+ out.write(this.fileBuff, this.offset, this.length);
+
+ return 0;
+ }
+ }
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/StorageClient1.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/StorageClient1.java
new file mode 100644
index 000000000..87de70325
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/StorageClient1.java
@@ -0,0 +1,732 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ *
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package org.csource.fastdfs;
+
+import org.csource.common.FastdfsException;
+import org.csource.common.NameValuePair;
+
+import java.io.IOException;
+
+/**
+ * Storage client for 1 field file id: combined group name and filename
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.21
+ */
+public class StorageClient1 extends StorageClient {
+ public static final String SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR = "/";
+
+ /**
+ * constructor
+ */
+ public StorageClient1() {
+ super();
+ }
+
+ /**
+ * constructor
+ *
+ * @param trackerServer the tracker server, can be null
+ * @param storageServer the storage server, can be null
+ */
+ public StorageClient1(TrackerServer trackerServer, StorageServer storageServer) {
+ super(trackerServer, storageServer);
+ }
+
+ public static byte split_file_id(String file_id, String[] results) {
+ int pos = file_id.indexOf(SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR);
+ if ((pos <= 0) || (pos == file_id.length() - 1)) {
+ return ProtoCommon.ERR_NO_EINVAL;
+ }
+
+ results[0] = file_id.substring(0, pos); //group name
+ results[1] = file_id.substring(pos + 1); //file name
+ return 0;
+ }
+
+ /**
+ * upload file to storage server (by file name)
+ *
+ * @param local_filename local filename to upload
+ * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename
+ * @param meta_list meta info array
+ * @return file id(including group name and filename) if success,
+ * return null if fail
+ */
+ public String upload_file1(String local_filename, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ String parts[] = this.upload_file(local_filename, file_ext_name, meta_list);
+ if (parts != null) {
+ return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * upload file to storage server (by file name)
+ *
+ * @param group_name the group name to upload file to, can be empty
+ * @param local_filename local filename to upload
+ * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename
+ * @param meta_list meta info array
+ * @return file id(including group name and filename) if success,
+ * return null if fail
+ */
+ public String upload_file1(String group_name, String local_filename, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ String parts[] = this.upload_file(group_name, local_filename, file_ext_name, meta_list);
+ if (parts != null) {
+ return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * upload file to storage server (by file buff)
+ *
+ * @param file_buff file content/buff
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return file id(including group name and filename) if success,
+ * return null if fail
+ */
+ public String upload_file1(byte[] file_buff, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ String parts[] = this.upload_file(file_buff, file_ext_name, meta_list);
+ if (parts != null) {
+ return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * upload file to storage server (by file buff)
+ *
+ * @param group_name the group name to upload file to, can be empty
+ * @param file_buff file content/buff
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return file id(including group name and filename) if success,
+ * return null if fail
+ */
+ public String upload_file1(String group_name, byte[] file_buff, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ String parts[] = this.upload_file(group_name, file_buff, file_ext_name, meta_list);
+ if (parts != null) {
+ return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * upload file to storage server (by callback)
+ *
+ * @param group_name the group name to upload file to, can be empty
+ * @param file_size the file size
+ * @param callback the write data callback object
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return file id(including group name and filename) if success,
+ * return null if fail
+ */
+ public String upload_file1(String group_name, long file_size,
+ UploadCallback callback, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ String parts[] = this.upload_file(group_name, file_size, callback, file_ext_name, meta_list);
+ if (parts != null) {
+ return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * upload appender file to storage server (by file name)
+ *
+ * @param local_filename local filename to upload
+ * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename
+ * @param meta_list meta info array
+ * @return file id(including group name and filename) if success,
+ * return null if fail
+ */
+ public String upload_appender_file1(String local_filename, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ String parts[] = this.upload_appender_file(local_filename, file_ext_name, meta_list);
+ if (parts != null) {
+ return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * upload appender file to storage server (by file name)
+ *
+ * @param group_name the group name to upload file to, can be empty
+ * @param local_filename local filename to upload
+ * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename
+ * @param meta_list meta info array
+ * @return file id(including group name and filename) if success,
+ * return null if fail
+ */
+ public String upload_appender_file1(String group_name, String local_filename, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ String parts[] = this.upload_appender_file(group_name, local_filename, file_ext_name, meta_list);
+ if (parts != null) {
+ return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * upload appender file to storage server (by file buff)
+ *
+ * @param file_buff file content/buff
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return file id(including group name and filename) if success,
+ * return null if fail
+ */
+ public String upload_appender_file1(byte[] file_buff, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ String parts[] = this.upload_appender_file(file_buff, file_ext_name, meta_list);
+ if (parts != null) {
+ return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * upload appender file to storage server (by file buff)
+ *
+ * @param group_name the group name to upload file to, can be empty
+ * @param file_buff file content/buff
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return file id(including group name and filename) if success,
+ * return null if fail
+ */
+ public String upload_appender_file1(String group_name, byte[] file_buff, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ String parts[] = this.upload_appender_file(group_name, file_buff, file_ext_name, meta_list);
+ if (parts != null) {
+ return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * upload appender file to storage server (by callback)
+ *
+ * @param group_name the group name to upload file to, can be empty
+ * @param file_size the file size
+ * @param callback the write data callback object
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return file id(including group name and filename) if success,
+ * return null if fail
+ */
+ public String upload_appender_file1(String group_name, long file_size,
+ UploadCallback callback, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ String parts[] = this.upload_appender_file(group_name, file_size, callback, file_ext_name, meta_list);
+ if (parts != null) {
+ return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * upload file to storage server (by file name, slave file mode)
+ *
+ * @param master_file_id the master file id to generate the slave file
+ * @param prefix_name the prefix name to generate the slave file
+ * @param local_filename local filename to upload
+ * @param file_ext_name file ext name, do not include dot(.), null to extract ext name from the local filename
+ * @param meta_list meta info array
+ * @return file id(including group name and filename) if success,
+ * return null if fail
+ */
+ public String upload_file1(String master_file_id, String prefix_name,
+ String local_filename, String file_ext_name, NameValuePair[] meta_list) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(master_file_id, parts);
+ if (this.errno != 0) {
+ return null;
+ }
+
+ parts = this.upload_file(parts[0], parts[1], prefix_name,
+ local_filename, file_ext_name, meta_list);
+ if (parts != null) {
+ return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * upload file to storage server (by file buff, slave file mode)
+ *
+ * @param master_file_id the master file id to generate the slave file
+ * @param prefix_name the prefix name to generate the slave file
+ * @param file_buff file content/buff
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return file id(including group name and filename) if success,
+ * return null if fail
+ */
+ public String upload_file1(String master_file_id, String prefix_name,
+ byte[] file_buff, String file_ext_name, NameValuePair[] meta_list) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(master_file_id, parts);
+ if (this.errno != 0) {
+ return null;
+ }
+
+ parts = this.upload_file(parts[0], parts[1], prefix_name, file_buff, file_ext_name, meta_list);
+ if (parts != null) {
+ return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * upload file to storage server (by file buff, slave file mode)
+ *
+ * @param master_file_id the master file id to generate the slave file
+ * @param prefix_name the prefix name to generate the slave file
+ * @param file_buff file content/buff
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return file id(including group name and filename) if success,
+ * return null if fail
+ */
+ public String upload_file1(String master_file_id, String prefix_name,
+ byte[] file_buff, int offset, int length, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(master_file_id, parts);
+ if (this.errno != 0) {
+ return null;
+ }
+
+ parts = this.upload_file(parts[0], parts[1], prefix_name, file_buff,
+ offset, length, file_ext_name, meta_list);
+ if (parts != null) {
+ return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * upload file to storage server (by callback)
+ *
+ * @param master_file_id the master file id to generate the slave file
+ * @param prefix_name the prefix name to generate the slave file
+ * @param file_size the file size
+ * @param callback the write data callback object
+ * @param file_ext_name file ext name, do not include dot(.)
+ * @param meta_list meta info array
+ * @return file id(including group name and filename) if success,
+ * return null if fail
+ */
+ public String upload_file1(String master_file_id, String prefix_name, long file_size,
+ UploadCallback callback, String file_ext_name,
+ NameValuePair[] meta_list) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(master_file_id, parts);
+ if (this.errno != 0) {
+ return null;
+ }
+
+ parts = this.upload_file(parts[0], parts[1], prefix_name, file_size, callback, file_ext_name, meta_list);
+ if (parts != null) {
+ return parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + parts[1];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * append file to storage server (by file name)
+ *
+ * @param appender_file_id the appender file id
+ * @param local_filename local filename to append
+ * @return 0 for success, != 0 for error (error no)
+ */
+ public int append_file1(String appender_file_id, String local_filename) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(appender_file_id, parts);
+ if (this.errno != 0) {
+ return this.errno;
+ }
+
+ return this.append_file(parts[0], parts[1], local_filename);
+ }
+
+ /**
+ * append file to storage server (by file buff)
+ *
+ * @param appender_file_id the appender file id
+ * @param file_buff file content/buff
+ * @return 0 for success, != 0 for error (error no)
+ */
+ public int append_file1(String appender_file_id, byte[] file_buff) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(appender_file_id, parts);
+ if (this.errno != 0) {
+ return this.errno;
+ }
+
+ return this.append_file(parts[0], parts[1], file_buff);
+ }
+
+ /**
+ * append file to storage server (by file buff)
+ *
+ * @param appender_file_id the appender file id
+ * @param file_buff file content/buffer
+ * @param offset start offset of the buffer
+ * @param length the length of the buffer to append
+ * @return 0 for success, != 0 for error (error no)
+ */
+ public int append_file1(String appender_file_id, byte[] file_buff, int offset, int length) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(appender_file_id, parts);
+ if (this.errno != 0) {
+ return this.errno;
+ }
+
+ return this.append_file(parts[0], parts[1], file_buff, offset, length);
+ }
+
+ /**
+ * append file to storage server (by callback)
+ *
+ * @param appender_file_id the appender file id
+ * @param file_size the file size
+ * @param callback the write data callback object
+ * @return 0 for success, != 0 for error (error no)
+ */
+ public int append_file1(String appender_file_id, long file_size, UploadCallback callback) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(appender_file_id, parts);
+ if (this.errno != 0) {
+ return this.errno;
+ }
+
+ return this.append_file(parts[0], parts[1], file_size, callback);
+ }
+
+ /**
+ * modify appender file to storage server (by file name)
+ *
+ * @param appender_file_id the appender file id
+ * @param file_offset the offset of appender file
+ * @param local_filename local filename to append
+ * @return 0 for success, != 0 for error (error no)
+ */
+ public int modify_file1(String appender_file_id,
+ long file_offset, String local_filename) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(appender_file_id, parts);
+ if (this.errno != 0) {
+ return this.errno;
+ }
+
+ return this.modify_file(parts[0], parts[1], file_offset, local_filename);
+ }
+
+ /**
+ * modify appender file to storage server (by file buff)
+ *
+ * @param appender_file_id the appender file id
+ * @param file_offset the offset of appender file
+ * @param file_buff file content/buff
+ * @return 0 for success, != 0 for error (error no)
+ */
+ public int modify_file1(String appender_file_id,
+ long file_offset, byte[] file_buff) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(appender_file_id, parts);
+ if (this.errno != 0) {
+ return this.errno;
+ }
+
+ return this.modify_file(parts[0], parts[1], file_offset, file_buff);
+ }
+
+ /**
+ * modify appender file to storage server (by file buff)
+ *
+ * @param appender_file_id the appender file id
+ * @param file_offset the offset of appender file
+ * @param file_buff file content/buff
+ * @param buffer_offset start offset of the buff
+ * @param buffer_length the length of buff to modify
+ * @return 0 for success, != 0 for error (error no)
+ */
+ public int modify_file1(String appender_file_id,
+ long file_offset, byte[] file_buff, int buffer_offset, int buffer_length) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(appender_file_id, parts);
+ if (this.errno != 0) {
+ return this.errno;
+ }
+
+ return this.modify_file(parts[0], parts[1], file_offset,
+ file_buff, buffer_offset, buffer_length);
+ }
+
+ /**
+ * modify appender file to storage server (by callback)
+ *
+ * @param appender_file_id the appender file id
+ * @param file_offset the offset of appender file
+ * @param modify_size the modify size
+ * @param callback the write data callback object
+ * @return 0 for success, != 0 for error (error no)
+ */
+ public int modify_file1(String appender_file_id,
+ long file_offset, long modify_size, UploadCallback callback) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(appender_file_id, parts);
+ if (this.errno != 0) {
+ return this.errno;
+ }
+
+ return this.modify_file(parts[0], parts[1], file_offset, modify_size, callback);
+ }
+
+ /**
+ * delete file from storage server
+ *
+ * @param file_id the file id(including group name and filename)
+ * @return 0 for success, none zero for fail (error code)
+ */
+ public int delete_file1(String file_id) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(file_id, parts);
+ if (this.errno != 0) {
+ return this.errno;
+ }
+
+ return this.delete_file(parts[0], parts[1]);
+ }
+
+ /**
+ * truncate appender file to size 0 from storage server
+ *
+ * @param appender_file_id the appender file id
+ * @return 0 for success, none zero for fail (error code)
+ */
+ public int truncate_file1(String appender_file_id) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(appender_file_id, parts);
+ if (this.errno != 0) {
+ return this.errno;
+ }
+
+ return this.truncate_file(parts[0], parts[1]);
+ }
+
+ /**
+ * truncate appender file from storage server
+ *
+ * @param appender_file_id the appender file id
+ * @param truncated_file_size truncated file size
+ * @return 0 for success, none zero for fail (error code)
+ */
+ public int truncate_file1(String appender_file_id, long truncated_file_size) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(appender_file_id, parts);
+ if (this.errno != 0) {
+ return this.errno;
+ }
+
+ return this.truncate_file(parts[0], parts[1], truncated_file_size);
+ }
+
+ /**
+ * download file from storage server
+ *
+ * @param file_id the file id(including group name and filename)
+ * @return file content/buffer, return null if fail
+ */
+ public byte[] download_file1(String file_id) throws IOException, FastdfsException {
+ final long file_offset = 0;
+ final long download_bytes = 0;
+
+ return this.download_file1(file_id, file_offset, download_bytes);
+ }
+
+ /**
+ * download file from storage server
+ *
+ * @param file_id the file id(including group name and filename)
+ * @param file_offset the start offset of the file
+ * @param download_bytes download bytes, 0 for remain bytes from offset
+ * @return file content/buff, return null if fail
+ */
+ public byte[] download_file1(String file_id, long file_offset, long download_bytes) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(file_id, parts);
+ if (this.errno != 0) {
+ return null;
+ }
+
+ return this.download_file(parts[0], parts[1], file_offset, download_bytes);
+ }
+
+ /**
+ * download file from storage server
+ *
+ * @param file_id the file id(including group name and filename)
+ * @param local_filename the filename on local
+ * @return 0 success, return none zero errno if fail
+ */
+ public int download_file1(String file_id, String local_filename) throws IOException, FastdfsException {
+ final long file_offset = 0;
+ final long download_bytes = 0;
+
+ return this.download_file1(file_id, file_offset, download_bytes, local_filename);
+ }
+
+ /**
+ * download file from storage server
+ *
+ * @param file_id the file id(including group name and filename)
+ * @param file_offset the start offset of the file
+ * @param download_bytes download bytes, 0 for remain bytes from offset
+ * @param local_filename the filename on local
+ * @return 0 success, return none zero errno if fail
+ */
+ public int download_file1(String file_id, long file_offset, long download_bytes, String local_filename) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(file_id, parts);
+ if (this.errno != 0) {
+ return this.errno;
+ }
+
+ return this.download_file(parts[0], parts[1], file_offset, download_bytes, local_filename);
+ }
+
+ /**
+ * download file from storage server
+ *
+ * @param file_id the file id(including group name and filename)
+ * @param callback the callback object, will call callback.recv() when data arrive
+ * @return 0 success, return none zero errno if fail
+ */
+ public int download_file1(String file_id, DownloadCallback callback) throws IOException, FastdfsException {
+ final long file_offset = 0;
+ final long download_bytes = 0;
+
+ return this.download_file1(file_id, file_offset, download_bytes, callback);
+ }
+
+ /**
+ * download file from storage server
+ *
+ * @param file_id the file id(including group name and filename)
+ * @param file_offset the start offset of the file
+ * @param download_bytes download bytes, 0 for remain bytes from offset
+ * @param callback the callback object, will call callback.recv() when data arrive
+ * @return 0 success, return none zero errno if fail
+ */
+ public int download_file1(String file_id, long file_offset, long download_bytes, DownloadCallback callback) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(file_id, parts);
+ if (this.errno != 0) {
+ return this.errno;
+ }
+
+ return this.download_file(parts[0], parts[1], file_offset, download_bytes, callback);
+ }
+
+ /**
+ * get all metadata items from storage server
+ *
+ * @param file_id the file id(including group name and filename)
+ * @return meta info array, return null if fail
+ */
+ public NameValuePair[] get_metadata1(String file_id) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(file_id, parts);
+ if (this.errno != 0) {
+ return null;
+ }
+
+ return this.get_metadata(parts[0], parts[1]);
+ }
+
+ /**
+ * set metadata items to storage server
+ *
+ * @param file_id the file id(including group name and filename)
+ * @param meta_list meta item array
+ * @param op_flag flag, can be one of following values:
+ *
ProtoCommon.STORAGE_SET_METADATA_FLAG_OVERWRITE: overwrite all old
+ * metadata items
+ *
ProtoCommon.STORAGE_SET_METADATA_FLAG_MERGE: merge, insert when
+ * the metadata item not exist, otherwise update it
+ * @return 0 for success, !=0 fail (error code)
+ */
+ public int set_metadata1(String file_id, NameValuePair[] meta_list, byte op_flag) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(file_id, parts);
+ if (this.errno != 0) {
+ return this.errno;
+ }
+
+ return this.set_metadata(parts[0], parts[1], meta_list, op_flag);
+ }
+
+ /**
+ * get file info from storage server
+ *
+ * @param file_id the file id(including group name and filename)
+ * @return FileInfo object for success, return null for fail
+ */
+ public FileInfo query_file_info1(String file_id) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(file_id, parts);
+ if (this.errno != 0) {
+ return null;
+ }
+
+ return this.query_file_info(parts[0], parts[1]);
+ }
+
+ /**
+ * get file info decoded from filename
+ *
+ * @param file_id the file id(including group name and filename)
+ * @return FileInfo object for success, return null for fail
+ */
+ public FileInfo get_file_info1(String file_id) throws IOException, FastdfsException {
+ String[] parts = new String[2];
+ this.errno = this.split_file_id(file_id, parts);
+ if (this.errno != 0) {
+ return null;
+ }
+
+ return this.get_file_info(parts[0], parts[1]);
+ }
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/StorageServer.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/StorageServer.java
new file mode 100644
index 000000000..029c6a354
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/StorageServer.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ *
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package org.csource.fastdfs;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+/**
+ * Storage Server Info
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.11
+ */
+public class StorageServer extends TrackerServer {
+ protected int store_path_index = 0;
+
+ /**
+ * Constructor
+ *
+ * @param ip_addr the ip address of storage server
+ * @param port the port of storage server
+ * @param store_path the store path index on the storage server
+ */
+ public StorageServer(String ip_addr, int port, int store_path) throws IOException {
+ super(ClientGlobal.getSocket(ip_addr, port), new InetSocketAddress(ip_addr, port));
+ this.store_path_index = store_path;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param ip_addr the ip address of storage server
+ * @param port the port of storage server
+ * @param store_path the store path index on the storage server
+ */
+ public StorageServer(String ip_addr, int port, byte store_path) throws IOException {
+ super(ClientGlobal.getSocket(ip_addr, port), new InetSocketAddress(ip_addr, port));
+ if (store_path < 0) {
+ this.store_path_index = 256 + store_path;
+ } else {
+ this.store_path_index = store_path;
+ }
+ }
+
+ /**
+ * @return the store path index on the storage server
+ */
+ public int getStorePathIndex() {
+ return this.store_path_index;
+ }
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/StructBase.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/StructBase.java
new file mode 100644
index 000000000..38d847ffa
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/StructBase.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ *
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package org.csource.fastdfs;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Date;
+
+/**
+ * C struct body decoder
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.17
+ */
+public abstract class StructBase {
+ /**
+ * set fields
+ *
+ * @param bs byte array
+ * @param offset start offset
+ */
+ public abstract void setFields(byte[] bs, int offset);
+
+ protected String stringValue(byte[] bs, int offset, FieldInfo filedInfo) {
+ try {
+ return (new String(bs, offset + filedInfo.offset, filedInfo.size, ClientGlobal.g_charset)).trim();
+ } catch (UnsupportedEncodingException ex) {
+ ex.printStackTrace();
+ return null;
+ }
+ }
+
+ protected long longValue(byte[] bs, int offset, FieldInfo filedInfo) {
+ return ProtoCommon.buff2long(bs, offset + filedInfo.offset);
+ }
+
+ protected int intValue(byte[] bs, int offset, FieldInfo filedInfo) {
+ return (int) ProtoCommon.buff2long(bs, offset + filedInfo.offset);
+ }
+
+ protected int int32Value(byte[] bs, int offset, FieldInfo filedInfo) {
+ return ProtoCommon.buff2int(bs, offset + filedInfo.offset);
+ }
+
+ protected byte byteValue(byte[] bs, int offset, FieldInfo filedInfo) {
+ return bs[offset + filedInfo.offset];
+ }
+
+ protected boolean booleanValue(byte[] bs, int offset, FieldInfo filedInfo) {
+ return bs[offset + filedInfo.offset] != 0;
+ }
+
+ protected Date dateValue(byte[] bs, int offset, FieldInfo filedInfo) {
+ return new Date(ProtoCommon.buff2long(bs, offset + filedInfo.offset) * 1000);
+ }
+
+ protected static class FieldInfo {
+ protected String name;
+ protected int offset;
+ protected int size;
+
+ public FieldInfo(String name, int offset, int size) {
+ this.name = name;
+ this.offset = offset;
+ this.size = size;
+ }
+ }
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/StructGroupStat.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/StructGroupStat.java
new file mode 100644
index 000000000..30cfd1c14
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/StructGroupStat.java
@@ -0,0 +1,225 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ *
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package org.csource.fastdfs;
+
+/**
+ * C struct body decoder
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.18
+ */
+public class StructGroupStat extends StructBase {
+ protected static final int FIELD_INDEX_GROUP_NAME = 0;
+ protected static final int FIELD_INDEX_TOTAL_MB = 1;
+ protected static final int FIELD_INDEX_FREE_MB = 2;
+ protected static final int FIELD_INDEX_TRUNK_FREE_MB = 3;
+ protected static final int FIELD_INDEX_STORAGE_COUNT = 4;
+ protected static final int FIELD_INDEX_STORAGE_PORT = 5;
+ protected static final int FIELD_INDEX_STORAGE_HTTP_PORT = 6;
+ protected static final int FIELD_INDEX_ACTIVE_COUNT = 7;
+ protected static final int FIELD_INDEX_CURRENT_WRITE_SERVER = 8;
+ protected static final int FIELD_INDEX_STORE_PATH_COUNT = 9;
+ protected static final int FIELD_INDEX_SUBDIR_COUNT_PER_PATH = 10;
+ protected static final int FIELD_INDEX_CURRENT_TRUNK_FILE_ID = 11;
+
+ protected static int fieldsTotalSize;
+ protected static FieldInfo[] fieldsArray = new FieldInfo[12];
+
+ static {
+ int offset = 0;
+ fieldsArray[FIELD_INDEX_GROUP_NAME] = new FieldInfo("groupName", offset, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + 1);
+ offset += ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + 1;
+
+ fieldsArray[FIELD_INDEX_TOTAL_MB] = new FieldInfo("totalMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_FREE_MB] = new FieldInfo("freeMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_TRUNK_FREE_MB] = new FieldInfo("trunkFreeMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_STORAGE_COUNT] = new FieldInfo("storageCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_STORAGE_PORT] = new FieldInfo("storagePort", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_STORAGE_HTTP_PORT] = new FieldInfo("storageHttpPort", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_ACTIVE_COUNT] = new FieldInfo("activeCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_CURRENT_WRITE_SERVER] = new FieldInfo("currentWriteServer", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_STORE_PATH_COUNT] = new FieldInfo("storePathCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUBDIR_COUNT_PER_PATH] = new FieldInfo("subdirCountPerPath", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_CURRENT_TRUNK_FILE_ID] = new FieldInfo("currentTrunkFileId", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsTotalSize = offset;
+ }
+
+ protected String groupName; //name of this group
+ protected long totalMB; //total disk storage in MB
+ protected long freeMB; //free disk space in MB
+ protected long trunkFreeMB; //trunk free space in MB
+ protected int storageCount; //storage server count
+ protected int storagePort; //storage server port
+ protected int storageHttpPort; //storage server HTTP port
+ protected int activeCount; //active storage server count
+ protected int currentWriteServer; //current storage server index to upload file
+ protected int storePathCount; //store base path count of each storage server
+ protected int subdirCountPerPath; //sub dir count per store path
+ protected int currentTrunkFileId; //current trunk file id
+
+ /**
+ * get fields total size
+ *
+ * @return fields total size
+ */
+ public static int getFieldsTotalSize() {
+ return fieldsTotalSize;
+ }
+
+ /**
+ * get group name
+ *
+ * @return group name
+ */
+ public String getGroupName() {
+ return this.groupName;
+ }
+
+ /**
+ * get total disk space in MB
+ *
+ * @return total disk space in MB
+ */
+ public long getTotalMB() {
+ return this.totalMB;
+ }
+
+ /**
+ * get free disk space in MB
+ *
+ * @return free disk space in MB
+ */
+ public long getFreeMB() {
+ return this.freeMB;
+ }
+
+ /**
+ * get trunk free space in MB
+ *
+ * @return trunk free space in MB
+ */
+ public long getTrunkFreeMB() {
+ return this.trunkFreeMB;
+ }
+
+ /**
+ * get storage server count in this group
+ *
+ * @return storage server count in this group
+ */
+ public int getStorageCount() {
+ return this.storageCount;
+ }
+
+ /**
+ * get active storage server count in this group
+ *
+ * @return active storage server count in this group
+ */
+ public int getActiveCount() {
+ return this.activeCount;
+ }
+
+ /**
+ * get storage server port
+ *
+ * @return storage server port
+ */
+ public int getStoragePort() {
+ return this.storagePort;
+ }
+
+ /**
+ * get storage server HTTP port
+ *
+ * @return storage server HTTP port
+ */
+ public int getStorageHttpPort() {
+ return this.storageHttpPort;
+ }
+
+ /**
+ * get current storage server index to upload file
+ *
+ * @return current storage server index to upload file
+ */
+ public int getCurrentWriteServer() {
+ return this.currentWriteServer;
+ }
+
+ /**
+ * get store base path count of each storage server
+ *
+ * @return store base path count of each storage server
+ */
+ public int getStorePathCount() {
+ return this.storePathCount;
+ }
+
+ /**
+ * get sub dir count per store path
+ *
+ * @return sub dir count per store path
+ */
+ public int getSubdirCountPerPath() {
+ return this.subdirCountPerPath;
+ }
+
+ /**
+ * get current trunk file id
+ *
+ * @return current trunk file id
+ */
+ public int getCurrentTrunkFileId() {
+ return this.currentTrunkFileId;
+ }
+
+ /**
+ * set fields
+ *
+ * @param bs byte array
+ * @param offset start offset
+ */
+ public void setFields(byte[] bs, int offset) {
+ this.groupName = stringValue(bs, offset, fieldsArray[FIELD_INDEX_GROUP_NAME]);
+ this.totalMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_MB]);
+ this.freeMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_FREE_MB]);
+ this.trunkFreeMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_TRUNK_FREE_MB]);
+ this.storageCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_COUNT]);
+ this.storagePort = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_PORT]);
+ this.storageHttpPort = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_HTTP_PORT]);
+ this.activeCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_ACTIVE_COUNT]);
+ this.currentWriteServer = intValue(bs, offset, fieldsArray[FIELD_INDEX_CURRENT_WRITE_SERVER]);
+ this.storePathCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORE_PATH_COUNT]);
+ this.subdirCountPerPath = intValue(bs, offset, fieldsArray[FIELD_INDEX_SUBDIR_COUNT_PER_PATH]);
+ this.currentTrunkFileId = intValue(bs, offset, fieldsArray[FIELD_INDEX_CURRENT_TRUNK_FILE_ID]);
+ }
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/StructStorageStat.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/StructStorageStat.java
new file mode 100644
index 000000000..e503da18a
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/StructStorageStat.java
@@ -0,0 +1,982 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ *
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package org.csource.fastdfs;
+
+import java.util.Date;
+
+/**
+ * C struct body decoder
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.25
+ */
+public class StructStorageStat extends StructBase {
+ protected static final int FIELD_INDEX_STATUS = 0;
+ protected static final int FIELD_INDEX_ID = 1;
+ protected static final int FIELD_INDEX_IP_ADDR = 2;
+ protected static final int FIELD_INDEX_DOMAIN_NAME = 3;
+ protected static final int FIELD_INDEX_SRC_IP_ADDR = 4;
+ protected static final int FIELD_INDEX_VERSION = 5;
+ protected static final int FIELD_INDEX_JOIN_TIME = 6;
+ protected static final int FIELD_INDEX_UP_TIME = 7;
+ protected static final int FIELD_INDEX_TOTAL_MB = 8;
+ protected static final int FIELD_INDEX_FREE_MB = 9;
+ protected static final int FIELD_INDEX_UPLOAD_PRIORITY = 10;
+ protected static final int FIELD_INDEX_STORE_PATH_COUNT = 11;
+ protected static final int FIELD_INDEX_SUBDIR_COUNT_PER_PATH = 12;
+ protected static final int FIELD_INDEX_CURRENT_WRITE_PATH = 13;
+ protected static final int FIELD_INDEX_STORAGE_PORT = 14;
+ protected static final int FIELD_INDEX_STORAGE_HTTP_PORT = 15;
+
+ protected static final int FIELD_INDEX_CONNECTION_ALLOC_COUNT = 16;
+ protected static final int FIELD_INDEX_CONNECTION_CURRENT_COUNT = 17;
+ protected static final int FIELD_INDEX_CONNECTION_MAX_COUNT = 18;
+
+ protected static final int FIELD_INDEX_TOTAL_UPLOAD_COUNT = 19;
+ protected static final int FIELD_INDEX_SUCCESS_UPLOAD_COUNT = 20;
+ protected static final int FIELD_INDEX_TOTAL_APPEND_COUNT = 21;
+ protected static final int FIELD_INDEX_SUCCESS_APPEND_COUNT = 22;
+ protected static final int FIELD_INDEX_TOTAL_MODIFY_COUNT = 23;
+ protected static final int FIELD_INDEX_SUCCESS_MODIFY_COUNT = 24;
+ protected static final int FIELD_INDEX_TOTAL_TRUNCATE_COUNT = 25;
+ protected static final int FIELD_INDEX_SUCCESS_TRUNCATE_COUNT = 26;
+ protected static final int FIELD_INDEX_TOTAL_SET_META_COUNT = 27;
+ protected static final int FIELD_INDEX_SUCCESS_SET_META_COUNT = 28;
+ protected static final int FIELD_INDEX_TOTAL_DELETE_COUNT = 29;
+ protected static final int FIELD_INDEX_SUCCESS_DELETE_COUNT = 30;
+ protected static final int FIELD_INDEX_TOTAL_DOWNLOAD_COUNT = 31;
+ protected static final int FIELD_INDEX_SUCCESS_DOWNLOAD_COUNT = 32;
+ protected static final int FIELD_INDEX_TOTAL_GET_META_COUNT = 33;
+ protected static final int FIELD_INDEX_SUCCESS_GET_META_COUNT = 34;
+ protected static final int FIELD_INDEX_TOTAL_CREATE_LINK_COUNT = 35;
+ protected static final int FIELD_INDEX_SUCCESS_CREATE_LINK_COUNT = 36;
+ protected static final int FIELD_INDEX_TOTAL_DELETE_LINK_COUNT = 37;
+ protected static final int FIELD_INDEX_SUCCESS_DELETE_LINK_COUNT = 38;
+ protected static final int FIELD_INDEX_TOTAL_UPLOAD_BYTES = 39;
+ protected static final int FIELD_INDEX_SUCCESS_UPLOAD_BYTES = 40;
+ protected static final int FIELD_INDEX_TOTAL_APPEND_BYTES = 41;
+ protected static final int FIELD_INDEX_SUCCESS_APPEND_BYTES = 42;
+ protected static final int FIELD_INDEX_TOTAL_MODIFY_BYTES = 43;
+ protected static final int FIELD_INDEX_SUCCESS_MODIFY_BYTES = 44;
+ protected static final int FIELD_INDEX_TOTAL_DOWNLOAD_BYTES = 45;
+ protected static final int FIELD_INDEX_SUCCESS_DOWNLOAD_BYTES = 46;
+ protected static final int FIELD_INDEX_TOTAL_SYNC_IN_BYTES = 47;
+ protected static final int FIELD_INDEX_SUCCESS_SYNC_IN_BYTES = 48;
+ protected static final int FIELD_INDEX_TOTAL_SYNC_OUT_BYTES = 49;
+ protected static final int FIELD_INDEX_SUCCESS_SYNC_OUT_BYTES = 50;
+ protected static final int FIELD_INDEX_TOTAL_FILE_OPEN_COUNT = 51;
+ protected static final int FIELD_INDEX_SUCCESS_FILE_OPEN_COUNT = 52;
+ protected static final int FIELD_INDEX_TOTAL_FILE_READ_COUNT = 53;
+ protected static final int FIELD_INDEX_SUCCESS_FILE_READ_COUNT = 54;
+ protected static final int FIELD_INDEX_TOTAL_FILE_WRITE_COUNT = 55;
+ protected static final int FIELD_INDEX_SUCCESS_FILE_WRITE_COUNT = 56;
+ protected static final int FIELD_INDEX_LAST_SOURCE_UPDATE = 57;
+ protected static final int FIELD_INDEX_LAST_SYNC_UPDATE = 58;
+ protected static final int FIELD_INDEX_LAST_SYNCED_TIMESTAMP = 59;
+ protected static final int FIELD_INDEX_LAST_HEART_BEAT_TIME = 60;
+ protected static final int FIELD_INDEX_IF_TRUNK_FILE = 61;
+
+ protected static int fieldsTotalSize;
+ protected static FieldInfo[] fieldsArray = new FieldInfo[62];
+
+ static {
+ int offset = 0;
+
+ fieldsArray[FIELD_INDEX_STATUS] = new FieldInfo("status", offset, 1);
+ offset += 1;
+
+ fieldsArray[FIELD_INDEX_ID] = new FieldInfo("id", offset, ProtoCommon.FDFS_STORAGE_ID_MAX_SIZE);
+ offset += ProtoCommon.FDFS_STORAGE_ID_MAX_SIZE;
+
+ fieldsArray[FIELD_INDEX_IP_ADDR] = new FieldInfo("ipAddr", offset, ProtoCommon.FDFS_IPADDR_SIZE);
+ offset += ProtoCommon.FDFS_IPADDR_SIZE;
+
+ fieldsArray[FIELD_INDEX_DOMAIN_NAME] = new FieldInfo("domainName", offset, ProtoCommon.FDFS_DOMAIN_NAME_MAX_SIZE);
+ offset += ProtoCommon.FDFS_DOMAIN_NAME_MAX_SIZE;
+
+ fieldsArray[FIELD_INDEX_SRC_IP_ADDR] = new FieldInfo("srcIpAddr", offset, ProtoCommon.FDFS_IPADDR_SIZE);
+ offset += ProtoCommon.FDFS_IPADDR_SIZE;
+
+ fieldsArray[FIELD_INDEX_VERSION] = new FieldInfo("version", offset, ProtoCommon.FDFS_VERSION_SIZE);
+ offset += ProtoCommon.FDFS_VERSION_SIZE;
+
+ fieldsArray[FIELD_INDEX_JOIN_TIME] = new FieldInfo("joinTime", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_UP_TIME] = new FieldInfo("upTime", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_TOTAL_MB] = new FieldInfo("totalMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_FREE_MB] = new FieldInfo("freeMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_UPLOAD_PRIORITY] = new FieldInfo("uploadPriority", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_STORE_PATH_COUNT] = new FieldInfo("storePathCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUBDIR_COUNT_PER_PATH] = new FieldInfo("subdirCountPerPath", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_CURRENT_WRITE_PATH] = new FieldInfo("currentWritePath", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_STORAGE_PORT] = new FieldInfo("storagePort", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_STORAGE_HTTP_PORT] = new FieldInfo("storageHttpPort", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_CONNECTION_ALLOC_COUNT] = new FieldInfo("connectionAllocCount", offset, 4);
+ offset += 4;
+
+ fieldsArray[FIELD_INDEX_CONNECTION_CURRENT_COUNT] = new FieldInfo("connectionCurrentCount", offset, 4);
+ offset += 4;
+
+ fieldsArray[FIELD_INDEX_CONNECTION_MAX_COUNT] = new FieldInfo("connectionMaxCount", offset, 4);
+ offset += 4;
+
+ fieldsArray[FIELD_INDEX_TOTAL_UPLOAD_COUNT] = new FieldInfo("totalUploadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUCCESS_UPLOAD_COUNT] = new FieldInfo("successUploadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_TOTAL_APPEND_COUNT] = new FieldInfo("totalAppendCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUCCESS_APPEND_COUNT] = new FieldInfo("successAppendCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_TOTAL_MODIFY_COUNT] = new FieldInfo("totalModifyCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUCCESS_MODIFY_COUNT] = new FieldInfo("successModifyCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_TOTAL_TRUNCATE_COUNT] = new FieldInfo("totalTruncateCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUCCESS_TRUNCATE_COUNT] = new FieldInfo("successTruncateCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_TOTAL_SET_META_COUNT] = new FieldInfo("totalSetMetaCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUCCESS_SET_META_COUNT] = new FieldInfo("successSetMetaCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_TOTAL_DELETE_COUNT] = new FieldInfo("totalDeleteCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUCCESS_DELETE_COUNT] = new FieldInfo("successDeleteCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_TOTAL_DOWNLOAD_COUNT] = new FieldInfo("totalDownloadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUCCESS_DOWNLOAD_COUNT] = new FieldInfo("successDownloadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_TOTAL_GET_META_COUNT] = new FieldInfo("totalGetMetaCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUCCESS_GET_META_COUNT] = new FieldInfo("successGetMetaCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_TOTAL_CREATE_LINK_COUNT] = new FieldInfo("totalCreateLinkCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUCCESS_CREATE_LINK_COUNT] = new FieldInfo("successCreateLinkCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_TOTAL_DELETE_LINK_COUNT] = new FieldInfo("totalDeleteLinkCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUCCESS_DELETE_LINK_COUNT] = new FieldInfo("successDeleteLinkCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_TOTAL_UPLOAD_BYTES] = new FieldInfo("totalUploadBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUCCESS_UPLOAD_BYTES] = new FieldInfo("successUploadBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_TOTAL_APPEND_BYTES] = new FieldInfo("totalAppendBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUCCESS_APPEND_BYTES] = new FieldInfo("successAppendBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_TOTAL_MODIFY_BYTES] = new FieldInfo("totalModifyBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUCCESS_MODIFY_BYTES] = new FieldInfo("successModifyBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_TOTAL_DOWNLOAD_BYTES] = new FieldInfo("totalDownloadloadBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUCCESS_DOWNLOAD_BYTES] = new FieldInfo("successDownloadloadBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_TOTAL_SYNC_IN_BYTES] = new FieldInfo("totalSyncInBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUCCESS_SYNC_IN_BYTES] = new FieldInfo("successSyncInBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_TOTAL_SYNC_OUT_BYTES] = new FieldInfo("totalSyncOutBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUCCESS_SYNC_OUT_BYTES] = new FieldInfo("successSyncOutBytes", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_TOTAL_FILE_OPEN_COUNT] = new FieldInfo("totalFileOpenCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUCCESS_FILE_OPEN_COUNT] = new FieldInfo("successFileOpenCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_TOTAL_FILE_READ_COUNT] = new FieldInfo("totalFileReadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUCCESS_FILE_READ_COUNT] = new FieldInfo("successFileReadCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_TOTAL_FILE_WRITE_COUNT] = new FieldInfo("totalFileWriteCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_SUCCESS_FILE_WRITE_COUNT] = new FieldInfo("successFileWriteCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_LAST_SOURCE_UPDATE] = new FieldInfo("lastSourceUpdate", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_LAST_SYNC_UPDATE] = new FieldInfo("lastSyncUpdate", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_LAST_SYNCED_TIMESTAMP] = new FieldInfo("lastSyncedTimestamp", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_LAST_HEART_BEAT_TIME] = new FieldInfo("lastHeartBeatTime", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ fieldsArray[FIELD_INDEX_IF_TRUNK_FILE] = new FieldInfo("ifTrunkServer", offset, 1);
+ offset += 1;
+
+ fieldsTotalSize = offset;
+ }
+
+ protected byte status;
+ protected String id;
+ protected String ipAddr;
+ protected String srcIpAddr;
+ protected String domainName; //http domain name
+ protected String version;
+ protected long totalMB; //total disk storage in MB
+ protected long freeMB; //free disk storage in MB
+ protected int uploadPriority; //upload priority
+ protected Date joinTime; //storage join timestamp (create timestamp)
+ protected Date upTime; //storage service started timestamp
+ protected int storePathCount; //store base path count of each storage server
+ protected int subdirCountPerPath;
+ protected int storagePort;
+ protected int storageHttpPort; //storage http server port
+ protected int currentWritePath; //current write path index
+ protected int connectionAllocCount;
+ protected int connectionCurrentCount;
+ protected int connectionMaxCount;
+ protected long totalUploadCount;
+ protected long successUploadCount;
+ protected long totalAppendCount;
+ protected long successAppendCount;
+ protected long totalModifyCount;
+ protected long successModifyCount;
+ protected long totalTruncateCount;
+ protected long successTruncateCount;
+ protected long totalSetMetaCount;
+ protected long successSetMetaCount;
+ protected long totalDeleteCount;
+ protected long successDeleteCount;
+ protected long totalDownloadCount;
+ protected long successDownloadCount;
+ protected long totalGetMetaCount;
+ protected long successGetMetaCount;
+ protected long totalCreateLinkCount;
+ protected long successCreateLinkCount;
+ protected long totalDeleteLinkCount;
+ protected long successDeleteLinkCount;
+ protected long totalUploadBytes;
+ protected long successUploadBytes;
+ protected long totalAppendBytes;
+ protected long successAppendBytes;
+ protected long totalModifyBytes;
+ protected long successModifyBytes;
+ protected long totalDownloadloadBytes;
+ protected long successDownloadloadBytes;
+ protected long totalSyncInBytes;
+ protected long successSyncInBytes;
+ protected long totalSyncOutBytes;
+ protected long successSyncOutBytes;
+ protected long totalFileOpenCount;
+ protected long successFileOpenCount;
+ protected long totalFileReadCount;
+ protected long successFileReadCount;
+ protected long totalFileWriteCount;
+ protected long successFileWriteCount;
+ protected Date lastSourceUpdate;
+ protected Date lastSyncUpdate;
+ protected Date lastSyncedTimestamp;
+ protected Date lastHeartBeatTime;
+ protected boolean ifTrunkServer;
+
+ /**
+ * get fields total size
+ *
+ * @return fields total size
+ */
+ public static int getFieldsTotalSize() {
+ return fieldsTotalSize;
+ }
+
+ /**
+ * get storage status
+ *
+ * @return storage status
+ */
+ public byte getStatus() {
+ return this.status;
+ }
+
+ /**
+ * get storage server id
+ *
+ * @return storage server id
+ */
+ public String getId() {
+ return this.id;
+ }
+
+ /**
+ * get storage server ip address
+ *
+ * @return storage server ip address
+ */
+ public String getIpAddr() {
+ return this.ipAddr;
+ }
+
+ /**
+ * get source storage ip address
+ *
+ * @return source storage ip address
+ */
+ public String getSrcIpAddr() {
+ return this.srcIpAddr;
+ }
+
+ /**
+ * get the domain name of the storage server
+ *
+ * @return the domain name of the storage server
+ */
+ public String getDomainName() {
+ return this.domainName;
+ }
+
+ /**
+ * get storage version
+ *
+ * @return storage version
+ */
+ public String getVersion() {
+ return this.version;
+ }
+
+ /**
+ * get total disk space in MB
+ *
+ * @return total disk space in MB
+ */
+ public long getTotalMB() {
+ return this.totalMB;
+ }
+
+ /**
+ * get free disk space in MB
+ *
+ * @return free disk space in MB
+ */
+ public long getFreeMB() {
+ return this.freeMB;
+ }
+
+ /**
+ * get storage server upload priority
+ *
+ * @return storage server upload priority
+ */
+ public int getUploadPriority() {
+ return this.uploadPriority;
+ }
+
+ /**
+ * get storage server join time
+ *
+ * @return storage server join time
+ */
+ public Date getJoinTime() {
+ return this.joinTime;
+ }
+
+ /**
+ * get storage server up time
+ *
+ * @return storage server up time
+ */
+ public Date getUpTime() {
+ return this.upTime;
+ }
+
+ /**
+ * get store base path count of each storage server
+ *
+ * @return store base path count of each storage server
+ */
+ public int getStorePathCount() {
+ return this.storePathCount;
+ }
+
+ /**
+ * get sub dir count per store path
+ *
+ * @return sub dir count per store path
+ */
+ public int getSubdirCountPerPath() {
+ return this.subdirCountPerPath;
+ }
+
+ /**
+ * get storage server port
+ *
+ * @return storage server port
+ */
+ public int getStoragePort() {
+ return this.storagePort;
+ }
+
+ /**
+ * get storage server HTTP port
+ *
+ * @return storage server HTTP port
+ */
+ public int getStorageHttpPort() {
+ return this.storageHttpPort;
+ }
+
+ /**
+ * get current write path index
+ *
+ * @return current write path index
+ */
+ public int getCurrentWritePath() {
+ return this.currentWritePath;
+ }
+
+ /**
+ * get total upload file count
+ *
+ * @return total upload file count
+ */
+ public long getTotalUploadCount() {
+ return this.totalUploadCount;
+ }
+
+ /**
+ * get success upload file count
+ *
+ * @return success upload file count
+ */
+ public long getSuccessUploadCount() {
+ return this.successUploadCount;
+ }
+
+ /**
+ * get total append count
+ *
+ * @return total append count
+ */
+ public long getTotalAppendCount() {
+ return this.totalAppendCount;
+ }
+
+ /**
+ * get success append count
+ *
+ * @return success append count
+ */
+ public long getSuccessAppendCount() {
+ return this.successAppendCount;
+ }
+
+ /**
+ * get total modify count
+ *
+ * @return total modify count
+ */
+ public long getTotalModifyCount() {
+ return this.totalModifyCount;
+ }
+
+ /**
+ * get success modify count
+ *
+ * @return success modify count
+ */
+ public long getSuccessModifyCount() {
+ return this.successModifyCount;
+ }
+
+ /**
+ * get total truncate count
+ *
+ * @return total truncate count
+ */
+ public long getTotalTruncateCount() {
+ return this.totalTruncateCount;
+ }
+
+ /**
+ * get success truncate count
+ *
+ * @return success truncate count
+ */
+ public long getSuccessTruncateCount() {
+ return this.successTruncateCount;
+ }
+
+ /**
+ * get total set meta data count
+ *
+ * @return total set meta data count
+ */
+ public long getTotalSetMetaCount() {
+ return this.totalSetMetaCount;
+ }
+
+ /**
+ * get success set meta data count
+ *
+ * @return success set meta data count
+ */
+ public long getSuccessSetMetaCount() {
+ return this.successSetMetaCount;
+ }
+
+ /**
+ * get total delete file count
+ *
+ * @return total delete file count
+ */
+ public long getTotalDeleteCount() {
+ return this.totalDeleteCount;
+ }
+
+ /**
+ * get success delete file count
+ *
+ * @return success delete file count
+ */
+ public long getSuccessDeleteCount() {
+ return this.successDeleteCount;
+ }
+
+ /**
+ * get total download file count
+ *
+ * @return total download file count
+ */
+ public long getTotalDownloadCount() {
+ return this.totalDownloadCount;
+ }
+
+ /**
+ * get success download file count
+ *
+ * @return success download file count
+ */
+ public long getSuccessDownloadCount() {
+ return this.successDownloadCount;
+ }
+
+ /**
+ * get total get metadata count
+ *
+ * @return total get metadata count
+ */
+ public long getTotalGetMetaCount() {
+ return this.totalGetMetaCount;
+ }
+
+ /**
+ * get success get metadata count
+ *
+ * @return success get metadata count
+ */
+ public long getSuccessGetMetaCount() {
+ return this.successGetMetaCount;
+ }
+
+ /**
+ * get total create linke count
+ *
+ * @return total create linke count
+ */
+ public long getTotalCreateLinkCount() {
+ return this.totalCreateLinkCount;
+ }
+
+ /**
+ * get success create linke count
+ *
+ * @return success create linke count
+ */
+ public long getSuccessCreateLinkCount() {
+ return this.successCreateLinkCount;
+ }
+
+ /**
+ * get total delete link count
+ *
+ * @return total delete link count
+ */
+ public long getTotalDeleteLinkCount() {
+ return this.totalDeleteLinkCount;
+ }
+
+ /**
+ * get success delete link count
+ *
+ * @return success delete link count
+ */
+ public long getSuccessDeleteLinkCount() {
+ return this.successDeleteLinkCount;
+ }
+
+ /**
+ * get total upload file bytes
+ *
+ * @return total upload file bytes
+ */
+ public long getTotalUploadBytes() {
+ return this.totalUploadBytes;
+ }
+
+ /**
+ * get success upload file bytes
+ *
+ * @return success upload file bytes
+ */
+ public long getSuccessUploadBytes() {
+ return this.successUploadBytes;
+ }
+
+ /**
+ * get total append bytes
+ *
+ * @return total append bytes
+ */
+ public long getTotalAppendBytes() {
+ return this.totalAppendBytes;
+ }
+
+ /**
+ * get success append bytes
+ *
+ * @return success append bytes
+ */
+ public long getSuccessAppendBytes() {
+ return this.successAppendBytes;
+ }
+
+ /**
+ * get total modify bytes
+ *
+ * @return total modify bytes
+ */
+ public long getTotalModifyBytes() {
+ return this.totalModifyBytes;
+ }
+
+ /**
+ * get success modify bytes
+ *
+ * @return success modify bytes
+ */
+ public long getSuccessModifyBytes() {
+ return this.successModifyBytes;
+ }
+
+ /**
+ * get total download file bytes
+ *
+ * @return total download file bytes
+ */
+ public long getTotalDownloadloadBytes() {
+ return this.totalDownloadloadBytes;
+ }
+
+ /**
+ * get success download file bytes
+ *
+ * @return success download file bytes
+ */
+ public long getSuccessDownloadloadBytes() {
+ return this.successDownloadloadBytes;
+ }
+
+ /**
+ * get total sync in bytes
+ *
+ * @return total sync in bytes
+ */
+ public long getTotalSyncInBytes() {
+ return this.totalSyncInBytes;
+ }
+
+ /**
+ * get success sync in bytes
+ *
+ * @return success sync in bytes
+ */
+ public long getSuccessSyncInBytes() {
+ return this.successSyncInBytes;
+ }
+
+ /**
+ * get total sync out bytes
+ *
+ * @return total sync out bytes
+ */
+ public long getTotalSyncOutBytes() {
+ return this.totalSyncOutBytes;
+ }
+
+ /**
+ * get success sync out bytes
+ *
+ * @return success sync out bytes
+ */
+ public long getSuccessSyncOutBytes() {
+ return this.successSyncOutBytes;
+ }
+
+ /**
+ * get total file opened count
+ *
+ * @return total file opened bytes
+ */
+ public long getTotalFileOpenCount() {
+ return this.totalFileOpenCount;
+ }
+
+ /**
+ * get success file opened count
+ *
+ * @return success file opened count
+ */
+ public long getSuccessFileOpenCount() {
+ return this.successFileOpenCount;
+ }
+
+ /**
+ * get total file read count
+ *
+ * @return total file read bytes
+ */
+ public long getTotalFileReadCount() {
+ return this.totalFileReadCount;
+ }
+
+ /**
+ * get success file read count
+ *
+ * @return success file read count
+ */
+ public long getSuccessFileReadCount() {
+ return this.successFileReadCount;
+ }
+
+ /**
+ * get total file write count
+ *
+ * @return total file write bytes
+ */
+ public long getTotalFileWriteCount() {
+ return this.totalFileWriteCount;
+ }
+
+ /**
+ * get success file write count
+ *
+ * @return success file write count
+ */
+ public long getSuccessFileWriteCount() {
+ return this.successFileWriteCount;
+ }
+
+ /**
+ * get last source update timestamp
+ *
+ * @return last source update timestamp
+ */
+ public Date getLastSourceUpdate() {
+ return this.lastSourceUpdate;
+ }
+
+ /**
+ * get last synced update timestamp
+ *
+ * @return last synced update timestamp
+ */
+ public Date getLastSyncUpdate() {
+ return this.lastSyncUpdate;
+ }
+
+ /**
+ * get last synced timestamp
+ *
+ * @return last synced timestamp
+ */
+ public Date getLastSyncedTimestamp() {
+ return this.lastSyncedTimestamp;
+ }
+
+ /**
+ * get last heart beat timestamp
+ *
+ * @return last heart beat timestamp
+ */
+ public Date getLastHeartBeatTime() {
+ return this.lastHeartBeatTime;
+ }
+
+ /**
+ * if the trunk server
+ *
+ * @return true for the trunk server, otherwise false
+ */
+ public boolean isTrunkServer() {
+ return this.ifTrunkServer;
+ }
+
+ /**
+ * get connection alloc count
+ *
+ * @return connection alloc count
+ */
+ public int getConnectionAllocCount() {
+ return this.connectionAllocCount;
+ }
+
+ /**
+ * get connection current count
+ *
+ * @return connection current count
+ */
+ public int getConnectionCurrentCount() {
+ return this.connectionCurrentCount;
+ }
+
+ /**
+ * get connection max count
+ *
+ * @return connection max count
+ */
+ public int getConnectionMaxCount() {
+ return this.connectionMaxCount;
+ }
+
+ /**
+ * set fields
+ *
+ * @param bs byte array
+ * @param offset start offset
+ */
+ public void setFields(byte[] bs, int offset) {
+ this.status = byteValue(bs, offset, fieldsArray[FIELD_INDEX_STATUS]);
+ this.id = stringValue(bs, offset, fieldsArray[FIELD_INDEX_ID]);
+ this.ipAddr = stringValue(bs, offset, fieldsArray[FIELD_INDEX_IP_ADDR]);
+ this.srcIpAddr = stringValue(bs, offset, fieldsArray[FIELD_INDEX_SRC_IP_ADDR]);
+ this.domainName = stringValue(bs, offset, fieldsArray[FIELD_INDEX_DOMAIN_NAME]);
+ this.version = stringValue(bs, offset, fieldsArray[FIELD_INDEX_VERSION]);
+ this.totalMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_MB]);
+ this.freeMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_FREE_MB]);
+ this.uploadPriority = intValue(bs, offset, fieldsArray[FIELD_INDEX_UPLOAD_PRIORITY]);
+ this.joinTime = dateValue(bs, offset, fieldsArray[FIELD_INDEX_JOIN_TIME]);
+ this.upTime = dateValue(bs, offset, fieldsArray[FIELD_INDEX_UP_TIME]);
+ this.storePathCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORE_PATH_COUNT]);
+ this.subdirCountPerPath = intValue(bs, offset, fieldsArray[FIELD_INDEX_SUBDIR_COUNT_PER_PATH]);
+ this.storagePort = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_PORT]);
+ this.storageHttpPort = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_HTTP_PORT]);
+ this.currentWritePath = intValue(bs, offset, fieldsArray[FIELD_INDEX_CURRENT_WRITE_PATH]);
+
+ this.connectionAllocCount = int32Value(bs, offset, fieldsArray[FIELD_INDEX_CONNECTION_ALLOC_COUNT]);
+ this.connectionCurrentCount = int32Value(bs, offset, fieldsArray[FIELD_INDEX_CONNECTION_CURRENT_COUNT]);
+ this.connectionMaxCount = int32Value(bs, offset, fieldsArray[FIELD_INDEX_CONNECTION_MAX_COUNT]);
+
+ this.totalUploadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_UPLOAD_COUNT]);
+ this.successUploadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_UPLOAD_COUNT]);
+ this.totalAppendCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_APPEND_COUNT]);
+ this.successAppendCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_APPEND_COUNT]);
+ this.totalModifyCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_MODIFY_COUNT]);
+ this.successModifyCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_MODIFY_COUNT]);
+ this.totalTruncateCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_TRUNCATE_COUNT]);
+ this.successTruncateCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_TRUNCATE_COUNT]);
+ this.totalSetMetaCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_SET_META_COUNT]);
+ this.successSetMetaCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_SET_META_COUNT]);
+ this.totalDeleteCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_DELETE_COUNT]);
+ this.successDeleteCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_DELETE_COUNT]);
+ this.totalDownloadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_DOWNLOAD_COUNT]);
+ this.successDownloadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_DOWNLOAD_COUNT]);
+ this.totalGetMetaCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_GET_META_COUNT]);
+ this.successGetMetaCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_GET_META_COUNT]);
+ this.totalCreateLinkCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_CREATE_LINK_COUNT]);
+ this.successCreateLinkCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_CREATE_LINK_COUNT]);
+ this.totalDeleteLinkCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_DELETE_LINK_COUNT]);
+ this.successDeleteLinkCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_DELETE_LINK_COUNT]);
+ this.totalUploadBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_UPLOAD_BYTES]);
+ this.successUploadBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_UPLOAD_BYTES]);
+ this.totalAppendBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_APPEND_BYTES]);
+ this.successAppendBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_APPEND_BYTES]);
+ this.totalModifyBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_MODIFY_BYTES]);
+ this.successModifyBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_MODIFY_BYTES]);
+ this.totalDownloadloadBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_DOWNLOAD_BYTES]);
+ this.successDownloadloadBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_DOWNLOAD_BYTES]);
+ this.totalSyncInBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_SYNC_IN_BYTES]);
+ this.successSyncInBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_SYNC_IN_BYTES]);
+ this.totalSyncOutBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_SYNC_OUT_BYTES]);
+ this.successSyncOutBytes = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_SYNC_OUT_BYTES]);
+ this.totalFileOpenCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_FILE_OPEN_COUNT]);
+ this.successFileOpenCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_FILE_OPEN_COUNT]);
+ this.totalFileReadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_FILE_READ_COUNT]);
+ this.successFileReadCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_FILE_READ_COUNT]);
+ this.totalFileWriteCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_FILE_WRITE_COUNT]);
+ this.successFileWriteCount = longValue(bs, offset, fieldsArray[FIELD_INDEX_SUCCESS_FILE_WRITE_COUNT]);
+ this.lastSourceUpdate = dateValue(bs, offset, fieldsArray[FIELD_INDEX_LAST_SOURCE_UPDATE]);
+ this.lastSyncUpdate = dateValue(bs, offset, fieldsArray[FIELD_INDEX_LAST_SYNC_UPDATE]);
+ this.lastSyncedTimestamp = dateValue(bs, offset, fieldsArray[FIELD_INDEX_LAST_SYNCED_TIMESTAMP]);
+ this.lastHeartBeatTime = dateValue(bs, offset, fieldsArray[FIELD_INDEX_LAST_HEART_BEAT_TIME]);
+ this.ifTrunkServer = booleanValue(bs, offset, fieldsArray[FIELD_INDEX_IF_TRUNK_FILE]);
+ }
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/TrackerClient.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/TrackerClient.java
new file mode 100644
index 000000000..a812c5ea8
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/TrackerClient.java
@@ -0,0 +1,811 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ *
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package org.csource.fastdfs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.Arrays;
+
+/**
+ * Tracker client
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.19
+ */
+public class TrackerClient {
+ protected TrackerGroup tracker_group;
+ protected byte errno;
+
+ /**
+ * constructor with global tracker group
+ */
+ public TrackerClient() {
+ this.tracker_group = ClientGlobal.g_tracker_group;
+ }
+
+ /**
+ * constructor with specified tracker group
+ *
+ * @param tracker_group the tracker group object
+ */
+ public TrackerClient(TrackerGroup tracker_group) {
+ this.tracker_group = tracker_group;
+ }
+
+ /**
+ * get the error code of last call
+ *
+ * @return the error code of last call
+ */
+ public byte getErrorCode() {
+ return this.errno;
+ }
+
+ /**
+ * get a connection to tracker server
+ *
+ * @return tracker server Socket object, return null if fail
+ */
+ public TrackerServer getConnection() throws IOException {
+ return this.tracker_group.getConnection();
+ }
+
+ /**
+ * query storage server to upload file
+ *
+ * @param trackerServer the tracker server
+ * @return storage server Socket object, return null if fail
+ */
+ public StorageServer getStoreStorage(TrackerServer trackerServer) throws IOException {
+ final String groupName = null;
+ return this.getStoreStorage(trackerServer, groupName);
+ }
+
+ /**
+ * query storage server to upload file
+ *
+ * @param trackerServer the tracker server
+ * @param groupName the group name to upload file to, can be empty
+ * @return storage server object, return null if fail
+ */
+ public StorageServer getStoreStorage(TrackerServer trackerServer, String groupName) throws IOException {
+ byte[] header;
+ String ip_addr;
+ int port;
+ byte cmd;
+ int out_len;
+ boolean bNewConnection;
+ byte store_path;
+ Socket trackerSocket;
+
+ if (trackerServer == null) {
+ trackerServer = getConnection();
+ if (trackerServer == null) {
+ return null;
+ }
+ bNewConnection = true;
+ } else {
+ bNewConnection = false;
+ }
+
+ trackerSocket = trackerServer.getSocket();
+ OutputStream out = trackerSocket.getOutputStream();
+
+ try {
+ if (groupName == null || groupName.length() == 0) {
+ cmd = ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE;
+ out_len = 0;
+ } else {
+ cmd = ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ONE;
+ out_len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN;
+ }
+ header = ProtoCommon.packHeader(cmd, out_len, (byte) 0);
+ out.write(header);
+
+ if (groupName != null && groupName.length() > 0) {
+ byte[] bGroupName;
+ byte[] bs;
+ int group_len;
+
+ bs = groupName.getBytes(ClientGlobal.g_charset);
+ bGroupName = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN];
+
+ if (bs.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) {
+ group_len = bs.length;
+ } else {
+ group_len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN;
+ }
+ Arrays.fill(bGroupName, (byte) 0);
+ System.arraycopy(bs, 0, bGroupName, 0, group_len);
+ out.write(bGroupName);
+ }
+
+ ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(trackerSocket.getInputStream(),
+ ProtoCommon.TRACKER_PROTO_CMD_RESP,
+ ProtoCommon.TRACKER_QUERY_STORAGE_STORE_BODY_LEN);
+ this.errno = pkgInfo.errno;
+ if (pkgInfo.errno != 0) {
+ return null;
+ }
+
+ ip_addr = new String(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN, ProtoCommon.FDFS_IPADDR_SIZE - 1).trim();
+
+ port = (int) ProtoCommon.buff2long(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN
+ + ProtoCommon.FDFS_IPADDR_SIZE - 1);
+ store_path = pkgInfo.body[ProtoCommon.TRACKER_QUERY_STORAGE_STORE_BODY_LEN - 1];
+
+ return new StorageServer(ip_addr, port, store_path);
+ } catch (IOException ex) {
+ if (!bNewConnection) {
+ try {
+ trackerServer.close();
+ } catch (IOException ex1) {
+ ex1.printStackTrace();
+ }
+ }
+
+ throw ex;
+ } finally {
+ if (bNewConnection) {
+ try {
+ trackerServer.close();
+ } catch (IOException ex1) {
+ ex1.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * query storage servers to upload file
+ *
+ * @param trackerServer the tracker server
+ * @param groupName the group name to upload file to, can be empty
+ * @return storage servers, return null if fail
+ */
+ public StorageServer[] getStoreStorages(TrackerServer trackerServer, String groupName) throws IOException {
+ byte[] header;
+ String ip_addr;
+ int port;
+ byte cmd;
+ int out_len;
+ boolean bNewConnection;
+ Socket trackerSocket;
+
+ if (trackerServer == null) {
+ trackerServer = getConnection();
+ if (trackerServer == null) {
+ return null;
+ }
+ bNewConnection = true;
+ } else {
+ bNewConnection = false;
+ }
+
+ trackerSocket = trackerServer.getSocket();
+ OutputStream out = trackerSocket.getOutputStream();
+
+ try {
+ if (groupName == null || groupName.length() == 0) {
+ cmd = ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ALL;
+ out_len = 0;
+ } else {
+ cmd = ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITH_GROUP_ALL;
+ out_len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN;
+ }
+ header = ProtoCommon.packHeader(cmd, out_len, (byte) 0);
+ out.write(header);
+
+ if (groupName != null && groupName.length() > 0) {
+ byte[] bGroupName;
+ byte[] bs;
+ int group_len;
+
+ bs = groupName.getBytes(ClientGlobal.g_charset);
+ bGroupName = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN];
+
+ if (bs.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) {
+ group_len = bs.length;
+ } else {
+ group_len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN;
+ }
+ Arrays.fill(bGroupName, (byte) 0);
+ System.arraycopy(bs, 0, bGroupName, 0, group_len);
+ out.write(bGroupName);
+ }
+
+ ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(trackerSocket.getInputStream(),
+ ProtoCommon.TRACKER_PROTO_CMD_RESP, -1);
+ this.errno = pkgInfo.errno;
+ if (pkgInfo.errno != 0) {
+ return null;
+ }
+
+ if (pkgInfo.body.length < ProtoCommon.TRACKER_QUERY_STORAGE_STORE_BODY_LEN) {
+ this.errno = ProtoCommon.ERR_NO_EINVAL;
+ return null;
+ }
+
+ int ipPortLen = pkgInfo.body.length - (ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + 1);
+ final int recordLength = ProtoCommon.FDFS_IPADDR_SIZE - 1 + ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ if (ipPortLen % recordLength != 0) {
+ this.errno = ProtoCommon.ERR_NO_EINVAL;
+ return null;
+ }
+
+ int serverCount = ipPortLen / recordLength;
+ if (serverCount > 16) {
+ this.errno = ProtoCommon.ERR_NO_ENOSPC;
+ return null;
+ }
+
+ StorageServer[] results = new StorageServer[serverCount];
+ byte store_path = pkgInfo.body[pkgInfo.body.length - 1];
+ int offset = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN;
+
+ for (int i = 0; i < serverCount; i++) {
+ ip_addr = new String(pkgInfo.body, offset, ProtoCommon.FDFS_IPADDR_SIZE - 1).trim();
+ offset += ProtoCommon.FDFS_IPADDR_SIZE - 1;
+
+ port = (int) ProtoCommon.buff2long(pkgInfo.body, offset);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ results[i] = new StorageServer(ip_addr, port, store_path);
+ }
+
+ return results;
+ } catch (IOException ex) {
+ if (!bNewConnection) {
+ try {
+ trackerServer.close();
+ } catch (IOException ex1) {
+ ex1.printStackTrace();
+ }
+ }
+
+ throw ex;
+ } finally {
+ if (bNewConnection) {
+ try {
+ trackerServer.close();
+ } catch (IOException ex1) {
+ ex1.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * query storage server to download file
+ *
+ * @param trackerServer the tracker server
+ * @param groupName the group name of storage server
+ * @param filename filename on storage server
+ * @return storage server Socket object, return null if fail
+ */
+ public StorageServer getFetchStorage(TrackerServer trackerServer,
+ String groupName, String filename) throws IOException {
+ ServerInfo[] servers = this.getStorages(trackerServer, ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE,
+ groupName, filename);
+ if (servers == null) {
+ return null;
+ } else {
+ return new StorageServer(servers[0].getIpAddr(), servers[0].getPort(), 0);
+ }
+ }
+
+ /**
+ * query storage server to update file (delete file or set meta data)
+ *
+ * @param trackerServer the tracker server
+ * @param groupName the group name of storage server
+ * @param filename filename on storage server
+ * @return storage server Socket object, return null if fail
+ */
+ public StorageServer getUpdateStorage(TrackerServer trackerServer,
+ String groupName, String filename) throws IOException {
+ ServerInfo[] servers = this.getStorages(trackerServer, ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE,
+ groupName, filename);
+ if (servers == null) {
+ return null;
+ } else {
+ return new StorageServer(servers[0].getIpAddr(), servers[0].getPort(), 0);
+ }
+ }
+
+ /**
+ * get storage servers to download file
+ *
+ * @param trackerServer the tracker server
+ * @param groupName the group name of storage server
+ * @param filename filename on storage server
+ * @return storage servers, return null if fail
+ */
+ public ServerInfo[] getFetchStorages(TrackerServer trackerServer,
+ String groupName, String filename) throws IOException {
+ return this.getStorages(trackerServer, ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ALL,
+ groupName, filename);
+ }
+
+ /**
+ * query storage server to download file
+ *
+ * @param trackerServer the tracker server
+ * @param cmd command code, ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_FETCH_ONE or
+ * ProtoCommon.TRACKER_PROTO_CMD_SERVICE_QUERY_UPDATE
+ * @param groupName the group name of storage server
+ * @param filename filename on storage server
+ * @return storage server Socket object, return null if fail
+ */
+ protected ServerInfo[] getStorages(TrackerServer trackerServer,
+ byte cmd, String groupName, String filename) throws IOException {
+ byte[] header;
+ byte[] bFileName;
+ byte[] bGroupName;
+ byte[] bs;
+ int len;
+ String ip_addr;
+ int port;
+ boolean bNewConnection;
+ Socket trackerSocket;
+
+ if (trackerServer == null) {
+ trackerServer = getConnection();
+ if (trackerServer == null) {
+ return null;
+ }
+ bNewConnection = true;
+ } else {
+ bNewConnection = false;
+ }
+ trackerSocket = trackerServer.getSocket();
+ OutputStream out = trackerSocket.getOutputStream();
+
+ try {
+ bs = groupName.getBytes(ClientGlobal.g_charset);
+ bGroupName = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN];
+ bFileName = filename.getBytes(ClientGlobal.g_charset);
+
+ if (bs.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) {
+ len = bs.length;
+ } else {
+ len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN;
+ }
+ Arrays.fill(bGroupName, (byte) 0);
+ System.arraycopy(bs, 0, bGroupName, 0, len);
+
+ header = ProtoCommon.packHeader(cmd, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + bFileName.length, (byte) 0);
+ byte[] wholePkg = new byte[header.length + bGroupName.length + bFileName.length];
+ System.arraycopy(header, 0, wholePkg, 0, header.length);
+ System.arraycopy(bGroupName, 0, wholePkg, header.length, bGroupName.length);
+ System.arraycopy(bFileName, 0, wholePkg, header.length + bGroupName.length, bFileName.length);
+ out.write(wholePkg);
+
+ ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(trackerSocket.getInputStream(),
+ ProtoCommon.TRACKER_PROTO_CMD_RESP, -1);
+ this.errno = pkgInfo.errno;
+ if (pkgInfo.errno != 0) {
+ return null;
+ }
+
+ if (pkgInfo.body.length < ProtoCommon.TRACKER_QUERY_STORAGE_FETCH_BODY_LEN) {
+ throw new IOException("Invalid body length: " + pkgInfo.body.length);
+ }
+
+ if ((pkgInfo.body.length - ProtoCommon.TRACKER_QUERY_STORAGE_FETCH_BODY_LEN) % (ProtoCommon.FDFS_IPADDR_SIZE - 1) != 0) {
+ throw new IOException("Invalid body length: " + pkgInfo.body.length);
+ }
+
+ int server_count = 1 + (pkgInfo.body.length - ProtoCommon.TRACKER_QUERY_STORAGE_FETCH_BODY_LEN) / (ProtoCommon.FDFS_IPADDR_SIZE - 1);
+
+ ip_addr = new String(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN, ProtoCommon.FDFS_IPADDR_SIZE - 1).trim();
+ int offset = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + ProtoCommon.FDFS_IPADDR_SIZE - 1;
+
+ port = (int) ProtoCommon.buff2long(pkgInfo.body, offset);
+ offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE;
+
+ ServerInfo[] servers = new ServerInfo[server_count];
+ servers[0] = new ServerInfo(ip_addr, port);
+ for (int i = 1; i < server_count; i++) {
+ servers[i] = new ServerInfo(new String(pkgInfo.body, offset, ProtoCommon.FDFS_IPADDR_SIZE - 1).trim(), port);
+ offset += ProtoCommon.FDFS_IPADDR_SIZE - 1;
+ }
+
+ return servers;
+ } catch (IOException ex) {
+ if (!bNewConnection) {
+ try {
+ trackerServer.close();
+ } catch (IOException ex1) {
+ ex1.printStackTrace();
+ }
+ }
+
+ throw ex;
+ } finally {
+ if (bNewConnection) {
+ try {
+ trackerServer.close();
+ } catch (IOException ex1) {
+ ex1.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * query storage server to download file
+ *
+ * @param trackerServer the tracker server
+ * @param file_id the file id(including group name and filename)
+ * @return storage server Socket object, return null if fail
+ */
+ public StorageServer getFetchStorage1(TrackerServer trackerServer, String file_id) throws IOException {
+ String[] parts = new String[2];
+ this.errno = StorageClient1.split_file_id(file_id, parts);
+ if (this.errno != 0) {
+ return null;
+ }
+
+ return this.getFetchStorage(trackerServer, parts[0], parts[1]);
+ }
+
+ /**
+ * get storage servers to download file
+ *
+ * @param trackerServer the tracker server
+ * @param file_id the file id(including group name and filename)
+ * @return storage servers, return null if fail
+ */
+ public ServerInfo[] getFetchStorages1(TrackerServer trackerServer, String file_id) throws IOException {
+ String[] parts = new String[2];
+ this.errno = StorageClient1.split_file_id(file_id, parts);
+ if (this.errno != 0) {
+ return null;
+ }
+
+ return this.getFetchStorages(trackerServer, parts[0], parts[1]);
+ }
+
+ /**
+ * list groups
+ *
+ * @param trackerServer the tracker server
+ * @return group stat array, return null if fail
+ */
+ public StructGroupStat[] listGroups(TrackerServer trackerServer) throws IOException {
+ byte[] header;
+ String ip_addr;
+ int port;
+ byte cmd;
+ int out_len;
+ boolean bNewConnection;
+ byte store_path;
+ Socket trackerSocket;
+
+ if (trackerServer == null) {
+ trackerServer = getConnection();
+ if (trackerServer == null) {
+ return null;
+ }
+ bNewConnection = true;
+ } else {
+ bNewConnection = false;
+ }
+
+ trackerSocket = trackerServer.getSocket();
+ OutputStream out = trackerSocket.getOutputStream();
+
+ try {
+ header = ProtoCommon.packHeader(ProtoCommon.TRACKER_PROTO_CMD_SERVER_LIST_GROUP, 0, (byte) 0);
+ out.write(header);
+
+ ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(trackerSocket.getInputStream(),
+ ProtoCommon.TRACKER_PROTO_CMD_RESP, -1);
+ this.errno = pkgInfo.errno;
+ if (pkgInfo.errno != 0) {
+ return null;
+ }
+
+ ProtoStructDecoder decoder = new ProtoStructDecoder();
+ return decoder.decode(pkgInfo.body, StructGroupStat.class, StructGroupStat.getFieldsTotalSize());
+ } catch (IOException ex) {
+ if (!bNewConnection) {
+ try {
+ trackerServer.close();
+ } catch (IOException ex1) {
+ ex1.printStackTrace();
+ }
+ }
+
+ throw ex;
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ this.errno = ProtoCommon.ERR_NO_EINVAL;
+ return null;
+ } finally {
+ if (bNewConnection) {
+ try {
+ trackerServer.close();
+ } catch (IOException ex1) {
+ ex1.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * query storage server stat info of the group
+ *
+ * @param trackerServer the tracker server
+ * @param groupName the group name of storage server
+ * @return storage server stat array, return null if fail
+ */
+ public StructStorageStat[] listStorages(TrackerServer trackerServer, String groupName) throws IOException {
+ final String storageIpAddr = null;
+ return this.listStorages(trackerServer, groupName, storageIpAddr);
+ }
+
+ /**
+ * query storage server stat info of the group
+ *
+ * @param trackerServer the tracker server
+ * @param groupName the group name of storage server
+ * @param storageIpAddr the storage server ip address, can be null or empty
+ * @return storage server stat array, return null if fail
+ */
+ public StructStorageStat[] listStorages(TrackerServer trackerServer,
+ String groupName, String storageIpAddr) throws IOException {
+ byte[] header;
+ byte[] bGroupName;
+ byte[] bs;
+ int len;
+ boolean bNewConnection;
+ Socket trackerSocket;
+
+ if (trackerServer == null) {
+ trackerServer = getConnection();
+ if (trackerServer == null) {
+ return null;
+ }
+ bNewConnection = true;
+ } else {
+ bNewConnection = false;
+ }
+ trackerSocket = trackerServer.getSocket();
+ OutputStream out = trackerSocket.getOutputStream();
+
+ try {
+ bs = groupName.getBytes(ClientGlobal.g_charset);
+ bGroupName = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN];
+
+ if (bs.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) {
+ len = bs.length;
+ } else {
+ len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN;
+ }
+ Arrays.fill(bGroupName, (byte) 0);
+ System.arraycopy(bs, 0, bGroupName, 0, len);
+
+ int ipAddrLen;
+ byte[] bIpAddr;
+ if (storageIpAddr != null && storageIpAddr.length() > 0) {
+ bIpAddr = storageIpAddr.getBytes(ClientGlobal.g_charset);
+ if (bIpAddr.length < ProtoCommon.FDFS_IPADDR_SIZE) {
+ ipAddrLen = bIpAddr.length;
+ } else {
+ ipAddrLen = ProtoCommon.FDFS_IPADDR_SIZE - 1;
+ }
+ } else {
+ bIpAddr = null;
+ ipAddrLen = 0;
+ }
+
+ header = ProtoCommon.packHeader(ProtoCommon.TRACKER_PROTO_CMD_SERVER_LIST_STORAGE, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + ipAddrLen, (byte) 0);
+ byte[] wholePkg = new byte[header.length + bGroupName.length + ipAddrLen];
+ System.arraycopy(header, 0, wholePkg, 0, header.length);
+ System.arraycopy(bGroupName, 0, wholePkg, header.length, bGroupName.length);
+ if (ipAddrLen > 0) {
+ System.arraycopy(bIpAddr, 0, wholePkg, header.length + bGroupName.length, ipAddrLen);
+ }
+ out.write(wholePkg);
+
+ ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(trackerSocket.getInputStream(),
+ ProtoCommon.TRACKER_PROTO_CMD_RESP, -1);
+ this.errno = pkgInfo.errno;
+ if (pkgInfo.errno != 0) {
+ return null;
+ }
+
+ ProtoStructDecoder decoder = new ProtoStructDecoder();
+ return decoder.decode(pkgInfo.body, StructStorageStat.class, StructStorageStat.getFieldsTotalSize());
+ } catch (IOException ex) {
+ if (!bNewConnection) {
+ try {
+ trackerServer.close();
+ } catch (IOException ex1) {
+ ex1.printStackTrace();
+ }
+ }
+
+ throw ex;
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ this.errno = ProtoCommon.ERR_NO_EINVAL;
+ return null;
+ } finally {
+ if (bNewConnection) {
+ try {
+ trackerServer.close();
+ } catch (IOException ex1) {
+ ex1.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * delete a storage server from the tracker server
+ *
+ * @param trackerServer the connected tracker server
+ * @param groupName the group name of storage server
+ * @param storageIpAddr the storage server ip address
+ * @return true for success, false for fail
+ */
+ private boolean deleteStorage(TrackerServer trackerServer,
+ String groupName, String storageIpAddr) throws IOException {
+ byte[] header;
+ byte[] bGroupName;
+ byte[] bs;
+ int len;
+ Socket trackerSocket;
+
+ trackerSocket = trackerServer.getSocket();
+ OutputStream out = trackerSocket.getOutputStream();
+
+ bs = groupName.getBytes(ClientGlobal.g_charset);
+ bGroupName = new byte[ProtoCommon.FDFS_GROUP_NAME_MAX_LEN];
+
+ if (bs.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) {
+ len = bs.length;
+ } else {
+ len = ProtoCommon.FDFS_GROUP_NAME_MAX_LEN;
+ }
+ Arrays.fill(bGroupName, (byte) 0);
+ System.arraycopy(bs, 0, bGroupName, 0, len);
+
+ int ipAddrLen;
+ byte[] bIpAddr = storageIpAddr.getBytes(ClientGlobal.g_charset);
+ if (bIpAddr.length < ProtoCommon.FDFS_IPADDR_SIZE) {
+ ipAddrLen = bIpAddr.length;
+ } else {
+ ipAddrLen = ProtoCommon.FDFS_IPADDR_SIZE - 1;
+ }
+
+ header = ProtoCommon.packHeader(ProtoCommon.TRACKER_PROTO_CMD_SERVER_DELETE_STORAGE, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + ipAddrLen, (byte) 0);
+ byte[] wholePkg = new byte[header.length + bGroupName.length + ipAddrLen];
+ System.arraycopy(header, 0, wholePkg, 0, header.length);
+ System.arraycopy(bGroupName, 0, wholePkg, header.length, bGroupName.length);
+ System.arraycopy(bIpAddr, 0, wholePkg, header.length + bGroupName.length, ipAddrLen);
+ out.write(wholePkg);
+
+ ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(trackerSocket.getInputStream(),
+ ProtoCommon.TRACKER_PROTO_CMD_RESP, 0);
+ this.errno = pkgInfo.errno;
+ return pkgInfo.errno == 0;
+ }
+
+ /**
+ * delete a storage server from the global FastDFS cluster
+ *
+ * @param groupName the group name of storage server
+ * @param storageIpAddr the storage server ip address
+ * @return true for success, false for fail
+ */
+ public boolean deleteStorage(String groupName, String storageIpAddr) throws IOException {
+ return this.deleteStorage(ClientGlobal.g_tracker_group, groupName, storageIpAddr);
+ }
+
+ /**
+ * delete a storage server from the FastDFS cluster
+ *
+ * @param trackerGroup the tracker server group
+ * @param groupName the group name of storage server
+ * @param storageIpAddr the storage server ip address
+ * @return true for success, false for fail
+ */
+ public boolean deleteStorage(TrackerGroup trackerGroup,
+ String groupName, String storageIpAddr) throws IOException {
+ int serverIndex;
+ int notFoundCount;
+ TrackerServer trackerServer;
+
+ notFoundCount = 0;
+ for (serverIndex = 0; serverIndex < trackerGroup.tracker_servers.length; serverIndex++) {
+ try {
+ trackerServer = trackerGroup.getConnection(serverIndex);
+ } catch (IOException ex) {
+ ex.printStackTrace(System.err);
+ this.errno = ProtoCommon.ECONNREFUSED;
+ return false;
+ }
+
+ try {
+ StructStorageStat[] storageStats = listStorages(trackerServer, groupName, storageIpAddr);
+ if (storageStats == null) {
+ if (this.errno == ProtoCommon.ERR_NO_ENOENT) {
+ notFoundCount++;
+ } else {
+ return false;
+ }
+ } else if (storageStats.length == 0) {
+ notFoundCount++;
+ } else if (storageStats[0].getStatus() == ProtoCommon.FDFS_STORAGE_STATUS_ONLINE ||
+ storageStats[0].getStatus() == ProtoCommon.FDFS_STORAGE_STATUS_ACTIVE) {
+ this.errno = ProtoCommon.ERR_NO_EBUSY;
+ return false;
+ }
+ } finally {
+ try {
+ trackerServer.close();
+ } catch (IOException ex1) {
+ ex1.printStackTrace();
+ }
+ }
+ }
+
+ if (notFoundCount == trackerGroup.tracker_servers.length) {
+ this.errno = ProtoCommon.ERR_NO_ENOENT;
+ return false;
+ }
+
+ notFoundCount = 0;
+ for (serverIndex = 0; serverIndex < trackerGroup.tracker_servers.length; serverIndex++) {
+ try {
+ trackerServer = trackerGroup.getConnection(serverIndex);
+ } catch (IOException ex) {
+ System.err.println("connect to server " + trackerGroup.tracker_servers[serverIndex].getAddress().getHostAddress() + ":" + trackerGroup.tracker_servers[serverIndex].getPort() + " fail");
+ ex.printStackTrace(System.err);
+ this.errno = ProtoCommon.ECONNREFUSED;
+ return false;
+ }
+
+ try {
+ if (!this.deleteStorage(trackerServer, groupName, storageIpAddr)) {
+ if (this.errno != 0) {
+ if (this.errno == ProtoCommon.ERR_NO_ENOENT) {
+ notFoundCount++;
+ } else if (this.errno != ProtoCommon.ERR_NO_EALREADY) {
+ return false;
+ }
+ }
+ }
+ } finally {
+ try {
+ trackerServer.close();
+ } catch (IOException ex1) {
+ ex1.printStackTrace();
+ }
+ }
+ }
+
+ if (notFoundCount == trackerGroup.tracker_servers.length) {
+ this.errno = ProtoCommon.ERR_NO_ENOENT;
+ return false;
+ }
+
+ if (this.errno == ProtoCommon.ERR_NO_ENOENT) {
+ this.errno = 0;
+ }
+
+ return this.errno == 0;
+ }
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/TrackerGroup.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/TrackerGroup.java
new file mode 100644
index 000000000..d0d3a53e7
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/TrackerGroup.java
@@ -0,0 +1,107 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ *
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package org.csource.fastdfs;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+/**
+ * Tracker server group
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.17
+ */
+public class TrackerGroup {
+ public int tracker_server_index;
+ public InetSocketAddress[] tracker_servers;
+ protected Integer lock;
+
+ /**
+ * Constructor
+ *
+ * @param tracker_servers tracker servers
+ */
+ public TrackerGroup(InetSocketAddress[] tracker_servers) {
+ this.tracker_servers = tracker_servers;
+ this.lock = new Integer(0);
+ this.tracker_server_index = 0;
+ }
+
+ /**
+ * return connected tracker server
+ *
+ * @return connected tracker server, null for fail
+ */
+ public TrackerServer getConnection(int serverIndex) throws IOException {
+ Socket sock = new Socket();
+ sock.setReuseAddress(true);
+ sock.setSoTimeout(ClientGlobal.g_network_timeout);
+ sock.connect(this.tracker_servers[serverIndex], ClientGlobal.g_connect_timeout);
+ return new TrackerServer(sock, this.tracker_servers[serverIndex]);
+ }
+
+ /**
+ * return connected tracker server
+ *
+ * @return connected tracker server, null for fail
+ */
+ public TrackerServer getConnection() throws IOException {
+ int current_index;
+
+ synchronized (this.lock) {
+ this.tracker_server_index++;
+ if (this.tracker_server_index >= this.tracker_servers.length) {
+ this.tracker_server_index = 0;
+ }
+
+ current_index = this.tracker_server_index;
+ }
+
+ try {
+ return this.getConnection(current_index);
+ } catch (IOException ex) {
+ System.err.println("connect to server " + this.tracker_servers[current_index].getAddress().getHostAddress() + ":" + this.tracker_servers[current_index].getPort() + " fail");
+ ex.printStackTrace(System.err);
+ }
+
+ for (int i = 0; i < this.tracker_servers.length; i++) {
+ if (i == current_index) {
+ continue;
+ }
+
+ try {
+ TrackerServer trackerServer = this.getConnection(i);
+
+ synchronized (this.lock) {
+ if (this.tracker_server_index == current_index) {
+ this.tracker_server_index = i;
+ }
+ }
+
+ return trackerServer;
+ } catch (IOException ex) {
+ System.err.println("connect to server " + this.tracker_servers[i].getAddress().getHostAddress() + ":" + this.tracker_servers[i].getPort() + " fail");
+ ex.printStackTrace(System.err);
+ }
+ }
+
+ return null;
+ }
+
+ public Object clone() {
+ InetSocketAddress[] trackerServers = new InetSocketAddress[this.tracker_servers.length];
+ for (int i = 0; i < trackerServers.length; i++) {
+ trackerServers[i] = new InetSocketAddress(this.tracker_servers[i].getAddress().getHostAddress(), this.tracker_servers[i].getPort());
+ }
+
+ return new TrackerGroup(trackerServers);
+ }
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/TrackerServer.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/TrackerServer.java
new file mode 100644
index 000000000..dfc11b02e
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/TrackerServer.java
@@ -0,0 +1,81 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ *
+ * FastDFS Java Client may be copied only under the terms of the GNU Lesser
+ * General Public License (LGPL).
+ * Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
+ */
+
+package org.csource.fastdfs;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+/**
+ * Tracker Server Info
+ *
+ * @author Happy Fish / YuQing
+ * @version Version 1.11
+ */
+public class TrackerServer {
+ protected Socket sock;
+ protected InetSocketAddress inetSockAddr;
+
+ /**
+ * Constructor
+ *
+ * @param sock Socket of server
+ * @param inetSockAddr the server info
+ */
+ public TrackerServer(Socket sock, InetSocketAddress inetSockAddr) {
+ this.sock = sock;
+ this.inetSockAddr = inetSockAddr;
+ }
+
+ /**
+ * get the connected socket
+ *
+ * @return the socket
+ */
+ public Socket getSocket() throws IOException {
+ if (this.sock == null) {
+ this.sock = ClientGlobal.getSocket(this.inetSockAddr);
+ }
+
+ return this.sock;
+ }
+
+ /**
+ * get the server info
+ *
+ * @return the server info
+ */
+ public InetSocketAddress getInetSocketAddress() {
+ return this.inetSockAddr;
+ }
+
+ public OutputStream getOutputStream() throws IOException {
+ return this.sock.getOutputStream();
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return this.sock.getInputStream();
+ }
+
+ public void close() throws IOException {
+ if (this.sock != null) {
+ try {
+ ProtoCommon.closeSocket(this.sock);
+ } finally {
+ this.sock = null;
+ }
+ }
+ }
+
+ protected void finalize() throws Throwable {
+ this.close();
+ }
+}
diff --git a/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/UploadCallback.java b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/UploadCallback.java
new file mode 100644
index 000000000..f388b46f2
--- /dev/null
+++ b/psi-java/fastdfs-spring-boot-starter/src/main/java/org/csource/fastdfs/UploadCallback.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (C) 2008 Happy Fish / YuQing
+ *