From ec353b09e592869a650e7b3f22d1da70ea2ce42f Mon Sep 17 00:00:00 2001 From: "959814898@qq.com" Date: Tue, 16 May 2023 22:43:17 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ReadWriteJackson2HttpMessageConverter.java | 190 ++ .../DefaultBeanSerializerModifier.java | 140 ++ .../el/start/core/jackson/JavaTimeModule.java | 44 + ...appingApiJackson2HttpMessageConverter.java | 107 + .../autoconfigure/DfsAutoConfiguration.java | 26 + .../dfs/autoconfigure/DfsAutoProperties.java | 41 + .../main/resources/META-INF/spring.factories | 2 + .../com/aivfo/dfs/client/core/Client.java | 99 + .../core/exception/DfsFailedException.java | 36 + .../core/fastDFS/client/FastDFSClient.java | 235 +++ .../core/fastDFS/common/FastDFSUtils.java | 137 ++ .../core/fastDFS/constant/FastDFSCmd.java | 13 + .../fastDFS/constant/FastDFSConstant.java | 73 + .../fastDFS/properties/FastDFSProperties.java | 115 ++ .../com/aivfo/dfs/DfsTestApplication.java | 64 + .../src/test/resources/application.properties | 8 + .../main/java/com/csource/common/Base64.java | 491 +++++ .../com/csource/common/IniFileReader.java | 215 ++ .../java/com/csource/common/MyException.java | 24 + .../com/csource/common/NameValuePair.java | 48 + .../com/csource/fastdfs/ClientGlobal.java | 357 ++++ .../com/csource/fastdfs/DownloadCallback.java | 29 + .../com/csource/fastdfs/DownloadStream.java | 45 + .../java/com/csource/fastdfs/FileInfo.java | 175 ++ .../java/com/csource/fastdfs/ProtoCommon.java | 506 +++++ .../csource/fastdfs/ProtoStructDecoder.java | 49 + .../java/com/csource/fastdfs/ServerInfo.java | 66 + .../com/csource/fastdfs/StorageClient.java | 1807 +++++++++++++++++ .../com/csource/fastdfs/StorageClient1.java | 766 +++++++ .../com/csource/fastdfs/StorageServer.java | 57 + .../java/com/csource/fastdfs/StructBase.java | 73 + .../com/csource/fastdfs/StructGroupStat.java | 225 ++ .../csource/fastdfs/StructStorageStat.java | 982 +++++++++ .../com/csource/fastdfs/TrackerClient.java | 809 ++++++++ .../com/csource/fastdfs/TrackerGroup.java | 101 + .../com/csource/fastdfs/TrackerServer.java | 66 + .../com/csource/fastdfs/UploadCallback.java | 28 + .../com/csource/fastdfs/UploadStream.java | 56 + .../com/csource/fastdfs/pool/Connection.java | 140 ++ .../fastdfs/pool/ConnectionFactory.java | 29 + .../fastdfs/pool/ConnectionManager.java | 148 ++ .../csource/fastdfs/pool/ConnectionPool.java | 81 + 42 files changed, 8703 insertions(+) create mode 100644 band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/jackson/AbstractReadWriteJackson2HttpMessageConverter.java create mode 100644 band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/jackson/DefaultBeanSerializerModifier.java create mode 100644 band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/jackson/JavaTimeModule.java create mode 100644 band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/jackson/MappingApiJackson2HttpMessageConverter.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-autoconfigure/src/main/java/com/aivfo/dfs/autoconfigure/DfsAutoConfiguration.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-autoconfigure/src/main/java/com/aivfo/dfs/autoconfigure/DfsAutoProperties.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/Client.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/exception/DfsFailedException.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/client/FastDFSClient.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/common/FastDFSUtils.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/constant/FastDFSCmd.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/constant/FastDFSConstant.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/properties/FastDFSProperties.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-starter/src/test/java/com/aivfo/dfs/DfsTestApplication.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-starter/src/test/resources/application.properties create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/common/Base64.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/common/IniFileReader.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/common/MyException.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/common/NameValuePair.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/ClientGlobal.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/DownloadCallback.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/DownloadStream.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/FileInfo.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/ProtoCommon.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/ProtoStructDecoder.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/ServerInfo.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/StorageClient.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/StorageClient1.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/StorageServer.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/StructBase.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/StructGroupStat.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/StructStorageStat.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/TrackerClient.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/TrackerGroup.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/TrackerServer.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/UploadCallback.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/UploadStream.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/pool/Connection.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/pool/ConnectionFactory.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/pool/ConnectionManager.java create mode 100644 module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/pool/ConnectionPool.java diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/jackson/AbstractReadWriteJackson2HttpMessageConverter.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/jackson/AbstractReadWriteJackson2HttpMessageConverter.java new file mode 100644 index 0000000..8f42c89 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/jackson/AbstractReadWriteJackson2HttpMessageConverter.java @@ -0,0 +1,190 @@ +package com.aivfo.el.start.core.jackson; + +import com.aivfo.el.starter.base.utils.Charsets; +import com.fasterxml.jackson.core.JsonEncoding; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.PrettyPrinter; +import com.fasterxml.jackson.core.util.DefaultIndenter; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.exc.InvalidDefinitionException; +import com.fasterxml.jackson.databind.ser.FilterProvider; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.http.HttpOutputMessage; +import org.springframework.http.MediaType; +import org.springframework.http.converter.AbstractGenericHttpMessageConverter; +import org.springframework.http.converter.HttpMessageConversionException; +import org.springframework.http.converter.HttpMessageNotWritableException; +import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter; +import org.springframework.http.converter.json.MappingJacksonValue; +import org.springframework.util.TypeUtils; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicReference; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: 分读写的 json 消息 处理器 + */ +public abstract class AbstractReadWriteJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter { + /** + * DEFAULT_CHARSET + */ + private static final java.nio.charset.Charset DEFAULT_CHARSET = Charsets.UTF_8; + + /** + * Write object mapper + */ + private final ObjectMapper writeObjectMapper; + /** + * Sse pretty printer + */ + @Nullable + private PrettyPrinter ssePrettyPrinter; + + /** + * Instantiates a new Abstract read write jackson 2 http message converter. + * + * @param readObjectMapper the read object mapper + * @param writeObjectMapper the write object mapper + * @param supportedMediaType the supported media type + * @since 1.0.0 + */ + protected AbstractReadWriteJackson2HttpMessageConverter(ObjectMapper readObjectMapper, + ObjectMapper writeObjectMapper, + MediaType supportedMediaType) { + this(readObjectMapper, writeObjectMapper); + this.setSupportedMediaTypes(Collections.singletonList(supportedMediaType)); + this.initSsePrettyPrinter(); + } + + /** + * Instantiates a new Abstract read write jackson 2 http message converter. + * + * @param readObjectMapper the read object mapper + * @param writeObjectMapper the write object mapper + * @since 1.0.0 + */ + protected AbstractReadWriteJackson2HttpMessageConverter(ObjectMapper readObjectMapper, ObjectMapper writeObjectMapper) { + super(readObjectMapper); + this.writeObjectMapper = writeObjectMapper; + this.initSsePrettyPrinter(); + } + + /** + * Init sse pretty printer + * + * @since 1.0.0 + */ + private void initSsePrettyPrinter() { + this.setDefaultCharset(DEFAULT_CHARSET); + DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter(); + prettyPrinter.indentObjectsWith(new DefaultIndenter(" ", "\ndata:")); + this.ssePrettyPrinter = prettyPrinter; + } + + /** + * Instantiates a new Abstract read write jackson 2 http message converter. + * + * @param readObjectMapper the read object mapper + * @param writeObjectMapper the write object mapper + * @param supportedMediaTypes the supported media types + * @since 1.0.0 + */ + protected AbstractReadWriteJackson2HttpMessageConverter(ObjectMapper readObjectMapper, + ObjectMapper writeObjectMapper, + MediaType... supportedMediaTypes) { + this(readObjectMapper, writeObjectMapper); + this.setSupportedMediaTypes(Arrays.asList(supportedMediaTypes)); + } + + /** + * Can write boolean + * + * @param clazz clazz + * @param mediaType media type + * @return the boolean + * @since 1.0.0 + */ + @Override + public boolean canWrite(@NotNull Class clazz, @Nullable MediaType mediaType) { + if (!this.canWrite(mediaType)) { + return false; + } + AtomicReference causeRef = new AtomicReference<>(); + if (this.objectMapper.canSerialize(clazz, causeRef)) { + return true; + } + this.logWarningIfNecessary(clazz, causeRef.get()); + return false; + } + + /** + * 最终的序列化逻辑 + * + * @param object object + * @param type type + * @param outputMessage output message + * @throws IOException io exception + * @throws HttpMessageNotWritableException http message not writable exception + * @see AbstractGenericHttpMessageConverter#write + * @since 1.0.0 + */ + @Override + protected void writeInternal(@NotNull Object object, @Nullable Type type, @NotNull HttpOutputMessage outputMessage) + throws IOException, HttpMessageNotWritableException { + + MediaType contentType = outputMessage.getHeaders().getContentType(); + JsonEncoding encoding = this.getJsonEncoding(contentType); + JsonGenerator generator = this.writeObjectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding); + try { + this.writePrefix(generator, object); + + Object value = object; + Class serializationView = null; + FilterProvider filters = null; + JavaType javaType = null; + + if (object instanceof MappingJacksonValue) { + MappingJacksonValue container = (MappingJacksonValue) object; + value = container.getValue(); + serializationView = container.getSerializationView(); + filters = container.getFilters(); + } + if (type != null && TypeUtils.isAssignable(type, value.getClass())) { + javaType = this.getJavaType(type, null); + } + + ObjectWriter objectWriter = (serializationView != null + ? this.writeObjectMapper.writerWithView(serializationView) + : this.writeObjectMapper.writer()); + if (filters != null) { + objectWriter = objectWriter.with(filters); + } + if (javaType != null && javaType.isContainerType()) { + objectWriter = objectWriter.forType(javaType); + } + SerializationConfig config = objectWriter.getConfig(); + if (contentType != null + && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) + && config.isEnabled(SerializationFeature.INDENT_OUTPUT)) { + objectWriter = objectWriter.with(this.ssePrettyPrinter); + } + objectWriter.writeValue(generator, value); + + this.writeSuffix(generator, object); + generator.flush(); + } catch (InvalidDefinitionException ex) { + throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex); + } catch (JsonProcessingException ex) { + throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getOriginalMessage(), ex); + } + } + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/jackson/DefaultBeanSerializerModifier.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/jackson/DefaultBeanSerializerModifier.java new file mode 100644 index 0000000..772f9d1 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/jackson/DefaultBeanSerializerModifier.java @@ -0,0 +1,140 @@ +package com.aivfo.el.start.core.jackson; + +import com.aivfo.el.starter.base.utils.StringPool; +import com.aivfo.el.starter.base.utils.StringUtils; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; +import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.time.OffsetDateTime; +import java.time.temporal.TemporalAccessor; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +/** + *

Description: jackson 默认值为 null 时的处理, + * 规则: + * {@code + * number: 0 + * string: null + * date: null + * boolean: false + * array: [] + * Object: {} + * } + */ +public class DefaultBeanSerializerModifier extends com.fasterxml.jackson.databind.ser.BeanSerializerModifier { + /** + * Change properties list + * + * @param config config + * @param beanDesc bean desc + * @param beanProperties bean properties + * @return the list + * @see DefaultSerializerProvider#_serializeNull(JsonGenerator) + * @since 1.0.0 + */ + @Override + @SuppressWarnings("all") + public List changeProperties(SerializationConfig config, + BeanDescription beanDesc, + @NotNull List beanProperties) { + beanProperties.forEach(writer -> { + // 如果已经有 null 序列化处理如注解: @JsonSerialize(nullsUsing = xxx) 跳过 + if (writer.hasNullSerializer()) { + return; + } + JavaType type = writer.getType(); + Class clazz = type.getRawClass(); + if (type.isTypeOrSubTypeOf(Number.class)) { + // writer.assignNullSerializer(NullJsonSerializers.NUMBER_JSON_SERIALIZER); + } else if (type.isTypeOrSubTypeOf(Boolean.class)) { + writer.assignNullSerializer(NullJsonSerializers.BOOLEAN_JSON_SERIALIZER); + } else if (type.isTypeOrSubTypeOf(Character.class)) { + writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER); + } else if (type.isTypeOrSubTypeOf(String.class)) { + writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER); + } else if (type.isArrayType() || clazz.isArray() || type.isTypeOrSubTypeOf(Collection.class)) { + writer.assignNullSerializer(NullJsonSerializers.ARRAY_JSON_SERIALIZER); + } else if (type.isTypeOrSubTypeOf(OffsetDateTime.class)) { + writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER); + } else if (type.isTypeOrSubTypeOf(Date.class) || type.isTypeOrSubTypeOf(TemporalAccessor.class)) { + writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER); + } else { + writer.assignNullSerializer(NullJsonSerializers.OBJECT_JSON_SERIALIZER); + } + }); + return super.changeProperties(config, beanDesc, beanProperties); + } + + /** + * The interface Null json serializers. + * + * @author dong4j + * @version 1.2.3 + * @email "mailto:dongshijie@fkhwl.com" + * @date 2019.12.24 01:46 + * @since 1.0.0 + */ + @SuppressWarnings("checkstyle:InterfaceIsType") + public interface NullJsonSerializers { + + /** + * The constant STRING_JSON_SERIALIZER. + */ + JsonSerializer STRING_JSON_SERIALIZER = new JsonSerializer() { + @Override + public void serialize(Object value, @NotNull JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeString(StringPool.EMPTY); + } + }; + + /** + * The constant NUMBER_JSON_SERIALIZER. + */ + JsonSerializer NUMBER_JSON_SERIALIZER = new JsonSerializer() { + @Override + public void serialize(Object value, @NotNull JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeNumber(StringUtils.INDEX_NOT_FOUND); + } + }; + + /** + * The constant BOOLEAN_JSON_SERIALIZER. + */ + JsonSerializer BOOLEAN_JSON_SERIALIZER = new JsonSerializer() { + @Override + public void serialize(Object value, @NotNull JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeObject(Boolean.FALSE); + } + }; + + /** + * The constant ARRAY_JSON_SERIALIZER. + */ + JsonSerializer ARRAY_JSON_SERIALIZER = new JsonSerializer() { + @Override + public void serialize(Object value, @NotNull JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeStartArray(); + gen.writeEndArray(); + } + }; + + /** + * The constant OBJECT_JSON_SERIALIZER. + */ + JsonSerializer OBJECT_JSON_SERIALIZER = new JsonSerializer() { + @Override + public void serialize(Object value, @NotNull JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeStartObject(); + gen.writeEndObject(); + } + }; + + } + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/jackson/JavaTimeModule.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/jackson/JavaTimeModule.java new file mode 100644 index 0000000..d3a61e8 --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/jackson/JavaTimeModule.java @@ -0,0 +1,44 @@ +package com.aivfo.el.start.core.jackson; + +import com.aivfo.el.start.core.util.DateTimeUtils; +import com.fasterxml.jackson.core.json.PackageVersion; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; + +/** + * @author: wangyl + * @date: 2023/5/16 + * @description: java 8 时间默认序列化 + */ +public class JavaTimeModule extends SimpleModule { + + /** + * serialVersionUID + */ + private static final long serialVersionUID = -8312156928465504942L; + + /** + * Java time module + * + * @since 1.0.0 + */ + public JavaTimeModule() { + super(PackageVersion.VERSION); + this.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeUtils.DATETIME_FORMAT)); + this.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeUtils.DATE_FORMAT)); + this.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeUtils.TIME_FORMAT)); + this.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeUtils.DATETIME_FORMAT)); + this.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeUtils.DATE_FORMAT)); + this.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeUtils.TIME_FORMAT)); + } + +} diff --git a/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/jackson/MappingApiJackson2HttpMessageConverter.java b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/jackson/MappingApiJackson2HttpMessageConverter.java new file mode 100644 index 0000000..624f88d --- /dev/null +++ b/band/aivfo-element/aivfo-element-core/src/main/java/com/aivfo/el/start/core/jackson/MappingApiJackson2HttpMessageConverter.java @@ -0,0 +1,107 @@ +package com.aivfo.el.start.core.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.http.MediaType; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; + +import java.io.IOException; + +/** + * 针对 api 服务对 android 和 ios 和 web 处理的 分读写的 jackson 处理

+ * 1. app 端上报数据是 使用 readObjectMapper + * 2. 返回给 app 端的数据使用 writeObjectMapper + */ +public class MappingApiJackson2HttpMessageConverter extends AbstractReadWriteJackson2HttpMessageConverter { + + /** + * Json prefix + */ + @Nullable + private String jsonPrefix; + + /** + * Construct a new {@link MappingApiJackson2HttpMessageConverter} using default configuration + * provided by {@link Jackson2ObjectMapperBuilder}. + * + * @since 1.0.0 + */ + public MappingApiJackson2HttpMessageConverter() { + this(Jackson2ObjectMapperBuilder.json().build()); + } + + /** + * Construct a new {@link MappingApiJackson2HttpMessageConverter} with a custom {@link ObjectMapper}. + * You can use {@link Jackson2ObjectMapperBuilder} to build it easily. + * + * @param objectMapper ObjectMapper + * @see Jackson2ObjectMapperBuilder#json() Jackson2ObjectMapperBuilder#json()Jackson2ObjectMapperBuilder#json() + * @since 1.0.0 + */ + public MappingApiJackson2HttpMessageConverter(ObjectMapper objectMapper) { + super(objectMapper, + initWriteObjectMapper(objectMapper), + MediaType.APPLICATION_JSON, + new MediaType("application", "*+json")); + } + + /** + * 初始化负责序列化的 ObjectMapper, 设置 null 处理. + * 如果需要扩展, 只需要重写 setSerializerFactory 即可. + * + * @param readObjectMapper read object mapper + * @return the object mapper + * @since 1.0.0 + */ + private static @NotNull ObjectMapper initWriteObjectMapper(@NotNull ObjectMapper readObjectMapper) { + // 拷贝 readObjectMapper + ObjectMapper writeObjectMapper = readObjectMapper.copy(); + writeObjectMapper.setSerializerFactory(writeObjectMapper.getSerializerFactory() + .withSerializerModifier(new DefaultBeanSerializerModifier())); + return writeObjectMapper; + } + + /** + * Specify a custom prefix to use for this view's JSON output. + * Default is none. + * + * @param jsonPrefix jsonPrefix + * @see #setPrefixJson #setPrefixJson#setPrefixJson#setPrefixJson#setPrefixJson#setPrefixJson + * @since 1.0.0 + */ + public void setJsonPrefix(@Nullable String jsonPrefix) { + this.jsonPrefix = jsonPrefix; + } + + /** + * Indicate whether the JSON output by this view should be prefixed with ")]}', ". Default is false. + *

Prefixing the JSON string in this manner is used to help prevent JSON Hijacking. + * The prefix renders the string syntactically invalid as a script so that it cannot be hijacked. + * This prefix should be stripped before parsing the string as JSON. + * + * @param prefixJson prefixJson + * @see #setJsonPrefix #setJsonPrefix#setJsonPrefix#setJsonPrefix#setJsonPrefix#setJsonPrefix + * @since 1.0.0 + */ + public void setPrefixJson(boolean prefixJson) { + this.jsonPrefix = (prefixJson ? ")]}', " : null); + } + + /** + * Write prefix * + * + * @param generator generator + * @param object object + * @throws IOException io exception + * @since 1.0.0 + */ + @Override + protected void writePrefix(@NotNull JsonGenerator generator, @NotNull Object object) throws IOException { + if (this.jsonPrefix != null) { + generator.writeRaw(this.jsonPrefix); + } + } + +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-autoconfigure/src/main/java/com/aivfo/dfs/autoconfigure/DfsAutoConfiguration.java b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-autoconfigure/src/main/java/com/aivfo/dfs/autoconfigure/DfsAutoConfiguration.java new file mode 100644 index 0000000..884f093 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-autoconfigure/src/main/java/com/aivfo/dfs/autoconfigure/DfsAutoConfiguration.java @@ -0,0 +1,26 @@ +package com.aivfo.dfs.autoconfigure; + +import com.aivfo.dfs.client.core.Client; +import com.aivfo.dfs.client.core.fastDFS.client.FastDFSClient; +import com.aivfo.dfs.client.core.fastDFS.properties.FastDFSProperties; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + + +@Configuration +@EnableConfigurationProperties(DfsAutoProperties.class) +@ConditionalOnProperty(prefix = DfsAutoProperties.PREFIX, name = DfsAutoProperties.ENABLE, matchIfMissing = true) +public class DfsAutoConfiguration { + + @Bean + @ConditionalOnProperty(prefix = DfsAutoProperties.FASTDFS_PREFIX, name = DfsAutoProperties.ENABLE, matchIfMissing = true) + @Primary + public Client fastDFSClient(DfsAutoProperties mqttProperties) { + final FastDFSProperties fastDFS = mqttProperties.getFastdfs(); + return new FastDFSClient(fastDFS); + } + +} \ No newline at end of file diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-autoconfigure/src/main/java/com/aivfo/dfs/autoconfigure/DfsAutoProperties.java b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-autoconfigure/src/main/java/com/aivfo/dfs/autoconfigure/DfsAutoProperties.java new file mode 100644 index 0000000..fc06604 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-autoconfigure/src/main/java/com/aivfo/dfs/autoconfigure/DfsAutoProperties.java @@ -0,0 +1,41 @@ +package com.aivfo.dfs.autoconfigure; + +import com.aivfo.dfs.client.core.fastDFS.properties.FastDFSProperties; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import javax.annotation.PostConstruct; + + +/** + * @author: wangyl + * @date: 2023/4/19 + * @description: + */ +@ConfigurationProperties(prefix = DfsAutoProperties.PREFIX) +@Data +public class DfsAutoProperties { + /** + * PREFIX + */ + static final String PREFIX = "aivfo.dfs"; + + /** + * FASTDFS_PREFIX + */ + static final String FASTDFS_PREFIX = "aivfo.dfs.fastDFS"; + /** + * 开启的常量 + */ + static final String ENABLE = "enable"; + /** + * 默认开启 + */ + private boolean enable = true; + + /** + * Clients + */ + private FastDFSProperties fastdfs; + +} \ No newline at end of file diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..ea0a013 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.aivfo.dfs.autoconfigure.DfsAutoConfiguration diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/Client.java b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/Client.java new file mode 100644 index 0000000..96a245b --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/Client.java @@ -0,0 +1,99 @@ +package com.aivfo.dfs.client.core; + +import com.aivfo.dfs.client.core.callback.DownloadCallBack; +import com.csource.common.MyException; + +import java.io.IOException; +import java.util.Map; + +/** + * @author: wangyl + * @date: 2023/4/27 + * @description: 客户端接口 + */ +public interface Client { + + /** + * 通过文件路径上传文件 + * + * @param fileFullName 文件全路径 + * @return java.lang.String 文件结果路径 + * @Date 2023/4/27 + * @Author wangyl + */ + String uploadFile(String fileFullName) throws Exception; + + /** + * 通过文件路径上传文件 可配置文件元数据以及上传组信息 + * + * @param fileFullName 文件全路径 + * @param fileMetaList 文件元数据 + * @return java.lang.String 文件结果路径 + * @Date 2023/4/27 + * @Author wangyl + */ + String uploadFile(String fileFullName, Map fileMetaList) throws Exception; + + /** + * 二进制流上传文件 + * + * @param fileData 文件内容 + * @param fileName 文件名称 + * @return java.lang.String + * @Date 2023/4/28 + * @Author wangyl + */ + String uploadFile(byte[] fileData, String fileName) throws Exception; + + /** + * 二进制流上传文件,附加文件属性 + * + * @param fileData + * @param fileName + * @param fileMetaList + * @return java.lang.String + * @Date 2023/4/28 + * @Author wangyl + */ + String uploadFile(byte[] fileData, String fileName, Map fileMetaList) throws Exception; + + /** + * 下载指定的远程文件 + * + * @param remoteFile 远程文件路径 + * @return void + * @Date 2023/5/10 + * @Author wangyl + */ + void downloadFile(String remoteFile, DownloadCallBack downloadCallBack) throws Exception; + + /** + * 下载文件流 + * + * @param remoteFile 远程文件路径 + * @return void + * @Date 2023/5/10 + * @Author wangyl + */ + byte[] downloadFile(String remoteFile) throws Exception; + + /** + * 获取文件真实磁盘路径 + * + * @param uploadFileResultPath 上传文件的返回路径 + * @return java.lang.String + * @Date 2023/5/5 + * @Author wangyl + */ + String getFileReallyPath(String uploadFileResultPath) throws Exception; + + /** + * 获取远程文件元数据 + * + * @param remoteFile 远程文件 + * @return java.util.Map + * @Date 2023/5/10 + * @Author wangyl + */ + Map getFileMeta(String remoteFile) throws Exception; +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/exception/DfsFailedException.java b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/exception/DfsFailedException.java new file mode 100644 index 0000000..9d02f3d --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/exception/DfsFailedException.java @@ -0,0 +1,36 @@ +package com.aivfo.dfs.client.core.exception; + +import com.aivfo.el.starter.base.exception.BasicException; + +/** + * @author: wangyl + * @date: 2023/4/14 + * @description: Dfs异常信息 + */ +public class DfsFailedException extends BasicException { + + /** + * DEFAULT_MESSAGE + */ + private static final String DEFAULT_MESSAGE = "服务内部错误: "; + private static final long serialVersionUID = -4288134722183807545L; + + /** + * Basic exception + * + * @since 1.7.0 + */ + public DfsFailedException() { + super(); + } + + /** + * Base exception + * + * @param describe describe + * @since 1.7.0 + */ + public DfsFailedException(String describe) { + super(DEFAULT_MESSAGE + describe); + } +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/client/FastDFSClient.java b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/client/FastDFSClient.java new file mode 100644 index 0000000..a26171b --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/client/FastDFSClient.java @@ -0,0 +1,235 @@ +package com.aivfo.dfs.client.core.fastDFS.client; + +import com.aivfo.dfs.client.core.Client; +import com.aivfo.dfs.client.core.callback.DownloadCallBack; +import com.aivfo.dfs.client.core.exception.DfsFailedException; +import com.aivfo.dfs.client.core.fastDFS.callback.FileDownLoadCallBack; +import com.aivfo.dfs.client.core.fastDFS.common.FastDFSUtils; +import com.aivfo.dfs.client.core.fastDFS.constant.FastDFSCmd; +import com.aivfo.dfs.client.core.fastDFS.constant.FastDFSConstant; +import com.aivfo.dfs.client.core.fastDFS.properties.FastDFSProperties; +import com.aivfo.el.start.core.util.CollectionUtils; +import com.aivfo.el.start.core.util.FileUtils; +import com.aivfo.el.starter.base.utils.StringPool; +import com.csource.common.NameValuePair; +import com.csource.fastdfs.*; +import com.csource.fastdfs.pool.Connection; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author: wangyl + * @date: 2023/4/28 + * @description: fastDFS客户端 + */ +@Slf4j +public class FastDFSClient implements Client { + + FastDFSProperties fastDFSProperties; + + /** + * @author: wangyl + * @date: 2023/5/4 + * @description: 初始化 + */ + public FastDFSClient(FastDFSProperties fastDFSProperties) { + this.fastDFSProperties = fastDFSProperties; + Properties properties = this.fastDFSProperties.ConversionToProperties(); + try { + ClientGlobal.initByProperties(properties); + } catch (Exception e) { + log.error("fastDFS初始化失败", e); + System.exit(-1); + } + } + + /** + * 初始化相关对象 + * + * @param + * @return org.csource.fastdfs.StorageClient + * @Date 2023/4/28 + * @Author wangyl + * @see StorageClient#releaseConnection(Connection, boolean) + * 对象都是非线程安全的 所以每次调用的时候都需要初始化 storageServer 这个对象源码在每次操作后都会被设置为null + */ + private StorageClient getStorageClient() throws IOException { + TrackerClient tracker = new TrackerClient(); + TrackerServer trackerServer = tracker.getTrackerServer(); + StorageServer storageServer = null; + StorageClient storageClient = new StorageClient(trackerServer, storageServer); + return storageClient; + } + + @Override + public String uploadFile(String fileFullName) throws Exception { + return this.uploadFileByName(fileFullName, new HashMap<>()); + } + + @Override + public String uploadFile(String fileFullName, Map fileMetaMap) throws Exception { + return this.uploadFileByName(fileFullName, fileMetaMap); + } + + @Override + public String uploadFile(byte[] fileData, String fileName) throws Exception { + return this.uploadFileByByte(fileData, fileName, new HashMap<>()); + } + + @Override + public String uploadFile(byte[] fileData, String fileName, Map fileMetaMap) throws Exception { + return this.uploadFileByByte(fileData, fileName, fileMetaMap); + } + + /** + * 下载指定的远程文件 + * + * @param remoteFile + * @param downloadCallBack + * @return void + * @Date 2023/5/10 + * @Author wangyl + */ + @Override + public void downloadFile(String remoteFile, DownloadCallBack downloadCallBack) throws Exception { + this.downloadFileByCallBack(remoteFile, downloadCallBack); + } + + /** + * 下载文件流 + * + * @param remoteFile 远程文件路径 + * @return void + * @Date 2023/5/10 + * @Author wangyl + */ + @Override + public byte[] downloadFile(String remoteFile) throws Exception { + return this.downloadFileByByte(remoteFile); + } + + /** + * 获取文件真实磁盘路径 + * 解析原理: + * + * @param uploadFileResultPath 上传文件的返回路径 例如:group1/M00/00/00/rBfCtGRSpMOAMnasABPRK3dgG_Q507.jpg + * @return java.lang.String + * @Date 2023/5/5 + * @Author wangyl + */ + @Override + public String getFileReallyPath(String uploadFileResultPath) { + FastDFSUtils.FastDFSPathInfo fastDFSPathInfo = FastDFSUtils.spiltFileResultPath(uploadFileResultPath); + Map> storagePath = fastDFSProperties.getStoragePath(); + List realPathPrefixList = storagePath.get(fastDFSPathInfo.getGroupName()); + Integer storagePathIndex = Integer.valueOf(fastDFSPathInfo.getStoragePath().substring(1)); + if (CollectionUtils.isEmpty(realPathPrefixList) || realPathPrefixList.size() < storagePathIndex) { + throw new DfsFailedException("组【" + fastDFSPathInfo.getGroupName() + "】没有配置storagePath参数,或者配置storagePath不正确 请检查配置"); + } + + String realPathPrefix = realPathPrefixList.get(storagePathIndex); + String realPath = realPathPrefix + StringPool.SLASH + FastDFSConstant.STORAGE_PATH_DATA + StringPool.SLASH + fastDFSPathInfo.getDirFirst() + StringPool.SLASH + fastDFSPathInfo.getDirSecond() + StringPool.SLASH + fastDFSPathInfo.getFileName(); + return realPath; + } + + /** + * 获取远程文件元数据 + * + * @param remoteFile 远程文件 + * @return java.util.Map + * @Date 2023/5/10 + * @Author wangyl + */ + @Override + public Map getFileMeta(String remoteFile) throws Exception { + StorageClient storageClient = this.getStorageClient(); + FastDFSUtils.FastDFSPathInfo fastDFSPathInfo = FastDFSUtils.spiltFileResultPath(remoteFile); + NameValuePair[] metadata = storageClient.get_metadata(fastDFSPathInfo.getGroupName(), fastDFSPathInfo.toFileId()); + Map collect = Arrays.stream(metadata).collect(Collectors.toMap(NameValuePair::getName, NameValuePair::getValue)); + return collect; + } + + /** + * 通过完整的文件流上传 + * + * @param fileData 文件数据 + * @param fileName 文件名称 + * @param fileMetaMap 文件元数据 + * @return java.lang.String + * @Date 2023/5/4 + * @Author wangyl + */ + private String uploadFileByByte(byte[] fileData, String fileName, Map fileMetaMap) throws Exception { + StorageClient storageClient = getStorageClient(); + FileUtils.FileNameInfo fileNameInfo = FileUtils.splitFileExt(fileName); + int fileSize = fileData.length; + List nameValuePairs = FastDFSUtils.defaultNameValuePair(fileNameInfo); + nameValuePairs.addAll(FastDFSUtils.mapToNameValuePair(fileMetaMap)); + String[] uploadResult = storageClient.do_upload_file(FastDFSCmd.STORAGE_PROTO_CMD_UPLOAD_FILE, null, null, fileNameInfo.getMainName(), + fileNameInfo.getExtName(), fileSize, new StorageClient.UploadBuff(fileData, 0, fileSize), nameValuePairs.toArray(new NameValuePair[0])); + return FastDFSUtils.uploadFileResultPath(uploadResult); + } + + /** + * 按照本地文件上传 + * + * @param fileFullName 文件全路径 + * @param fileMetaMap 文件元数据 + * @return java.lang.String + * @Date 2023/5/4 + * @Author wangyl + */ + private String uploadFileByName(String fileFullName, Map fileMetaMap) throws Exception { + File file = new File(fileFullName); + FileUtils.FileNameInfo fileNameInfo = FileUtils.splitFileExt(fileFullName); + List nameValuePairs = FastDFSUtils.defaultNameValuePair(fileNameInfo); + nameValuePairs.addAll(FastDFSUtils.mapToNameValuePair(fileMetaMap)); + FileInputStream fis = new FileInputStream(file); + StorageClient storageClient = getStorageClient(); + String[] uploadResult = storageClient.do_upload_file(FastDFSCmd.STORAGE_PROTO_CMD_UPLOAD_FILE, null, null, fileNameInfo.getMainName(), + fileNameInfo.getExtName(), file.length(), new UploadStream(fis, file.length()), nameValuePairs.toArray(new NameValuePair[0])); + return FastDFSUtils.uploadFileResultPath(uploadResult); + } + + /** + * 文件下载 结果发送回调函数 + * + * @param uploadFileResultPath + * @param downloadCallback + * @return void + * @Date 2023/5/10 + * @Author wangyl + */ + private void downloadFileByCallBack(String uploadFileResultPath, DownloadCallBack downloadCallback) throws Exception { + StorageClient storageClient = this.getStorageClient(); + FastDFSUtils.FastDFSPathInfo fastDFSPathInfo = FastDFSUtils.spiltFileResultPath(uploadFileResultPath); + FileDownLoadCallBack fileDownLoadCallBack = new FileDownLoadCallBack(downloadCallback); + int result = storageClient.download_file(fastDFSPathInfo.getGroupName(), fastDFSPathInfo.toFileId(), 0L, 0L, fileDownLoadCallBack); + if (result != 0) { + throw new DfsFailedException(); + } + } + + /** + * 文件下载 返回字节流 + * + * @param uploadFileResultPath + * @return byte[] + * @Date 2023/5/10 + * @Author wangyl + */ + private byte[] downloadFileByByte(String uploadFileResultPath) throws Exception { + StorageClient storageClient = this.getStorageClient(); + FastDFSUtils.FastDFSPathInfo fastDFSPathInfo = FastDFSUtils.spiltFileResultPath(uploadFileResultPath); + byte[] data = storageClient.download_file(fastDFSPathInfo.getGroupName(), fastDFSPathInfo.toFileId(), 0L, 0L); + if (Objects.isNull(data)) { + throw new DfsFailedException(); + } + return data; + } +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/common/FastDFSUtils.java b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/common/FastDFSUtils.java new file mode 100644 index 0000000..3fc5483 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/common/FastDFSUtils.java @@ -0,0 +1,137 @@ +package com.aivfo.dfs.client.core.fastDFS.common; + +import com.aivfo.dfs.client.core.exception.DfsFailedException; +import com.aivfo.dfs.client.core.fastDFS.constant.FastDFSConstant; +import com.aivfo.el.start.core.util.FileUtils; +import com.aivfo.el.starter.base.utils.StringPool; +import com.csource.common.NameValuePair; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author: wangyl + * @date: 2023/5/4 + * @description: DFS文件工具 + */ +public class FastDFSUtils { + final static Integer RESULT_SIZE = 2; + + /** + * 获取文件上传结果 + * + * @param uploadFileResult + * @return java.lang.String + * @Date 2023/5/4 + * @Author wangyl + */ + public static String uploadFileResultPath(String[] uploadFileResult) { + if (Objects.isNull(uploadFileResult) || uploadFileResult.length < RESULT_SIZE) { + throw new DfsFailedException("上传结果异常"); + } + return uploadFileResult[0] + "/" + uploadFileResult[1]; + } + + /** + * map 转fastDFS的文件元数据 + * + * @param fileMetaMap + * @return com.csource.common.NameValuePair[] + * @Date 2023/5/4 + * @Author wangyl + */ + public static List mapToNameValuePair(Map fileMetaMap) { + List nameValuePairs = fileMetaMap.entrySet().stream().map((key) -> { + NameValuePair nameValuePair = new NameValuePair(key.getKey(), key.getValue()); + return nameValuePair; + }).collect(Collectors.toList()); + return nameValuePairs; + } + + /** + * 文件默认原属性生成 + * + * @param fileNameInfo + * @return java.util.List + * @Date 2023/5/10 + * @Author wangyl + */ + public static List defaultNameValuePair(FileUtils.FileNameInfo fileNameInfo) { + final List defaultNameValuePairList = new ArrayList<>(); + NameValuePair mainName = new NameValuePair(FastDFSConstant.FILE_NAME, fileNameInfo.getMainName()); + NameValuePair extName = new NameValuePair(FastDFSConstant.FILE_EXT_NAME, fileNameInfo.getExtName()); + defaultNameValuePairList.add(mainName); + defaultNameValuePairList.add(extName); + return defaultNameValuePairList; + } + + /** + * 上传结果切割 + * + * @param uploadFileResultPath + * @return com.aivfo.dfs.client.core.fastDFS.common.FastDFSUtils.FastDFSPathInfo + * @Date 2023/5/10 + * @Author wangyl + */ + public static FastDFSPathInfo spiltFileResultPath(String uploadFileResultPath) { + if (uploadFileResultPath.startsWith(StringPool.SLASH)) { + uploadFileResultPath = uploadFileResultPath.substring(1); + } + String[] split = uploadFileResultPath.split(StringPool.SLASH); + if (split.length < FastDFSConstant.UPLOAD_FILE_RESULT_SPILT_PATH || !split[1].startsWith(FastDFSConstant.STORAGE_PATH_START)) { + throw new DfsFailedException("uploadFileResultPath 输入错误。请输入例如:group1/M00/00/00/rBfCtGRSpMOAMnasABPRK3dgG_Q507.jpg 的路径"); + } + FastDFSPathInfo fastDFSPathInfo = new FastDFSPathInfo(); + fastDFSPathInfo.setGroupName(split[0]); + fastDFSPathInfo.setStoragePath(split[1]); + fastDFSPathInfo.setDirFirst(split[2]); + fastDFSPathInfo.setDirSecond(split[3]); + fastDFSPathInfo.setFileName(split[4]); + return fastDFSPathInfo; + } + + /** + * @author: wangyl + * @date: 2023/5/10 + * @description: fastDFS路径信息 + */ + @Data + public static class FastDFSPathInfo { + /** + * 分组名字 + */ + String groupName; + /** + * 存储路径信息 + */ + String storagePath; + /** + * 第一个文件夹 + */ + String dirFirst; + /** + * 第二个文件夹 + */ + String dirSecond; + /** + * 文件名字 + */ + String fileName; + + /** + * 返回文件id + * + * @param + * @return java.lang.String + * @Date 2023/5/10 + * @Author wangyl + */ + public String toFileId() { + return storagePath + StringPool.SLASH + dirFirst + StringPool.SLASH + dirSecond + StringPool.SLASH + fileName; + } + } +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/constant/FastDFSCmd.java b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/constant/FastDFSCmd.java new file mode 100644 index 0000000..e989aeb --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/constant/FastDFSCmd.java @@ -0,0 +1,13 @@ +package com.aivfo.dfs.client.core.fastDFS.constant; + +/** + * @author: wangyl + * @date: 2023/5/4 + * @description: 客户端和服务端交互的命令 + */ +public class FastDFSCmd { + /** + * 上传文件 + */ + public static final byte STORAGE_PROTO_CMD_UPLOAD_FILE = 11; +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/constant/FastDFSConstant.java b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/constant/FastDFSConstant.java new file mode 100644 index 0000000..9318997 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/constant/FastDFSConstant.java @@ -0,0 +1,73 @@ +package com.aivfo.dfs.client.core.fastDFS.constant; + +/** + * @author: wangyl + * @date: 2023/5/4 + * @description: fastDFS 常量数据 + */ +public class FastDFSConstant { + /** + * 连接超时时间 + */ + 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"; + /** + * http token + */ + public static final String PROP_KEY_HTTP_ANTI_STEAL_TOKEN = "fastdfs.http_anti_steal_token"; + /** + * http密钥 + */ + public static final String PROP_KEY_HTTP_SECRET_KEY = "fastdfs.http_secret_key"; + /** + * http track 端口 + */ + public static final String PROP_KEY_HTTP_TRACKER_HTTP_PORT = "fastdfs.http_tracker_http_port"; + /** + * tracker服务列表,用“,”分割 + */ + public static final String PROP_KEY_TRACKER_SERVERS = "fastdfs.tracker_servers"; + /** + * 是否启动连接池 + */ + public static final String PROP_KEY_CONNECTION_POOL_ENABLED = "fastdfs.connection_pool.enabled"; + /** + * 连接池最大连接数量 + */ + public static final String PROP_KEY_CONNECTION_POOL_MAX_COUNT_PER_ENTRY = "fastdfs.connection_pool.max_count_per_entry"; + /** + * 连接池最大连接时间 + */ + public static final String PROP_KEY_CONNECTION_POOL_MAX_IDLE_TIME = "fastdfs.connection_pool.max_idle_time"; + /** + * 连接池最大等待时间 + */ + public static final String PROP_KEY_CONNECTION_POOL_MAX_WAIT_TIME_IN_MS = "fastdfs.connection_pool.max_wait_time_in_ms"; + /** + * 文件名字 + */ + public static final String FILE_NAME = "fileName"; + /** + * 文件后缀 + */ + public static final String FILE_EXT_NAME = "fileExtName"; + /** + * StoragePath的开头 + */ + public final static String STORAGE_PATH_START = "M"; + /** + * StoragePath的Data + */ + public final static String STORAGE_PATH_DATA = "data"; + /** + * 上传路径结果分割后长度 + */ + public final static int UPLOAD_FILE_RESULT_SPILT_PATH = 5; +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/properties/FastDFSProperties.java b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/properties/FastDFSProperties.java new file mode 100644 index 0000000..597c967 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-core/src/main/java/com/aivfo/dfs/client/core/fastDFS/properties/FastDFSProperties.java @@ -0,0 +1,115 @@ +package com.aivfo.dfs.client.core.fastDFS.properties; + +import com.aivfo.dfs.client.core.fastDFS.constant.FastDFSConstant; +import com.csource.fastdfs.ClientGlobal; +import lombok.Data; +import org.checkerframework.checker.units.qual.C; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + + +/** + * @author: wangyl + * @date: 2023/4/27 + * @description: FastDFS配置 + */ +@Data +public class FastDFSProperties { + /** + * 默认开启 + */ + private boolean enable = true; + + /** + * 连接超时时间 + */ + private String connectTimeoutInSeconds = ""; + /** + * 传输超时时间 + */ + private String networkTimeoutInSeconds = ""; + /** + * 字符集编码 + */ + private String charset = ""; + /** + * HTTP防盗链令牌 + */ + private String httpAntiStealToken = ""; + /** + * HTTP加密密钥 + */ + private String httpSecretKey = ""; + /** + * HTTP跟踪器端口 + */ + private String httpTrackerHttpPort = ""; + /** + * tracker服务器 多个用,分割 + */ + private String trackerServers; + /** + * 连接池信息 + */ + private ConnectionPool connectionPool = new ConnectionPool(); + + /** + * storage Path + */ + private Map> storagePath = new HashMap<>(); + + /** + * @author: wangyl + * @date: 2023/4/28 + * @description: 连接池配置 + */ + @Data + public static class ConnectionPool { + /** + * 是否启用连接池 默认启用 + */ + private Boolean enabled = true; + /** + * 最大数量 + */ + private String maxCountPerEntry = ""; + /** + * 最大闲置时间 + */ + private String maxIdleTime = ""; + /** + * 最大等待时间(毫秒) + */ + private String maxWaitTimeInMs = ""; + + } + + /** + * 转换成Properties对象初始化 + * + * @param + * @return java.util.Properties + * @Date 2023/4/28 + * @Author wangyl + */ + public Properties ConversionToProperties() { + Properties properties = new Properties(); + properties.setProperty(FastDFSConstant.PROP_KEY_CONNECT_TIMEOUT_IN_SECONDS, this.getConnectTimeoutInSeconds()); + properties.setProperty(FastDFSConstant.PROP_KEY_NETWORK_TIMEOUT_IN_SECONDS, this.getNetworkTimeoutInSeconds()); + properties.setProperty(FastDFSConstant.PROP_KEY_CHARSET, this.getCharset()); + properties.setProperty(FastDFSConstant.PROP_KEY_HTTP_ANTI_STEAL_TOKEN, this.getHttpAntiStealToken()); + properties.setProperty(FastDFSConstant.PROP_KEY_HTTP_SECRET_KEY, this.getHttpSecretKey()); + properties.setProperty(FastDFSConstant.PROP_KEY_HTTP_TRACKER_HTTP_PORT, this.getHttpTrackerHttpPort()); + properties.setProperty(FastDFSConstant.PROP_KEY_TRACKER_SERVERS, this.getTrackerServers()); + properties.setProperty(FastDFSConstant.PROP_KEY_CONNECTION_POOL_ENABLED, this.getConnectionPool().getEnabled().toString()); + properties.setProperty(FastDFSConstant.PROP_KEY_CONNECTION_POOL_MAX_COUNT_PER_ENTRY, this.getConnectionPool().getMaxCountPerEntry()); + properties.setProperty(FastDFSConstant.PROP_KEY_CONNECTION_POOL_MAX_IDLE_TIME, this.getConnectionPool().getMaxIdleTime()); + properties.setProperty(FastDFSConstant.PROP_KEY_CONNECTION_POOL_MAX_WAIT_TIME_IN_MS, this.getConnectionPool().getMaxWaitTimeInMs()); + return properties; + } + + +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-starter/src/test/java/com/aivfo/dfs/DfsTestApplication.java b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-starter/src/test/java/com/aivfo/dfs/DfsTestApplication.java new file mode 100644 index 0000000..3601b9d --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-starter/src/test/java/com/aivfo/dfs/DfsTestApplication.java @@ -0,0 +1,64 @@ +package com.aivfo.dfs; + +import com.aivfo.dfs.client.core.Client; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.PropertySource; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Map; + +@SpringBootTest(classes = DfsTestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.NONE) +@PropertySource(value = "classpath:application.properties") +@ComponentScan(basePackages = "com.aivfo.dfs") +public class DfsTestApplication { + @Autowired + Client client; + + @Test + public void uploadFile() throws Exception { + String s = client.uploadFile("C:\\Users\\95981\\Desktop\\wangyongliang.jpg"); + System.out.println(s); + } + + @Test + public void getFileReallyPath() throws Exception { + String s = client.getFileReallyPath("group1/M01/00/00/wKgfWWRbZ-aAVncdABPRK3dgG_Q052.jpg"); + System.out.println(s); + } + + @Test + public void downloadFile() throws Exception { + String a = "group1/M01/00/00/wKgfWWRbZ-aAVncdABPRK3dgG_Q052.jpg"; + FileOutputStream out = new FileOutputStream("C:\\Users\\95981\\Desktop\\03.jpg"); + client.downloadFile(a, (date, dateSize) -> { + try { + out.write(date, 0, dateSize); + } catch (IOException e) { + e.printStackTrace(); + return -1; + } + return 0; + }); + out.close(); + } + + @Test + public void downloadFile1() throws Exception { + String a = "group1/M01/00/00/wKgfWWRbZ-aAVncdABPRK3dgG_Q052.jpg"; + FileOutputStream out = new FileOutputStream("C:\\Users\\95981\\Desktop\\04.jpg"); + byte[] bytes = client.downloadFile(a); + out.write(bytes); + out.close(); + } + + @Test + public void getFileMeta() throws Exception { + String a = "group1/M02/00/00/wKgfWWRbleaAWfVEABPRK3dgG_Q188.jpg"; + Map fileMeta = client.getFileMeta(a); + System.out.println(fileMeta); + } +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-starter/src/test/resources/application.properties b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-starter/src/test/resources/application.properties new file mode 100644 index 0000000..1b1ebca --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-dfs-client-spring-boot-starter/src/test/resources/application.properties @@ -0,0 +1,8 @@ +aivfo.dfs.fastdfs.enable=true +aivfo.dfs.fastdfs.trackerServers=192.168.31.89:22122,192.168.31.89:22123 +aivfo.dfs.fastdfs.storagePath.group1[0]=/mnt/data01/image/group1/storaged01 +aivfo.dfs.fastdfs.storagePath.group1[1]=/mnt/data01/image/group1/storaged02 +aivfo.dfs.fastdfs.storagePath.group1[2]=/mnt/data01/image/group1/storaged03 +aivfo.dfs.fastdfs.storagePath.group2[0]=/mnt/data01/image/group1/storaged01 +aivfo.dfs.fastdfs.storagePath.group2[1]=/mnt/data01/image/group1/storaged02 +aivfo.dfs.fastdfs.storagePath.group2[2]=/mnt/data01/image/group1/storaged03 \ No newline at end of file diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/common/Base64.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/common/Base64.java new file mode 100644 index 0000000..b634c15 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/common/Base64.java @@ -0,0 +1,491 @@ +package com.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/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/common/IniFileReader.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/common/IniFileReader.java new file mode 100644 index 0000000..9136c2c --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/common/IniFileReader.java @@ -0,0 +1,215 @@ +/** + * 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 https://github.com/happyfish100/fastdfs for more detail. + **/ + +package com.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(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + + @SuppressWarnings("unchecked") + 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(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/common/MyException.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/common/MyException.java new file mode 100644 index 0000000..d6253a3 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/common/MyException.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 https://github.com/happyfish100/fastdfs for more detail. + */ + +package com.csource.common; + +/** + * My Exception + * + * @author Happy Fish / YuQing + * @version Version 1.0 + */ +public class MyException extends Exception { + public MyException(String s, Exception e) { + } + + public MyException(String message) { + super(message); + } +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/common/NameValuePair.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/common/NameValuePair.java new file mode 100644 index 0000000..9513846 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/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 https://github.com/happyfish100/fastdfs for more detail. + */ + +package com.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/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/ClientGlobal.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/ClientGlobal.java new file mode 100644 index 0000000..34ff295 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/ClientGlobal.java @@ -0,0 +1,357 @@ +/** + * 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 https://github.com/happyfish100/fastdfs for more detail. + **/ + +package com.csource.fastdfs; + +import com.csource.common.IniFileReader; +import com.csource.common.MyException; + +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 String PROP_KEY_CONNECTION_POOL_ENABLED = "fastdfs.connection_pool.enabled"; + public static final String PROP_KEY_CONNECTION_POOL_MAX_COUNT_PER_ENTRY = "fastdfs.connection_pool.max_count_per_entry"; + public static final String PROP_KEY_CONNECTION_POOL_MAX_IDLE_TIME = "fastdfs.connection_pool.max_idle_time"; + public static final String PROP_KEY_CONNECTION_POOL_MAX_WAIT_TIME_IN_MS = "fastdfs.connection_pool.max_wait_time_in_ms"; + + 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 final boolean DEFAULT_CONNECTION_POOL_ENABLED = true; + public static final int DEFAULT_CONNECTION_POOL_MAX_COUNT_PER_ENTRY = 100; + public static final int DEFAULT_CONNECTION_POOL_MAX_IDLE_TIME = 3600;//second + public static final int DEFAULT_CONNECTION_POOL_MAX_WAIT_TIME_IN_MS = 1000;//millisecond + + 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 boolean g_connection_pool_enabled = DEFAULT_CONNECTION_POOL_ENABLED; + public static int g_connection_pool_max_count_per_entry = DEFAULT_CONNECTION_POOL_MAX_COUNT_PER_ENTRY; + public static int g_connection_pool_max_idle_time = DEFAULT_CONNECTION_POOL_MAX_IDLE_TIME * 1000; //millisecond + public static int g_connection_pool_max_wait_time_in_ms = DEFAULT_CONNECTION_POOL_MAX_WAIT_TIME_IN_MS; //millisecond + + 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, MyException { + 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 MyException("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 MyException("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"); + } + g_connection_pool_enabled = iniReader.getBoolValue("connection_pool.enabled", DEFAULT_CONNECTION_POOL_ENABLED); + g_connection_pool_max_count_per_entry = iniReader.getIntValue("connection_pool.max_count_per_entry", DEFAULT_CONNECTION_POOL_MAX_COUNT_PER_ENTRY); + g_connection_pool_max_idle_time = iniReader.getIntValue("connection_pool.max_idle_time", DEFAULT_CONNECTION_POOL_MAX_IDLE_TIME); + if (g_connection_pool_max_idle_time < 0) { + g_connection_pool_max_idle_time = DEFAULT_CONNECTION_POOL_MAX_IDLE_TIME; + } + g_connection_pool_max_idle_time *= 1000; + g_connection_pool_max_wait_time_in_ms = iniReader.getIntValue("connection_pool.max_wait_time_in_ms", DEFAULT_CONNECTION_POOL_MAX_WAIT_TIME_IN_MS); + if (g_connection_pool_max_wait_time_in_ms < 0) { + g_connection_pool_max_wait_time_in_ms = DEFAULT_CONNECTION_POOL_MAX_WAIT_TIME_IN_MS; + } + } + + /** + * 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, MyException { + 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, MyException { + String trackerServersConf = props.getProperty(PROP_KEY_TRACKER_SERVERS); + if (trackerServersConf == null || trackerServersConf.trim().length() == 0) { + throw new MyException(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); + String poolEnabled = props.getProperty(PROP_KEY_CONNECTION_POOL_ENABLED); + String poolMaxCountPerEntry = props.getProperty(PROP_KEY_CONNECTION_POOL_MAX_COUNT_PER_ENTRY); + String poolMaxIdleTime = props.getProperty(PROP_KEY_CONNECTION_POOL_MAX_IDLE_TIME); + String poolMaxWaitTimeInMS = props.getProperty(PROP_KEY_CONNECTION_POOL_MAX_WAIT_TIME_IN_MS); + 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); + } + if (poolEnabled != null && poolEnabled.trim().length() != 0) { + g_connection_pool_enabled = Boolean.parseBoolean(poolEnabled); + } + if (poolMaxCountPerEntry != null && poolMaxCountPerEntry.trim().length() != 0) { + g_connection_pool_max_count_per_entry = Integer.parseInt(poolMaxCountPerEntry); + } + if (poolMaxIdleTime != null && poolMaxIdleTime.trim().length() != 0) { + g_connection_pool_max_idle_time = Integer.parseInt(poolMaxIdleTime) * 1000; + } + if (poolMaxWaitTimeInMS != null && poolMaxWaitTimeInMS.trim().length() != 0) { + g_connection_pool_max_wait_time_in_ms = Integer.parseInt(poolMaxWaitTimeInMS); + } + } + + /** + * 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, MyException { + 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, MyException { + 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.setReuseAddress(true); + 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 boolean isG_connection_pool_enabled() { + return g_connection_pool_enabled; + } + + 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 += ","; + String address = inetSocketAddress.toString(); + trackerServers += address.startsWith("/") ? address.substring(1) : address; + } + } + 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 g_connection_pool_enabled = " + g_connection_pool_enabled + + "\n g_connection_pool_max_count_per_entry = " + g_connection_pool_max_count_per_entry + + "\n g_connection_pool_max_idle_time(ms) = " + g_connection_pool_max_idle_time + + "\n g_connection_pool_max_wait_time_in_ms(ms) = " + g_connection_pool_max_wait_time_in_ms + + "\n trackerServers = " + trackerServers + + "\n}"; + } + +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/DownloadCallback.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/DownloadCallback.java new file mode 100644 index 0000000..4262b07 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/DownloadCallback.java @@ -0,0 +1,29 @@ +/** + * 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 https://github.com/happyfish100/fastdfs for more detail. + */ + +package com.csource.fastdfs; + +import java.io.IOException; + +/** + * 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 + */ + int recv(long file_size, byte[] data, int bytes) throws IOException; +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/DownloadStream.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/DownloadStream.java new file mode 100644 index 0000000..62c4a0f --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/DownloadStream.java @@ -0,0 +1,45 @@ +package com.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 + */ + @Override + 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/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/FileInfo.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/FileInfo.java new file mode 100644 index 0000000..851600b --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/FileInfo.java @@ -0,0 +1,175 @@ +/** + * 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 https://github.com/happyfish100/fastdfs for more detail. + */ + +package com.csource.fastdfs; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * Server Info + * + * @author Happy Fish / YuQing + * @version Version 1.23 + */ +public class FileInfo { + public static final short FILE_TYPE_NORMAL = 1; + public static final short FILE_TYPE_APPENDER = 2; + public static final short FILE_TYPE_SLAVE = 4; + + protected boolean fetch_from_server; + protected short file_type; + protected String source_ip_addr; + protected long file_size; + protected Date create_timestamp; + protected int crc32; + + /** + * Constructor + * + * @param fetch_from_server if fetch from server flag + * @param file_type the file type + * @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(boolean fetch_from_server, short file_type, long file_size, + int create_timestamp, int crc32, String source_ip_addr) { + this.fetch_from_server = fetch_from_server; + this.file_type = file_type; + 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 fetch_from_server flag + * + * @return the fetch_from_server flag + */ + public boolean getFetchFromServer() { + return this.fetch_from_server; + } + + /** + * set the fetch_from_server flag + * + * @param fetch_from_server the fetch from server flag + */ + public void setFetchFromServer(boolean fetch_from_server) { + this.fetch_from_server = fetch_from_server; + } + + /** + * get the file type + * + * @return the file type + */ + public short getFileType() { + return this.file_type; + } + + /** + * set the file type + * + * @param file_type the file type + */ + public void setFileType(short file_type) { + this.file_type = file_type; + } + + /** + * 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 + */ + @Override + public String toString() { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + return "fetch_from_server = " + this.fetch_from_server + ", " + + "file_type = " + this.file_type + ", " + + "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/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/ProtoCommon.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/ProtoCommon.java new file mode 100644 index 0000000..2980c77 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/ProtoCommon.java @@ -0,0 +1,506 @@ +/** + * 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 https://github.com/happyfish100/fastdfs for more detail. + **/ + +package com.csource.fastdfs; + + +import com.csource.common.MyException; +import com.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_REGENERATE_APPENDER_FILENAME = 38; //rename appender file to normal 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, MyException { + 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 MyException { + String true_ext_name; + int dotIndex; + + if (master_filename.length() < 28 + FDFS_FILE_EXT_NAME_MAX_LEN) { + throw new MyException("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 MyException("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/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/ProtoStructDecoder.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/ProtoStructDecoder.java new file mode 100644 index 0000000..c773b7a --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/ProtoStructDecoder.java @@ -0,0 +1,49 @@ +/** + * 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 https://github.com/happyfish100/fastdfs for more detail. + */ + +package com.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 + */ + @SuppressWarnings("unchecked") + 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/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/ServerInfo.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/ServerInfo.java new file mode 100644 index 0000000..49e08b7 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/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 https://github.com/happyfish100/fastdfs for more detail. + */ + +package com.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/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/StorageClient.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/StorageClient.java new file mode 100644 index 0000000..769d292 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/StorageClient.java @@ -0,0 +1,1807 @@ +/** + * 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 https://github.com/happyfish100/fastdfs for more detail. + */ + +package com.csource.fastdfs; + +import com.csource.common.Base64; +import com.csource.common.MyException; +import com.csource.common.NameValuePair; +import com.csource.fastdfs.pool.Connection; + +import java.io.*; +import java.util.Arrays; + +/** + * Storage client for 2 fields file id: group name and filename + * Note: the instance of this class is NOT thread safe !!! + * if not necessary, do NOT set storage server instance + * + * @author Happy Fish / YuQing + * @version Version 1.27 + */ +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; + } + + /** + * constructor with tracker server + * + * @param trackerServer the tracker server, can be null + */ + public StorageClient(TrackerServer trackerServer) { + this.trackerServer = trackerServer; + this.storageServer = null; + } + + /** + * constructor with tracker server and storage server + * NOTE: if not necessary, do NOT set storage server instance + * + * @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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + if ((group_name == null || group_name.length() == 0) || + (master_filename == null || master_filename.length() == 0) || + (prefix_name == null)) { + throw new MyException("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, MyException { + if ((group_name == null || group_name.length() == 0) || + (master_filename == null || master_filename.length() == 0) || + (prefix_name == null)) { + throw new MyException("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, MyException { + if ((group_name == null || group_name.length() == 0) || + (master_filename == null || master_filename.length() == 0) || + (prefix_name == null)) { + throw new MyException("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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + return this.do_modify_file(group_name, appender_filename, file_offset, + modify_size, callback); + } + + /** + * regenerate filename for appender file + * + * @param group_name the group name of appender file + * @param appender_filename the appender filename + * @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[] regenerate_appender_filename(String group_name, String appender_filename) throws IOException, MyException { + byte[] header; + boolean bNewStorageServer; + Connection connection = null; + byte[] hexLenBytes; + byte[] appenderFilenameBytes; + int offset; + long body_len; + + if ((group_name == null || group_name.length() == 0) || + (appender_filename == null || appender_filename.length() == 0)) { + this.errno = ProtoCommon.ERR_NO_EINVAL; + return null; + } + + bNewStorageServer = this.newUpdatableStorageConnection(group_name, appender_filename); + + try { + connection = this.storageServer.getConnection(); + + appenderFilenameBytes = appender_filename.getBytes(ClientGlobal.g_charset); + body_len = appenderFilenameBytes.length; + + header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_REGENERATE_APPENDER_FILENAME, body_len, (byte) 0); + byte[] wholePkg = new byte[(int) (header.length + body_len)]; + System.arraycopy(header, 0, wholePkg, 0, header.length); + offset = header.length; + + System.arraycopy(appenderFilenameBytes, 0, wholePkg, offset, appenderFilenameBytes.length); + offset += appenderFilenameBytes.length; + + OutputStream out = connection.getOutputStream(); + out.write(wholePkg); + + ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), + ProtoCommon.STORAGE_PROTO_CMD_RESP, -1); + this.errno = pkgInfo.errno; + if (pkgInfo.errno != 0) { + return null; + } + + if (pkgInfo.body.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) { + throw new MyException("body length: " + pkgInfo.body.length + " <= " + ProtoCommon.FDFS_GROUP_NAME_MAX_LEN); + } + + String new_group_name = new String(pkgInfo.body, 0, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN).trim(); + String remote_filename = new String(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN, + pkgInfo.body.length - ProtoCommon.FDFS_GROUP_NAME_MAX_LEN); + String[] results = new String[2]; + results[0] = new_group_name; + results[1] = remote_filename; + + return results; + } catch (IOException ex) { + try { + connection.close(); + } catch (IOException ex1) { + ex1.printStackTrace(); + } finally { + connection = null; + } + throw ex; + } finally { + releaseConnection(connection, bNewStorageServer); + } + } + + /** + * 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:
+ *
  • results[0]: the group name to store the file
+ *
  • results[1]: the new created filename
+ * return null if fail + */ + public String[] do_upload_file(byte cmd, String group_name, String master_filename, + String prefix_name, String file_ext_name, long file_size, UploadCallback callback, + NameValuePair[] meta_list) throws IOException, MyException { + byte[] header; + byte[] ext_name_bs; + String new_group_name; + String remote_filename; + boolean bNewStorageServer; + Connection connection = null; + byte[] sizeBytes; + byte[] hexLenBytes; + byte[] masterFilenameBytes; + boolean bUploadSlave; + int offset; + long body_len; + + bUploadSlave = ((group_name != null && group_name.length() > 0) && + (master_filename != null && master_filename.length() > 0) && + (prefix_name != null)); + if (bUploadSlave) { + bNewStorageServer = this.newUpdatableStorageConnection(group_name, master_filename); + } else { + bNewStorageServer = this.newWritableStorageConnection(group_name); + } + + try { + connection = this.storageServer.getConnection(); + ext_name_bs = new byte[ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN]; + Arrays.fill(ext_name_bs, (byte) 0); + if (file_ext_name != null && file_ext_name.length() > 0) { + byte[] bs = file_ext_name.getBytes(ClientGlobal.g_charset); + int ext_name_len = bs.length; + if (ext_name_len > ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN) { + ext_name_len = ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN; + } + System.arraycopy(bs, 0, ext_name_bs, 0, ext_name_len); + } + + if (bUploadSlave) { + masterFilenameBytes = master_filename.getBytes(ClientGlobal.g_charset); + + sizeBytes = new byte[2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE]; + body_len = sizeBytes.length + ProtoCommon.FDFS_FILE_PREFIX_MAX_LEN + ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + + masterFilenameBytes.length + file_size; + + hexLenBytes = ProtoCommon.long2buff(master_filename.length()); + System.arraycopy(hexLenBytes, 0, sizeBytes, 0, hexLenBytes.length); + offset = hexLenBytes.length; + } else { + masterFilenameBytes = null; + sizeBytes = new byte[1 + 1 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE]; + body_len = sizeBytes.length + ProtoCommon.FDFS_FILE_EXT_NAME_MAX_LEN + file_size; + + sizeBytes[0] = (byte) this.storageServer.getStorePathIndex(); + offset = 1; + } + + hexLenBytes = ProtoCommon.long2buff(file_size); + System.arraycopy(hexLenBytes, 0, sizeBytes, offset, hexLenBytes.length); + + OutputStream out = connection.getOutputStream(); + header = ProtoCommon.packHeader(cmd, body_len, (byte) 0); + byte[] wholePkg = new byte[(int) (header.length + body_len - file_size)]; + System.arraycopy(header, 0, wholePkg, 0, header.length); + System.arraycopy(sizeBytes, 0, wholePkg, header.length, sizeBytes.length); + offset = header.length + sizeBytes.length; + if (bUploadSlave) { + byte[] prefix_name_bs = new byte[ProtoCommon.FDFS_FILE_PREFIX_MAX_LEN]; + byte[] bs = prefix_name.getBytes(ClientGlobal.g_charset); + int prefix_name_len = bs.length; + Arrays.fill(prefix_name_bs, (byte) 0); + if (prefix_name_len > ProtoCommon.FDFS_FILE_PREFIX_MAX_LEN) { + prefix_name_len = ProtoCommon.FDFS_FILE_PREFIX_MAX_LEN; + } + if (prefix_name_len > 0) { + System.arraycopy(bs, 0, prefix_name_bs, 0, prefix_name_len); + } + + System.arraycopy(prefix_name_bs, 0, wholePkg, offset, prefix_name_bs.length); + offset += prefix_name_bs.length; + } + + System.arraycopy(ext_name_bs, 0, wholePkg, offset, ext_name_bs.length); + offset += ext_name_bs.length; + + if (bUploadSlave) { + System.arraycopy(masterFilenameBytes, 0, wholePkg, offset, masterFilenameBytes.length); + offset += masterFilenameBytes.length; + } + + out.write(wholePkg); + + if ((this.errno = (byte) callback.send(out)) != 0) { + return null; + } + + ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), + ProtoCommon.STORAGE_PROTO_CMD_RESP, -1); + this.errno = pkgInfo.errno; + if (pkgInfo.errno != 0) { + return null; + } + + if (pkgInfo.body.length <= ProtoCommon.FDFS_GROUP_NAME_MAX_LEN) { + throw new MyException("body length: " + pkgInfo.body.length + " <= " + ProtoCommon.FDFS_GROUP_NAME_MAX_LEN); + } + + new_group_name = new String(pkgInfo.body, 0, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN).trim(); + remote_filename = new String(pkgInfo.body, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN, pkgInfo.body.length - ProtoCommon.FDFS_GROUP_NAME_MAX_LEN); + String[] results = new String[2]; + results[0] = new_group_name; + results[1] = remote_filename; + + if (meta_list == null || meta_list.length == 0) { + return results; + } + + int result = 0; + try { + result = this.set_metadata(new_group_name, remote_filename, + meta_list, ProtoCommon.STORAGE_SET_METADATA_FLAG_OVERWRITE); + } catch (IOException ex) { + result = 5; + throw ex; + } finally { + if (result != 0) { + this.errno = (byte) result; + this.delete_file(new_group_name, remote_filename); + return null; + } + } + return results; + } catch (IOException ex) { + try { + connection.close(); + } catch (IOException ex1) { + ex1.printStackTrace(); + } finally { + connection = null; + } + throw ex; + } finally { + releaseConnection(connection, bNewStorageServer); + + } + } + + /** + * append file to storage server + * + * @param group_name the group name of appender file + * @param appender_filename the appender filename + * @param file_size the file size + * @param callback the write data callback object + * @return return true for success, false for fail + */ + protected int do_append_file(String group_name, String appender_filename, + long file_size, UploadCallback callback) throws IOException, MyException { + byte[] header; + boolean bNewStorageServer; + Connection connection = null; + byte[] hexLenBytes; + byte[] appenderFilenameBytes; + int offset; + long body_len; + + if ((group_name == null || group_name.length() == 0) || + (appender_filename == null || appender_filename.length() == 0)) { + this.errno = ProtoCommon.ERR_NO_EINVAL; + return this.errno; + } + + bNewStorageServer = this.newUpdatableStorageConnection(group_name, appender_filename); + + try { + connection = this.storageServer.getConnection(); + + appenderFilenameBytes = appender_filename.getBytes(ClientGlobal.g_charset); + body_len = 2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE + appenderFilenameBytes.length + file_size; + + header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_APPEND_FILE, body_len, (byte) 0); + byte[] wholePkg = new byte[(int) (header.length + body_len - file_size)]; + System.arraycopy(header, 0, wholePkg, 0, header.length); + offset = header.length; + + hexLenBytes = ProtoCommon.long2buff(appender_filename.length()); + System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); + offset += hexLenBytes.length; + + hexLenBytes = ProtoCommon.long2buff(file_size); + System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); + offset += hexLenBytes.length; + + OutputStream out = connection.getOutputStream(); + + System.arraycopy(appenderFilenameBytes, 0, wholePkg, offset, appenderFilenameBytes.length); + offset += appenderFilenameBytes.length; + + out.write(wholePkg); + if ((this.errno = (byte) callback.send(out)) != 0) { + return this.errno; + } + + ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), + ProtoCommon.STORAGE_PROTO_CMD_RESP, 0); + this.errno = pkgInfo.errno; + if (pkgInfo.errno != 0) { + return this.errno; + } + + return 0; + } catch (IOException ex) { + try { + connection.close(); + } catch (IOException ex1) { + ex1.printStackTrace(); + } finally { + connection = null; + } + throw ex; + } finally { + releaseConnection(connection, bNewStorageServer); + } + } + + private void releaseConnection(Connection connection, boolean bNewStorageServer) { + try { + if (connection != null) { + connection.release(); + } + } catch (IOException ex1) { + ex1.printStackTrace(); + } finally { + if (bNewStorageServer) { + this.storageServer = null; + } + } + } + + /** + * modify appender file to storage server + * + * @param group_name the group name of appender file + * @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 return true for success, false for fail + */ + protected int do_modify_file(String group_name, String appender_filename, + long file_offset, long modify_size, UploadCallback callback) throws IOException, MyException { + byte[] header; + boolean bNewStorageServer; + Connection connection = null; + byte[] hexLenBytes; + byte[] appenderFilenameBytes; + int offset; + long body_len; + + if ((group_name == null || group_name.length() == 0) || + (appender_filename == null || appender_filename.length() == 0)) { + this.errno = ProtoCommon.ERR_NO_EINVAL; + return this.errno; + } + + bNewStorageServer = this.newUpdatableStorageConnection(group_name, appender_filename); + + try { + connection = this.storageServer.getConnection(); + + appenderFilenameBytes = appender_filename.getBytes(ClientGlobal.g_charset); + body_len = 3 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE + appenderFilenameBytes.length + modify_size; + + header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_MODIFY_FILE, body_len, (byte) 0); + byte[] wholePkg = new byte[(int) (header.length + body_len - modify_size)]; + System.arraycopy(header, 0, wholePkg, 0, header.length); + offset = header.length; + + hexLenBytes = ProtoCommon.long2buff(appender_filename.length()); + System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); + offset += hexLenBytes.length; + + hexLenBytes = ProtoCommon.long2buff(file_offset); + System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); + offset += hexLenBytes.length; + + hexLenBytes = ProtoCommon.long2buff(modify_size); + System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); + offset += hexLenBytes.length; + + OutputStream out = connection.getOutputStream(); + + System.arraycopy(appenderFilenameBytes, 0, wholePkg, offset, appenderFilenameBytes.length); + offset += appenderFilenameBytes.length; + + out.write(wholePkg); + if ((this.errno = (byte) callback.send(out)) != 0) { + return this.errno; + } + + ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), + ProtoCommon.STORAGE_PROTO_CMD_RESP, 0); + this.errno = pkgInfo.errno; + if (pkgInfo.errno != 0) { + return this.errno; + } + + return 0; + } catch (IOException ex) { + try { + connection.close(); + } catch (IOException ex1) { + ex1.printStackTrace(); + } finally { + connection = null; + } + + throw ex; + } finally { + releaseConnection(connection, bNewStorageServer); + } + } + + /** + * delete file from storage server + * + * @param group_name the group name of storage server + * @param remote_filename filename on storage server + * @return 0 for success, none zero for fail (error code) + */ + public int delete_file(String group_name, String remote_filename) throws IOException, MyException { + boolean bNewStorageServer = this.newUpdatableStorageConnection(group_name, remote_filename); + Connection connection = this.storageServer.getConnection(); + + try { + this.send_package(ProtoCommon.STORAGE_PROTO_CMD_DELETE_FILE, group_name, remote_filename, connection); + ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), + ProtoCommon.STORAGE_PROTO_CMD_RESP, 0); + + this.errno = pkgInfo.errno; + return pkgInfo.errno; + } catch (IOException ex) { + try { + connection.close(); + } catch (IOException ex1) { + ex1.printStackTrace(); + } finally { + connection = null; + } + throw ex; + } finally { + releaseConnection(connection, bNewStorageServer); + } + } + + /** + * truncate appender file to size 0 from storage server + * + * @param group_name the group name of storage server + * @param appender_filename the appender filename + * @return 0 for success, none zero for fail (error code) + */ + public int truncate_file(String group_name, String appender_filename) throws IOException, MyException { + final long truncated_file_size = 0; + return this.truncate_file(group_name, appender_filename, truncated_file_size); + } + + /** + * truncate appender file from storage server + * + * @param group_name the group name of storage server + * @param appender_filename the appender filename + * @param truncated_file_size truncated file size + * @return 0 for success, none zero for fail (error code) + */ + public int truncate_file(String group_name, String appender_filename, + long truncated_file_size) throws IOException, MyException { + byte[] header; + boolean bNewStorageServer; + Connection connection = null; + byte[] hexLenBytes; + byte[] appenderFilenameBytes; + int offset; + int body_len; + + if ((group_name == null || group_name.length() == 0) || + (appender_filename == null || appender_filename.length() == 0)) { + this.errno = ProtoCommon.ERR_NO_EINVAL; + return this.errno; + } + + bNewStorageServer = this.newUpdatableStorageConnection(group_name, appender_filename); + + try { + connection = this.storageServer.getConnection(); + + appenderFilenameBytes = appender_filename.getBytes(ClientGlobal.g_charset); + body_len = 2 * ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE + appenderFilenameBytes.length; + + header = ProtoCommon.packHeader(ProtoCommon.STORAGE_PROTO_CMD_TRUNCATE_FILE, body_len, (byte) 0); + byte[] wholePkg = new byte[header.length + body_len]; + System.arraycopy(header, 0, wholePkg, 0, header.length); + offset = header.length; + + hexLenBytes = ProtoCommon.long2buff(appender_filename.length()); + System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); + offset += hexLenBytes.length; + + hexLenBytes = ProtoCommon.long2buff(truncated_file_size); + System.arraycopy(hexLenBytes, 0, wholePkg, offset, hexLenBytes.length); + offset += hexLenBytes.length; + + OutputStream out = connection.getOutputStream(); + + System.arraycopy(appenderFilenameBytes, 0, wholePkg, offset, appenderFilenameBytes.length); + offset += appenderFilenameBytes.length; + + out.write(wholePkg); + ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), + ProtoCommon.STORAGE_PROTO_CMD_RESP, 0); + this.errno = pkgInfo.errno; + return pkgInfo.errno; + } catch (IOException ex) { + try { + connection.close(); + } catch (IOException ex1) { + ex1.printStackTrace(); + } finally { + connection = null; + } + throw ex; + } finally { + releaseConnection(connection, bNewStorageServer); + } + } + + /** + * download file from storage server + * + * @param group_name the group name of storage server + * @param remote_filename filename on storage server + * @return file content/buff, return null if fail + */ + public byte[] download_file(String group_name, String remote_filename) throws IOException, MyException { + final long file_offset = 0; + final long download_bytes = 0; + + return this.download_file(group_name, remote_filename, file_offset, download_bytes); + } + + /** + * download file from 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, 0 for remain bytes from offset + * @return file content/buff, return null if fail + */ + public byte[] download_file(String group_name, String remote_filename, long file_offset, long download_bytes) throws IOException, MyException { + boolean bNewStorageServer = this.newReadableStorageConnection(group_name, remote_filename); + Connection connection = this.storageServer.getConnection(); + + try { + ProtoCommon.RecvPackageInfo pkgInfo; + + this.send_download_package(group_name, remote_filename, file_offset, download_bytes, connection); + pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), + ProtoCommon.STORAGE_PROTO_CMD_RESP, -1); + + this.errno = pkgInfo.errno; + if (pkgInfo.errno != 0) { + return null; + } + + return pkgInfo.body; + } catch (IOException ex) { + try { + connection.close(); + } catch (IOException ex1) { + ex1.printStackTrace(); + } finally { + connection = null; + } + throw ex; + } finally { + releaseConnection(connection, bNewStorageServer); + } + } + + /** + * download file from storage server + * + * @param group_name the group name of storage server + * @param remote_filename filename on storage server + * @param local_filename filename on local + * @return 0 success, return none zero errno if fail + */ + public int download_file(String group_name, String remote_filename, + String local_filename) throws IOException, MyException { + final long file_offset = 0; + final long download_bytes = 0; + return this.download_file(group_name, remote_filename, + file_offset, download_bytes, local_filename); + } + + /** + * download file from 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, 0 for remain bytes from offset + * @param local_filename filename on local + * @return 0 success, return none zero errno if fail + */ + public int download_file(String group_name, String remote_filename, + long file_offset, long download_bytes, + String local_filename) throws IOException, MyException { + boolean bNewStorageServer = this.newReadableStorageConnection(group_name, remote_filename); + Connection connection = this.storageServer.getConnection(); + try { + ProtoCommon.RecvHeaderInfo header; + FileOutputStream out = new FileOutputStream(local_filename); + try { + this.errno = 0; + this.send_download_package(group_name, remote_filename, file_offset, download_bytes, connection); + + InputStream in = connection.getInputStream(); + header = ProtoCommon.recvHeader(in, ProtoCommon.STORAGE_PROTO_CMD_RESP, -1); + this.errno = header.errno; + if (header.errno != 0) { + return header.errno; + } + + byte[] buff = new byte[256 * 1024]; + long remainBytes = header.body_len; + int bytes; + + //System.out.println("expect_body_len=" + header.body_len); + + while (remainBytes > 0) { + if ((bytes = in.read(buff, 0, remainBytes > buff.length ? buff.length : (int) remainBytes)) < 0) { + throw new IOException("recv package size " + (header.body_len - remainBytes) + " != " + header.body_len); + } + + out.write(buff, 0, bytes); + remainBytes -= bytes; + + //System.out.println("totalBytes=" + (header.body_len - remainBytes)); + } + + return 0; + } catch (IOException ex) { + if (this.errno == 0) { + this.errno = ProtoCommon.ERR_NO_EIO; + } + + throw ex; + } finally { + out.close(); + if (this.errno != 0) { + (new File(local_filename)).delete(); + } + } + } catch (IOException ex) { + try { + connection.close(); + } catch (IOException ex1) { + ex1.printStackTrace(); + } finally { + connection = null; + } + throw ex; + } finally { + releaseConnection(connection, bNewStorageServer); + } + } + + /** + * download file from storage server + * + * @param group_name the group name of storage server + * @param remote_filename filename on storage server + * @param callback call callback.recv() when data arrive + * @return 0 success, return none zero errno if fail + */ + public int download_file(String group_name, String remote_filename, + DownloadCallback callback) throws IOException, MyException { + final long file_offset = 0; + final long download_bytes = 0; + return this.download_file(group_name, remote_filename, + file_offset, download_bytes, callback); + } + + /** + * download file from 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, 0 for remain bytes from offset + * @param callback call callback.recv() when data arrive + * @return 0 success, return none zero errno if fail + */ + public int download_file(String group_name, String remote_filename, + long file_offset, long download_bytes, + DownloadCallback callback) throws IOException, MyException { + int result; + boolean bNewStorageServer = this.newReadableStorageConnection(group_name, remote_filename); + Connection connection = this.storageServer.getConnection(); + + try { + ProtoCommon.RecvHeaderInfo header; + this.send_download_package(group_name, remote_filename, file_offset, download_bytes, connection); + + InputStream in = connection.getInputStream(); + header = ProtoCommon.recvHeader(in, ProtoCommon.STORAGE_PROTO_CMD_RESP, -1); + this.errno = header.errno; + if (header.errno != 0) { + return header.errno; + } + + byte[] buff = new byte[2 * 1024]; + long remainBytes = header.body_len; + int bytes; + + //System.out.println("expect_body_len=" + header.body_len); + + while (remainBytes > 0) { + if ((bytes = in.read(buff, 0, remainBytes > buff.length ? buff.length : (int) remainBytes)) < 0) { + throw new IOException("recv package size " + (header.body_len - remainBytes) + " != " + header.body_len); + } + + if ((result = callback.recv(header.body_len, buff, bytes)) != 0) { + this.errno = (byte) result; + return result; + } + + remainBytes -= bytes; + //System.out.println("totalBytes=" + (header.body_len - remainBytes)); + } + + return 0; + } catch (IOException ex) { + try { + connection.close(); + } catch (IOException ex1) { + ex1.printStackTrace(); + } finally { + connection = null; + } + throw ex; + } finally { + releaseConnection(connection, bNewStorageServer); + } + } + + /** + * get all metadata items from storage server + * + * @param group_name the group name of storage server + * @param remote_filename filename on storage server + * @return meta info array, return null if fail + */ + public NameValuePair[] get_metadata(String group_name, String remote_filename) throws IOException, MyException { + boolean bNewStorageServer = this.newUpdatableStorageConnection(group_name, remote_filename); + Connection connection = this.storageServer.getConnection(); + try { + ProtoCommon.RecvPackageInfo pkgInfo; + + this.send_package(ProtoCommon.STORAGE_PROTO_CMD_GET_METADATA, group_name, remote_filename, connection); + pkgInfo = ProtoCommon.recvPackage(connection.getInputStream(), + ProtoCommon.STORAGE_PROTO_CMD_RESP, -1); + + this.errno = pkgInfo.errno; + if (pkgInfo.errno != 0) { + return null; + } + + return ProtoCommon.split_metadata(new String(pkgInfo.body, ClientGlobal.g_charset)); + } catch (IOException ex) { + try { + connection.close(); + } catch (IOException ex1) { + ex1.printStackTrace(); + } finally { + connection = null; + } + throw ex; + } finally { + releaseConnection(connection, bNewStorageServer); + } + } + + /** + * set metadata items to storage server + * + * @param group_name the group name of storage server + * @param remote_filename filename on storage server + * @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_metadata(String group_name, String remote_filename, + NameValuePair[] meta_list, byte op_flag) throws IOException, MyException { + boolean bNewStorageServer = this.newUpdatableStorageConnection(group_name, remote_filename); + Connection connection = this.storageServer.getConnection(); + 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 = connection.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(connection.getInputStream(), + ProtoCommon.STORAGE_PROTO_CMD_RESP, 0); + + this.errno = pkgInfo.errno; + return pkgInfo.errno; + } catch (IOException ex) { + try { + connection.close(); + } catch (IOException ex1) { + ex1.printStackTrace(); + } finally { + connection = null; + } + throw ex; + } finally { + releaseConnection(connection, bNewStorageServer); + } + } + + /** + * 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, MyException { + 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)); + + short file_type; + long file_size = ProtoCommon.buff2long(buff, 4 * 2); + if (((file_size & ProtoCommon.APPENDER_FILE_SIZE) != 0)) { + file_type = FileInfo.FILE_TYPE_APPENDER; + } else 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_type = FileInfo.FILE_TYPE_SLAVE; + } else { + file_type = FileInfo.FILE_TYPE_NORMAL; + } + + if (file_type == FileInfo.FILE_TYPE_SLAVE || + file_type == FileInfo.FILE_TYPE_APPENDER) { //slave file or appender file + FileInfo fi = this.query_file_info(group_name, remote_filename); + if (fi == null) { + return null; + } + + fi.setFileType(file_type); + return fi; + } + + int create_timestamp = ProtoCommon.buff2int(buff, 4); + if ((file_size >> 63) != 0) { + file_size &= 0xFFFFFFFFL; //low 32 bits is file size + } + int crc32 = ProtoCommon.buff2int(buff, 4 * 4); + + return new FileInfo(false, file_type, file_size, create_timestamp, + crc32, ProtoCommon.getIpAddress(buff, 0)); + } + + /** + * 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, MyException { + boolean bNewStorageServer = this.newUpdatableStorageConnection(group_name, remote_filename); + Connection connection = this.storageServer.getConnection(); + 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 = connection.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(connection.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(true, FileInfo.FILE_TYPE_NORMAL, file_size, + create_timestamp, crc32, source_ip_addr); + } catch (IOException ex) { + try { + connection.close(); + } catch (IOException ex1) { + ex1.printStackTrace(); + } finally { + connection = null; + } + throw ex; + } finally { + releaseConnection(connection, bNewStorageServer); + } + } + + /** + * 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, MyException { + 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 MyException("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, MyException { + 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 MyException("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, MyException { + 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 MyException("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, Connection connection) 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); + connection.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, Connection connection) 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); + connection.getOutputStream().write(wholePkg); + } + + public boolean isConnected() { + return trackerServer != null; + } + + public boolean isAvaliable() { + return trackerServer != null; + } + + public void close() throws IOException { + trackerServer = null; + } + + public TrackerServer getTrackerServer() { + return trackerServer; + } + + public void setTrackerServer(TrackerServer trackerServer) { + this.trackerServer = trackerServer; + } + + public StorageServer getStorageServer() { + return storageServer; + } + + public void setStorageServer(StorageServer storageServer) { + this.storageServer = storageServer; + } + + /** + * 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 + */ + @Override + public int send(OutputStream out) throws IOException { + out.write(this.fileBuff, this.offset, this.length); + + return 0; + } + } +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/StorageClient1.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/StorageClient1.java new file mode 100644 index 0000000..6376d67 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/StorageClient1.java @@ -0,0 +1,766 @@ +/** + * 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 https://github.com/happyfish100/fastdfs for more detail. + */ + +package com.csource.fastdfs; + + +import com.csource.common.MyException; +import com.csource.common.NameValuePair; + +import java.io.IOException; + +/** + * Storage client for 1 field file id: combined group name and filename + * Note: the instance of this class is NOT thread safe !!! + * if not necessary, do NOT set storage server instance + * + * @author Happy Fish / YuQing + * @version Version 1.27 + */ +public class StorageClient1 extends StorageClient { + public static final String SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR = "/"; + + /** + * constructor + */ + public StorageClient1() { + super(); + } + + /** + * constructor with trackerServer + * + * @param trackerServer the tracker server, can be null + */ + public StorageClient1(TrackerServer trackerServer) { + super(trackerServer); + } + + /** + * constructor with trackerServer and storageServer + * NOTE: if not necessary, do NOT set storage server instance + * + * @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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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); + } + + /** + * regenerate filename for appender file + * + * @param appender_file_id the appender file id + * @return the regenerated file id, return null if fail + */ + public String regenerate_appender_filename1(String appender_file_id) throws IOException, MyException { + String[] parts = new String[2]; + this.errno = this.split_file_id(appender_file_id, parts); + if (this.errno != 0) { + return null; + } + + String[] new_parts = this.regenerate_appender_filename(parts[0], parts[1]); + if (new_parts != null) { + return new_parts[0] + SPLIT_GROUP_NAME_AND_FILENAME_SEPERATOR + new_parts[1]; + } else { + return null; + } + } + + /** + * 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + 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/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/StorageServer.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/StorageServer.java new file mode 100644 index 0000000..8c51f03 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/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 https://github.com/happyfish100/fastdfs for more detail. + */ + +package com.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(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(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/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/StructBase.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/StructBase.java new file mode 100644 index 0000000..1576459 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/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 https://github.com/happyfish100/fastdfs for more detail. + */ + +package com.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/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/StructGroupStat.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/StructGroupStat.java new file mode 100644 index 0000000..41116cf --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/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 https://github.com/happyfish100/fastdfs for more detail. + */ + +package com.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/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/StructStorageStat.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/StructStorageStat.java new file mode 100644 index 0000000..6bd4522 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/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 https://github.com/happyfish100/fastdfs for more detail. + */ + +package com.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/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/TrackerClient.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/TrackerClient.java new file mode 100644 index 0000000..cbc37e1 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/TrackerClient.java @@ -0,0 +1,809 @@ +/** + * 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 https://github.com/happyfish100/fastdfs for more detail. + */ + +package com.csource.fastdfs; + + +import com.csource.common.MyException; +import com.csource.fastdfs.pool.Connection; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; + +/** + * Tracker client for request to tracker server + * Note: the instance of this class is NOT thread safe !!! + * + * @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 getTrackerServer() throws IOException { + return this.tracker_group.getTrackerServer(); + } + + /** + * 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, MyException { + final String groupName = null; + return this.getStoreStorage(trackerServer, groupName); + } + + public Connection getConnection(TrackerServer trackerServer) throws IOException, MyException { + Connection connection = null; + int length = this.tracker_group.tracker_servers.length; + boolean failOver = length > 1 && trackerServer == null; + try { + if (trackerServer == null) { + trackerServer = getTrackerServer(); + if (trackerServer == null) { + throw new MyException("tracker server is empty!"); + } + } + connection = trackerServer.getConnection(); + } catch (IOException e) { + if (failOver) { + System.err.println("trackerServer get connection error, emsg:" + e.getMessage()); + } else { + throw e; + } + } catch (MyException e) { + if (failOver) { + System.err.println("trackerServer get connection error, emsg:" + e.getMessage()); + } else { + throw e; + } + } + if (connection != null || !failOver) { + return connection; + } + //do fail over + int currentIndex = 0; + if (trackerServer != null) { + currentIndex = trackerServer.getIndex(); + } + int failOverCount = 0; + while (failOverCount < length - 1) { + failOverCount++; + currentIndex++; + if (currentIndex >= length) { + currentIndex = 0; + } + try { + trackerServer = this.tracker_group.getTrackerServer(currentIndex); + if (trackerServer == null) { + throw new MyException("tracker server is empty!"); + } + return trackerServer.getConnection(); + } catch (IOException e) { + System.err.println("fail over trackerServer get connection error, failOverCount:" + failOverCount + "," + e.getMessage()); + if (failOverCount == length - 1) { + throw e; + } + + } catch (MyException e) { + System.err.println("fail over trackerServer get connection error, failOverCount:" + failOverCount + ", " + e.getMessage()); + if (failOverCount == length - 1) { + throw e; + } + } + + + } + return null; + } + + + /** + * 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, MyException { + byte[] header; + String ip_addr; + int port; + byte cmd; + int out_len; + byte store_path; + Connection connection = getConnection(trackerServer); + OutputStream out = connection.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(connection.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) { + try { + connection.close(); + } catch (IOException ex1) { + ex1.printStackTrace(); + } finally { + connection = null; + } + throw ex; + } finally { + if (connection != null) { + try { + connection.release(); + } 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, MyException { + byte[] header; + String ip_addr; + int port; + byte cmd; + int out_len; + Connection connection = getConnection(trackerServer); + OutputStream out = connection.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(connection.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) { + try { + connection.close(); + } catch (IOException ex1) { + ex1.printStackTrace(); + } finally { + connection = null; + } + throw ex; + } finally { + if (connection != null) { + try { + connection.release(); + } 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, MyException { + 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, MyException { + 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, MyException { + 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, MyException { + byte[] header; + byte[] bFileName; + byte[] bGroupName; + byte[] bs; + int len; + String ip_addr; + int port; + Connection connection = getConnection(trackerServer); + OutputStream out = connection.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(connection.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) { + try { + connection.close(); + } catch (IOException ex1) { + ex1.printStackTrace(); + } finally { + connection = null; + } + + throw ex; + } finally { + if (connection != null) { + try { + connection.release(); + } 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, MyException { + 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, MyException { + 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, MyException { + byte[] header; + String ip_addr; + int port; + byte cmd; + int out_len; + byte store_path; + Connection connection = getConnection(trackerServer); + OutputStream out = connection.getOutputStream(); + + try { + header = ProtoCommon.packHeader(ProtoCommon.TRACKER_PROTO_CMD_SERVER_LIST_GROUP, 0, (byte) 0); + out.write(header); + + ProtoCommon.RecvPackageInfo pkgInfo = ProtoCommon.recvPackage(connection.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) { + try { + connection.close(); + } catch (IOException ex1) { + ex1.printStackTrace(); + } finally { + connection = null; + } + throw ex; + } catch (Exception ex) { + ex.printStackTrace(); + this.errno = ProtoCommon.ERR_NO_EINVAL; + return null; + } finally { + if (connection != null) { + try { + connection.release(); + } 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, MyException { + 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, MyException { + byte[] header; + byte[] bGroupName; + byte[] bs; + int len; + Connection connection = getConnection(trackerServer); + OutputStream out = connection.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(connection.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) { + try { + connection.close(); + } catch (IOException ex1) { + ex1.printStackTrace(); + } + + throw ex; + } catch (Exception ex) { + ex.printStackTrace(); + this.errno = ProtoCommon.ERR_NO_EINVAL; + return null; + } finally { + if (connection != null) { + try { + connection.release(); + } 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, MyException { + byte[] header; + byte[] bGroupName; + byte[] bs; + int len; + Connection connection; + + connection = trackerServer.getConnection(); + OutputStream out = connection.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 = 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(connection.getInputStream(), + ProtoCommon.TRACKER_PROTO_CMD_RESP, 0); + this.errno = pkgInfo.errno; + return pkgInfo.errno == 0; + } catch (IOException e) { + try { + connection.close(); + } finally { + connection = null; + } + throw e; + } finally { + if (connection != null) { + connection.release(); + } + } + } + + /** + * 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, MyException { + 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, MyException { + int serverIndex; + int notFoundCount; + TrackerServer trackerServer; + + notFoundCount = 0; + for (serverIndex = 0; serverIndex < trackerGroup.tracker_servers.length; serverIndex++) { + try { + trackerServer = trackerGroup.getTrackerServer(serverIndex); + } catch (IOException ex) { + ex.printStackTrace(System.err); + this.errno = ProtoCommon.ECONNREFUSED; + return false; + } + 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; + } + + } + + 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.getTrackerServer(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; + } + 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; + } + } + } + + } + + 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/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/TrackerGroup.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/TrackerGroup.java new file mode 100644 index 0000000..ba5d9e1 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/TrackerGroup.java @@ -0,0 +1,101 @@ +/** + * 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 https://github.com/happyfish100/fastdfs for more detail. + */ + +package com.csource.fastdfs; + +import java.io.IOException; +import java.net.InetSocketAddress; + +/** + * 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 getTrackerServer(int serverIndex) throws IOException { + return new TrackerServer(this.tracker_servers[serverIndex], serverIndex); + } + + /** + * return connected tracker server + * + * @return connected tracker server, null for fail + */ + public TrackerServer getTrackerServer() 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.getTrackerServer(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.getTrackerServer(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/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/TrackerServer.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/TrackerServer.java new file mode 100644 index 0000000..44d917d --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/TrackerServer.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 https://github.com/happyfish100/fastdfs for more detail. + */ + +package com.csource.fastdfs; + +import com.csource.common.MyException; +import com.csource.fastdfs.pool.ConnectionFactory; +import com.csource.fastdfs.pool.ConnectionPool; +import com.csource.fastdfs.pool.Connection; + +import java.io.IOException; +import java.net.InetSocketAddress; + +/** + * Tracker Server Info + * + * @author Happy Fish / YuQing + * @version Version 1.11 + */ +public class TrackerServer { + protected InetSocketAddress inetSockAddr; + + protected int index; + + + public TrackerServer(InetSocketAddress inetSockAddr) throws IOException { + this.inetSockAddr = inetSockAddr; + } + + public TrackerServer(InetSocketAddress inetSockAddr, int index) { + this.inetSockAddr = inetSockAddr; + this.index = index; + } + + public Connection getConnection() throws MyException, IOException { + Connection connection; + if (ClientGlobal.g_connection_pool_enabled) { + connection = ConnectionPool.getConnection(this.inetSockAddr); + } else { + connection = ConnectionFactory.create(this.inetSockAddr); + } + return connection; + } + + /** + * get the server info + * + * @return the server info + */ + public InetSocketAddress getInetSocketAddress() { + return this.inetSockAddr; + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/UploadCallback.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/UploadCallback.java new file mode 100644 index 0000000..635c803 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/UploadCallback.java @@ -0,0 +1,28 @@ +/** + * 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 https://github.com/happyfish100/fastdfs for more detail. + */ + +package com.csource.fastdfs; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * upload file callback interface + * + * @author Happy Fish / YuQing + * @version Version 1.0 + */ +public interface UploadCallback { + /** + * 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 + */ + int send(OutputStream out) throws IOException; +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/UploadStream.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/UploadStream.java new file mode 100644 index 0000000..77c6e8a --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/UploadStream.java @@ -0,0 +1,56 @@ +package com.csource.fastdfs; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Upload file by stream + * + * @author zhouzezhong & Happy Fish / YuQing + * @version Version 1.11 + */ +public class UploadStream implements UploadCallback { + private InputStream inputStream; //input stream for reading + private long fileSize = 0; //size of the uploaded file + + /** + * constructor + * + * @param inputStream input stream for uploading + * @param fileSize size of uploaded file + */ + public UploadStream(InputStream inputStream, long fileSize) { + super(); + this.inputStream = inputStream; + this.fileSize = fileSize; + } + + /** + * 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 + */ + @Override + public int send(OutputStream out) throws IOException { + long remainBytes = fileSize; + byte[] buff = new byte[256 * 1024]; + int bytes; + while (remainBytes > 0) { + try { + if ((bytes = inputStream.read(buff, 0, remainBytes > buff.length ? buff.length : (int) remainBytes)) < 0) { + return -1; + } + } catch (IOException ex) { + ex.printStackTrace(); + return -1; + } + + out.write(buff, 0, bytes); + remainBytes -= bytes; + } + + return 0; + } +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/pool/Connection.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/pool/Connection.java new file mode 100644 index 0000000..4c1f8a7 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/pool/Connection.java @@ -0,0 +1,140 @@ +package com.csource.fastdfs.pool; + + +import com.csource.fastdfs.ProtoCommon; +import com.csource.fastdfs.ClientGlobal; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.Socket; + +public class Connection { + + private Socket sock; + private InetSocketAddress inetSockAddr; + private Long lastAccessTime = System.currentTimeMillis(); + + private boolean needActiveTest = false; + + public Connection(Socket sock, InetSocketAddress inetSockAddr) { + this.sock = sock; + this.inetSockAddr = inetSockAddr; + } + + /** + * 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 Long getLastAccessTime() { + return lastAccessTime; + } + + public void setLastAccessTime(Long lastAccessTime) { + this.lastAccessTime = lastAccessTime; + } + + /** + * @throws IOException + */ + public void close() throws IOException { + //if connection enabled get from connection pool + if (ClientGlobal.g_connection_pool_enabled) { + ConnectionPool.closeConnection(this); + } else { + this.closeDirectly(); + } + } + + public void release() throws IOException { + if (ClientGlobal.g_connection_pool_enabled) { + ConnectionPool.releaseConnection(this); + } else { + this.closeDirectly(); + } + } + + /** + * force close socket, + */ + public void closeDirectly() throws IOException { + if (this.sock != null) { + try { + ProtoCommon.closeSocket(this.sock); + } finally { + this.sock = null; + } + } + } + + public boolean activeTest() throws IOException { + if (this.sock == null) { + return false; + } + return ProtoCommon.activeTest(this.sock); + } + + public boolean isConnected() { + boolean isConnected = false; + if (sock != null) { + if (sock.isConnected()) { + isConnected = true; + } + } + return isConnected; + } + + public boolean isAvaliable() { + if (isConnected()) { + if (sock.getPort() == 0) { + return false; + } + if (sock.getInetAddress() == null) { + return false; + } + if (sock.getRemoteSocketAddress() == null) { + return false; + } + if (sock.isInputShutdown()) { + return false; + } + if (sock.isOutputShutdown()) { + return false; + } + return true; + } + return false; + } + + public boolean isNeedActiveTest() { + return needActiveTest; + } + + public void setNeedActiveTest(boolean needActiveTest) { + this.needActiveTest = needActiveTest; + } + + @Override + public String toString() { + return "Connection{" + + "sock=" + sock + + ", inetSockAddr=" + inetSockAddr + + ", lastAccessTime=" + lastAccessTime + + ", needActiveTest=" + needActiveTest + + '}'; + } +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/pool/ConnectionFactory.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/pool/ConnectionFactory.java new file mode 100644 index 0000000..9dadcbf --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/pool/ConnectionFactory.java @@ -0,0 +1,29 @@ +package com.csource.fastdfs.pool; + +import com.csource.common.MyException; +import com.csource.fastdfs.ClientGlobal; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; + +public class ConnectionFactory { + /** + * create from InetSocketAddress + * + * @param socketAddress + * @return + * @throws IOException + */ + public static Connection create(InetSocketAddress socketAddress) throws MyException { + try { + Socket sock = new Socket(); + sock.setReuseAddress(true); + sock.setSoTimeout(ClientGlobal.g_network_timeout); + sock.connect(socketAddress, ClientGlobal.g_connect_timeout); + return new Connection(sock, socketAddress); + } catch (Exception e) { + throw new MyException("connect to server " + socketAddress.getAddress().getHostAddress() + ":" + socketAddress.getPort() + " fail, emsg:" + e.getMessage()); + } + } +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/pool/ConnectionManager.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/pool/ConnectionManager.java new file mode 100644 index 0000000..5f263ee --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/pool/ConnectionManager.java @@ -0,0 +1,148 @@ +package com.csource.fastdfs.pool; + +import com.csource.common.MyException; +import com.csource.fastdfs.ClientGlobal; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.LinkedList; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +public class ConnectionManager { + + private InetSocketAddress inetSocketAddress; + + /** + * total create connection pool + */ + private AtomicInteger totalCount = new AtomicInteger(); + + /** + * free connection count + */ + private AtomicInteger freeCount = new AtomicInteger(); + + /** + * lock + */ + private ReentrantLock lock = new ReentrantLock(true); + + private Condition condition = lock.newCondition(); + + /** + * free connections + */ + private LinkedList freeConnections = new LinkedList(); + + private ConnectionManager() { + + } + + public ConnectionManager(InetSocketAddress socketAddress) { + this.inetSocketAddress = socketAddress; + } + + public Connection getConnection() throws MyException { + lock.lock(); + try { + Connection connection = null; + while (true) { + if (freeCount.get() > 0) { + freeCount.decrementAndGet(); + connection = freeConnections.poll(); + if (!connection.isAvaliable() || (System.currentTimeMillis() - connection.getLastAccessTime()) > ClientGlobal.g_connection_pool_max_idle_time) { + closeConnection(connection); + continue; + } + if (connection.isNeedActiveTest()) { + boolean isActive = false; + try { + isActive = connection.activeTest(); + } catch (IOException e) { + System.err.println("send to server[" + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort() + "] active test error ,emsg:" + e.getMessage()); + isActive = false; + } + if (!isActive) { + closeConnection(connection); + continue; + } else { + connection.setNeedActiveTest(false); + } + } + } else if (ClientGlobal.g_connection_pool_max_count_per_entry == 0 || totalCount.get() < ClientGlobal.g_connection_pool_max_count_per_entry) { + connection = ConnectionFactory.create(this.inetSocketAddress); + totalCount.incrementAndGet(); + } else { + try { + if (condition.await(ClientGlobal.g_connection_pool_max_wait_time_in_ms, TimeUnit.MILLISECONDS)) { + //wait single success + continue; + } + throw new MyException("connect to server " + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort() + " fail, wait_time > " + ClientGlobal.g_connection_pool_max_wait_time_in_ms + "ms"); + } catch (InterruptedException e) { + e.printStackTrace(); + throw new MyException("connect to server " + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort() + " fail, emsg:" + e.getMessage()); + } + } + return connection; + } + } finally { + lock.unlock(); + } + } + + public void releaseConnection(Connection connection) { + if (connection == null) { + return; + } + lock.lock(); + try { + connection.setLastAccessTime(System.currentTimeMillis()); + freeConnections.add(connection); + freeCount.incrementAndGet(); + condition.signal(); + } finally { + lock.unlock(); + } + + } + + public void closeConnection(Connection connection) { + try { + if (connection != null) { + totalCount.decrementAndGet(); + connection.closeDirectly(); + } + } catch (IOException e) { + System.err.println("close socket[" + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort() + "] error ,emsg:" + e.getMessage()); + e.printStackTrace(); + } + } + + public void setActiveTestFlag() { + if (freeCount.get() > 0) { + lock.lock(); + try { + for (Connection freeConnection : freeConnections) { + freeConnection.setNeedActiveTest(true); + } + } finally { + lock.unlock(); + } + } + } + + + @Override + public String toString() { + return "ConnectionManager{" + + "ip:port='" + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort() + + ", totalCount=" + totalCount + + ", freeCount=" + freeCount + + ", freeConnections =" + freeConnections + + '}'; + } +} diff --git a/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/pool/ConnectionPool.java b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/pool/ConnectionPool.java new file mode 100644 index 0000000..07f8ee0 --- /dev/null +++ b/module/aivfo-dfs-client-spring-boot/aivfo-fast-dfs-client/src/main/java/com/csource/fastdfs/pool/ConnectionPool.java @@ -0,0 +1,81 @@ +package com.csource.fastdfs.pool; + +import com.csource.common.MyException; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class ConnectionPool { + /** + * key is ip:port, value is ConnectionManager + */ + private final static ConcurrentHashMap CP = new ConcurrentHashMap(); + + public static Connection getConnection(InetSocketAddress socketAddress) throws MyException { + if (socketAddress == null) { + return null; + } + String key = getKey(socketAddress); + ConnectionManager connectionManager; + connectionManager = CP.get(key); + if (connectionManager == null) { + synchronized (ConnectionPool.class) { + connectionManager = CP.get(key); + if (connectionManager == null) { + connectionManager = new ConnectionManager(socketAddress); + CP.put(key, connectionManager); + } + } + } + return connectionManager.getConnection(); + } + + public static void releaseConnection(Connection connection) throws IOException { + if (connection == null) { + return; + } + String key = getKey(connection.getInetSocketAddress()); + ConnectionManager connectionManager = CP.get(key); + if (connectionManager != null) { + connectionManager.releaseConnection(connection); + } else { + connection.closeDirectly(); + } + + } + + public static void closeConnection(Connection connection) throws IOException { + if (connection == null) { + return; + } + String key = getKey(connection.getInetSocketAddress()); + ConnectionManager connectionManager = CP.get(key); + if (connectionManager != null) { + connectionManager.closeConnection(connection); + connectionManager.setActiveTestFlag(); + } else { + connection.closeDirectly(); + } + } + + private static String getKey(InetSocketAddress socketAddress) { + if (socketAddress == null) { + return null; + } + return String.format("%s:%s", socketAddress.getAddress().getHostAddress(), socketAddress.getPort()); + } + + @Override + public String toString() { + if (!CP.isEmpty()) { + StringBuilder builder = new StringBuilder(); + for (Map.Entry managerEntry : CP.entrySet()) { + builder.append("key:[" + managerEntry.getKey() + " ]-------- entry:" + managerEntry.getValue() + "\n"); + } + return builder.toString(); + } + return null; + } +}