🎉 2.3.2.RELEASE

This commit is contained in:
smallchill 2019-06-17 00:15:24 +08:00
parent adcd42b62a
commit 1d6f118774
32 changed files with 1742 additions and 94 deletions

View File

@ -5,7 +5,7 @@
<parent>
<groupId>org.springblade</groupId>
<artifactId>blade-tool</artifactId>
<version>2.3.1</version>
<version>2.3.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -75,7 +75,7 @@ mybatis-plus:
swagger:
title: SpringBlade 接口文档系统
description: SpringBlade 接口文档系统
version: 2.3.1
version: 2.3.2
license: Powered By SpringBlade
licenseUrl: https://bladex.vip
terms-of-service-url: https://bladex.vip

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>2.3.1</version>
<version>2.3.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>2.3.1</version>
<version>2.3.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>2.3.1</version>
<version>2.3.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -27,6 +27,7 @@ import org.springframework.util.StringUtils;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 项目启动器搞定环境变量问题
@ -100,8 +101,10 @@ public class BladeApplication {
props.setProperty("spring.cloud.nacos.config.file-extension", NacosConstant.NACOS_CONFIG_FORMAT);
props.setProperty("spring.cloud.sentinel.transport.dashboard", SentinelConstant.SENTINEL_ADDR);
// 加载自定义组件
ServiceLoader<LauncherService> loader = ServiceLoader.load(LauncherService.class);
loader.forEach(launcherService -> launcherService.launcher(builder, appName, profile));
List<LauncherService> launcherList = new ArrayList<>();
ServiceLoader.load(LauncherService.class).forEach(launcherList::add);
launcherList.stream().sorted(Comparator.comparing(LauncherService::getOrder)).collect(Collectors.toList())
.forEach(launcherService -> launcherService.launcher(builder, appName, profile));
return builder;
}

View File

@ -25,7 +25,7 @@ public interface AppConstant {
/**
* 应用版本
*/
String APPLICATION_VERSION = "2.3.1";
String APPLICATION_VERSION = "2.3.2";
/**
* 基础包
@ -68,6 +68,10 @@ public interface AppConstant {
* 开发模块名称
*/
String APPLICATION_DEVELOP_NAME = APPLICATION_NAME_PREFIX + "develop";
/**
* 资源模块名称
*/
String APPLICATION_RESOURCE_NAME = APPLICATION_NAME_PREFIX + "resource";
/**
* 测试模块名称
*/

View File

@ -16,13 +16,14 @@
package org.springblade.core.launch.service;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.core.Ordered;
/**
* launcher 扩展 用于一些组件发现
*
* @author Chill
*/
public interface LauncherService {
public interface LauncherService extends Ordered, Comparable<LauncherService> {
/**
* 启动时 处理 SpringApplicationBuilder
@ -33,4 +34,25 @@ public interface LauncherService {
*/
void launcher(SpringApplicationBuilder builder, String appName, String profile);
/**
* 获取排列顺序
*
* @return order
*/
@Override
default int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
/**
* 对比排序
*
* @param o LauncherService
* @return compare
*/
@Override
default int compareTo(LauncherService o) {
return Integer.compare(this.getOrder(), o.getOrder());
}
}

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>2.3.1</version>
<version>2.3.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>2.3.1</version>
<version>2.3.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>

32
blade-core-oss/pom.xml Normal file
View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>2.3.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>blade-core-oss</artifactId>
<name>${project.artifactId}</name>
<version>${blade.tool.version}</version>
<packaging>jar</packaging>
<dependencies>
<!--Blade-->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-tool</artifactId>
<version>${blade.tool.version}</version>
</dependency>
<!--QiNiu-->
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>7.2.18</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,248 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.oss;
import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.FileInfo;
import com.qiniu.util.Auth;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springblade.core.oss.model.BladeFile;
import org.springblade.core.oss.model.OssFile;
import org.springblade.core.oss.props.OssProperties;
import org.springblade.core.oss.rule.OssRule;
import org.springblade.core.tool.utils.CollectionUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringPool;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
/**
* QiniuTemplate
*
* @author Chill
*/
@AllArgsConstructor
public class QiniuTemplate {
private Auth auth;
private UploadManager uploadManager;
private BucketManager bucketManager;
private OssProperties ossProperties;
private OssRule ossRule;
@SneakyThrows
public void makeBucket(String bucketName) {
if (!CollectionUtil.contains(bucketManager.buckets(), getBucketName(bucketName))) {
bucketManager.createBucket(getBucketName(bucketName), Zone.zone0().getRegion());
}
}
@SneakyThrows
public void removeBucket(String bucketName) {
bucketManager.deleteBucket(getBucketName(bucketName));
}
@SneakyThrows
public boolean bucketExists(String bucketName) {
return CollectionUtil.contains(bucketManager.buckets(), getBucketName(bucketName));
}
@SneakyThrows
public void copyFile(String bucketName, String fileName, String destBucketName) {
bucketManager.copy(getBucketName(bucketName), fileName, getBucketName(destBucketName), fileName);
}
@SneakyThrows
public void copyFile(String bucketName, String fileName, String destBucketName, String destFileName) {
bucketManager.copy(getBucketName(bucketName), fileName, getBucketName(destBucketName), destFileName);
}
@SneakyThrows
public OssFile statFile(String fileName) {
return statFile(ossProperties.getBucketName(), fileName);
}
@SneakyThrows
public OssFile statFile(String bucketName, String fileName) {
FileInfo stat = bucketManager.stat(getBucketName(bucketName), fileName);
OssFile ossFile = new OssFile();
ossFile.setName(stat.key);
ossFile.setName(Func.isEmpty(stat.key) ? fileName : stat.key);
ossFile.setLink(fileLink(ossFile.getName()));
ossFile.setHash(stat.hash);
ossFile.setLength(stat.fsize);
ossFile.setPutTime(new Date(stat.putTime / 10000));
ossFile.setContentType(stat.mimeType);
return ossFile;
}
@SneakyThrows
public String filePath(String fileName) {
return getBucketName().concat(StringPool.SLASH).concat(fileName);
}
@SneakyThrows
public String filePath(String bucketName, String fileName) {
return getBucketName(bucketName).concat(StringPool.SLASH).concat(fileName);
}
@SneakyThrows
public String fileLink(String fileName) {
return ossProperties.getEndpoint().concat(StringPool.SLASH).concat(fileName);
}
@SneakyThrows
public String fileLink(String bucketName, String fileName) {
return ossProperties.getEndpoint().concat(StringPool.SLASH).concat(fileName);
}
@SneakyThrows
public BladeFile putFile(MultipartFile file) {
return putFile(ossProperties.getBucketName(), file.getOriginalFilename(), file);
}
@SneakyThrows
public BladeFile putFile(String fileName, MultipartFile file) {
return putFile(ossProperties.getBucketName(), fileName, file);
}
@SneakyThrows
public BladeFile putFile(String bucketName, String fileName, MultipartFile file) {
return putFile(bucketName, fileName, file);
}
@SneakyThrows
public BladeFile putFile(String fileName, InputStream stream) {
return putFile(ossProperties.getBucketName(), fileName, stream);
}
@SneakyThrows
public BladeFile putFile(String bucketName, String fileName, InputStream stream) {
return put(bucketName, stream, fileName, false);
}
@SneakyThrows
public BladeFile put(String bucketName, InputStream stream, String key, boolean cover) {
makeBucket(bucketName);
key = getFileName(key);
// 覆盖上传
if (cover) {
uploadManager.put(stream, key, getUploadToken(bucketName, key), null, null);
} else {
Response response = uploadManager.put(stream, key, getUploadToken(bucketName), null, null);
int retry = 0;
int retryCount = 5;
while (response.needRetry() && retry < retryCount) {
response = uploadManager.put(stream, key, getUploadToken(bucketName), null, null);
retry++;
}
}
BladeFile file = new BladeFile();
file.setName(key);
file.setLink(fileLink(bucketName, key));
return file;
}
@SneakyThrows
public void removeFile(String fileName) {
bucketManager.delete(getBucketName(), fileName);
}
@SneakyThrows
public void removeFile(String bucketName, String fileName) {
bucketManager.delete(getBucketName(bucketName), fileName);
}
@SneakyThrows
public void removeFiles(List<String> fileNames) {
fileNames.forEach(this::removeFile);
}
@SneakyThrows
public void removeFiles(String bucketName, List<String> fileNames) {
fileNames.forEach(fileName -> removeFile(getBucketName(bucketName), fileName));
}
/**
* 根据规则生成存储桶名称规则
*
* @return String
*/
private String getBucketName() {
return getBucketName(ossProperties.getBucketName());
}
/**
* 根据规则生成存储桶名称规则
*
* @param bucketName 存储桶名称
* @return String
*/
private String getBucketName(String bucketName) {
return ossRule.bucketName(bucketName);
}
/**
* 根据规则生成文件名称规则
*
* @param originalFilename 原始文件名
* @return string
*/
private String getFileName(String originalFilename) {
return ossRule.fileName(originalFilename);
}
/**
* 获取上传凭证普通上传
*/
public String getUploadToken(String bucketName) {
return auth.uploadToken(getBucketName(bucketName));
}
/**
* 获取上传凭证覆盖上传
*/
private String getUploadToken(String bucketName, String key) {
return auth.uploadToken(getBucketName(bucketName), key);
}
}

View File

@ -0,0 +1,83 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.oss.config;
import com.qiniu.common.Zone;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.UploadManager;
import com.qiniu.util.Auth;
import lombok.AllArgsConstructor;
import org.springblade.core.oss.QiniuTemplate;
import org.springblade.core.oss.props.OssProperties;
import org.springblade.core.oss.rule.BladeOssRule;
import org.springblade.core.oss.rule.OssRule;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
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;
/**
* Oss配置类
*
* @author Chill
*/
@Configuration
@AllArgsConstructor
@EnableConfigurationProperties(OssProperties.class)
@ConditionalOnProperty(value = "oss.enable", havingValue = "true")
public class OssConfiguration {
private OssProperties ossProperties;
@Bean
@ConditionalOnMissingBean(OssRule.class)
public OssRule ossRule() {
return new BladeOssRule();
}
@Bean
public com.qiniu.storage.Configuration qiniuConfiguration() {
return new com.qiniu.storage.Configuration(Zone.zone0());
}
@Bean
public Auth auth() {
return Auth.create(ossProperties.getAccessKey(), ossProperties.getSecretKey());
}
@Bean
@ConditionalOnBean(com.qiniu.storage.Configuration.class)
public UploadManager uploadManager(com.qiniu.storage.Configuration cfg) {
return new UploadManager(cfg);
}
@Bean
@ConditionalOnBean(com.qiniu.storage.Configuration.class)
public BucketManager bucketManager(com.qiniu.storage.Configuration cfg) {
return new BucketManager(auth(), cfg);
}
@Bean
@ConditionalOnMissingBean(QiniuTemplate.class)
@ConditionalOnBean({Auth.class, UploadManager.class, BucketManager.class, OssRule.class})
public QiniuTemplate qiniuTemplate(Auth auth, UploadManager uploadManager, BucketManager bucketManager, OssRule ossRule) {
return new QiniuTemplate(auth, uploadManager, bucketManager, ossProperties, ossRule);
}
}

View File

@ -0,0 +1,35 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.oss.model;
import lombok.Data;
/**
* BladeFile
*
* @author Chill
*/
@Data
public class BladeFile {
/**
* 文件地址
*/
private String link;
/**
* 文件名
*/
private String name;
}

View File

@ -0,0 +1,53 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.oss.model;
import lombok.Data;
import java.util.Date;
/**
* OssFile
*
* @author Chill
*/
@Data
public class OssFile {
/**
* 文件地址
*/
private String link;
/**
* 文件名
*/
private String name;
/**
* 文件hash值
*/
public String hash;
/**
* 文件大小
*/
private long length;
/**
* 文件上传时间
*/
private Date putTime;
/**
* 文件contentType
*/
private String contentType;
}

View File

@ -0,0 +1,65 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.oss.props;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Minio参数配置类
*
* @author Chill
*/
@Data
@ConfigurationProperties(prefix = "oss")
public class OssProperties {
/**
* 是否启用
*/
private Boolean enable;
/**
* 对象存储名称
*/
private String name;
/**
* 是否开启租户模式
*/
private Boolean tenantMode;
/**
* 对象存储服务的URL
*/
private String endpoint;
/**
* Access key就像用户ID可以唯一标识你的账户
*/
private String accessKey;
/**
* Secret key是你账户的密码
*/
private String secretKey;
/**
* 默认的存储桶名称
*/
private String bucketName = "bladex";
}

View File

@ -0,0 +1,42 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.oss.rule;
import lombok.AllArgsConstructor;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.tool.utils.FileUtil;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.StringUtil;
/**
* 默认存储桶生成规则
*
* @author Chill
*/
@AllArgsConstructor
public class BladeOssRule implements OssRule {
@Override
public String bucketName(String bucketName) {
return bucketName;
}
@Override
public String fileName(String originalFilename) {
return "upload" + StringPool.SLASH + DateUtil.today() + StringPool.SLASH + StringUtil.randomUUID() + StringPool.DOT + FileUtil.getFileExtension(originalFilename);
}
}

View File

@ -0,0 +1,41 @@
/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.oss.rule;
/**
* Oss通用规则
*
* @author Chill
*/
public interface OssRule {
/**
* 获取存储桶规则
*
* @param bucketName 存储桶名称
* @return String
*/
String bucketName(String bucketName);
/**
* 获取文件名规则
*
* @param originalFilename 文件名
* @return String
*/
String fileName(String originalFilename);
}

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>2.3.1</version>
<version>2.3.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>blade-tool</artifactId>
<groupId>org.springblade</groupId>
<version>2.3.1</version>
<version>2.3.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -53,7 +53,7 @@ public class SwaggerProperties {
/**
* 版本
**/
private String version = "2.3.1";
private String version = "2.3.2";
/**
* 许可证
**/

31
blade-core-test/pom.xml Normal file
View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springblade</groupId>
<artifactId>blade-tool</artifactId>
<version>2.3.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>blade-core-test</artifactId>
<name>${project.artifactId}</name>
<version>${blade.tool.version}</version>
<packaging>jar</packaging>
<dependencies>
<!-- Blade -->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-launch</artifactId>
<version>${blade.tool.version}</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,30 @@
/**
* Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.test;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
/**
* boot test 基类
*
* @author L.cm
*/
@RunWith(BladeSpringRunner.class)
public abstract class BladeBaseTest extends AbstractJUnit4SpringContextTests {
}

View File

@ -0,0 +1,57 @@
/**
* Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
/**
* 简化 测试
*
* @author L.cm
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootTest
public @interface BladeBootTest {
/**
* 服务名appName
* @return appName
*/
@AliasFor("appName")
String value() default "blade-test";
/**
* 服务名appName
* @return appName
*/
@AliasFor("value")
String appName() default "blade-test";
/**
* profile
* @return profile
*/
String profile() default "dev";
/**
* 启用 ServiceLoader 加载 launcherService
* @return 是否启用
*/
boolean enableLoader() default false;
}

View File

@ -0,0 +1,29 @@
/**
* Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.test;
/**
* blade test 异常
*
* @author L.cm
*/
class BladeBootTestException extends RuntimeException {
BladeBootTestException(String message) {
super(message);
}
}

View File

@ -0,0 +1,80 @@
/**
* Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.test;
import org.junit.runners.model.InitializationError;
import org.springblade.core.launch.BladeApplication;
import org.springblade.core.launch.constant.AppConstant;
import org.springblade.core.launch.constant.NacosConstant;
import org.springblade.core.launch.constant.SentinelConstant;
import org.springblade.core.launch.service.LauncherService;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.*;
import java.util.stream.Collectors;
/**
* 设置启动参数
*
* @author L.cm
*/
public class BladeSpringRunner extends SpringJUnit4ClassRunner {
public BladeSpringRunner(Class<?> clazz) throws InitializationError {
super(clazz);
setUpTestClass(clazz);
}
private void setUpTestClass(Class<?> clazz) {
BladeBootTest bladeBootTest = AnnotationUtils.getAnnotation(clazz, BladeBootTest.class);
if (bladeBootTest == null) {
throw new BladeBootTestException(String.format("%s must be @BladeBootTest .", clazz));
}
String appName = bladeBootTest.appName();
String profile = bladeBootTest.profile();
boolean isLocalDev = BladeApplication.isLocalDev();
Properties props = System.getProperties();
props.setProperty("blade.env", profile);
props.setProperty("blade.name", appName);
props.setProperty("blade.is-local", String.valueOf(isLocalDev));
props.setProperty("blade.dev-mode", profile.equals(AppConstant.PROD_CODE) ? "false" : "true");
props.setProperty("blade.service.version", AppConstant.APPLICATION_VERSION);
props.setProperty("spring.application.name", appName);
props.setProperty("spring.profiles.active", profile);
props.setProperty("info.version", AppConstant.APPLICATION_VERSION);
props.setProperty("info.desc", appName);
props.setProperty("spring.cloud.nacos.discovery.server-addr", NacosConstant.NACOS_ADDR);
props.setProperty("spring.cloud.nacos.config.server-addr", NacosConstant.NACOS_ADDR);
props.setProperty("spring.cloud.nacos.config.prefix", NacosConstant.NACOS_CONFIG_PREFIX);
props.setProperty("spring.cloud.nacos.config.file-extension", NacosConstant.NACOS_CONFIG_FORMAT);
props.setProperty("spring.cloud.sentinel.transport.dashboard", SentinelConstant.SENTINEL_ADDR);
props.setProperty("spring.main.allow-bean-definition-overriding", "true");
// 加载自定义组件
if (bladeBootTest.enableLoader()) {
List<LauncherService> launcherList = new ArrayList<>();
SpringApplicationBuilder builder = new SpringApplicationBuilder(clazz);
ServiceLoader.load(LauncherService.class).forEach(launcherList::add);
launcherList.stream().sorted(Comparator.comparing(LauncherService::getOrder)).collect(Collectors.toList())
.forEach(launcherService -> launcherService.launcher(builder, appName, profile));
}
System.err.println(String.format("---[junit.test]:[%s]---启动中,读取到的环境变量:[%s]", appName, profile));
}
}

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.springblade</groupId>
<artifactId>blade-tool</artifactId>
<version>2.3.1</version>
<version>2.3.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -56,17 +56,17 @@
<artifactId>swagger-models</artifactId>
<version>${swagger.models.version}</version>
</dependency>
<!-- protostuff -->
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
<version>${protostuff.version}</version>
</dependency>
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>${protostuff.version}</version>
</dependency>
<!-- protostuff -->
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
<version>${protostuff.version}</version>
</dependency>
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>${protostuff.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,36 @@
/**
* Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.tool.utils;
/**
* char 常量池
*
* @author L.cm
*/
public interface CharPool {
char UPPER_A = 'A';
char LOWER_A = 'a';
char UPPER_Z = 'Z';
char LOWER_Z = 'z';
char DOT = '.';
char AT = '@';
char LEFT_BRACE = '{';
char RIGHT_BRACE = '}';
char LEFT_BRACKET = '(';
char RIGHT_BRACKET = ')';
}

View File

@ -1,119 +1,264 @@
/**
* Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.tool.utils;
import lombok.experimental.UtilityClass;
import org.springframework.util.Assert;
import java.text.ParseException;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalQuery;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
/**
* 日期工具类
*
* @author L.cm
*/
@UtilityClass
public class DateUtil {
public static final String PATTERN_DATETIME = "yyyy-MM-dd HH:mm:ss";
public static final String PATTERN_DATE = "yyyy-MM-dd";
public static final String PATTERN_TIME = "HH:mm:ss";
/**
* 设置年
* date 格式化
*/
public static final ConcurrentDateFormat DATETIME_FORMAT = ConcurrentDateFormat.of(PATTERN_DATETIME);
public static final ConcurrentDateFormat DATE_FORMAT = ConcurrentDateFormat.of(PATTERN_DATE);
public static final ConcurrentDateFormat TIME_FORMAT = ConcurrentDateFormat.of(PATTERN_TIME);
/**
* java 8 时间格式化
*/
public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATETIME);
public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATE);
public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(DateUtil.PATTERN_TIME);
/**
* 添加年
*
* @param date 时间
* @param amount 年数-1表示减少
* @param date 时间
* @param yearsToAdd 添加的年数
* @return 设置后的时间
*/
public static Date setYears(Date date, int amount) {
return set(date, Calendar.YEAR, amount);
public static Date plusYears(Date date, int yearsToAdd) {
return DateUtil.set(date, Calendar.YEAR, yearsToAdd);
}
/**
* 设置月
* 添加
*
* @param date 时间
* @param amount 月数-1表示减少
* @param date 时间
* @param monthsToAdd 添加的月数
* @return 设置后的时间
*/
public static Date setMonths(Date date, int amount) {
return set(date, Calendar.MONTH, amount);
public static Date plusMonths(Date date, int monthsToAdd) {
return DateUtil.set(date, Calendar.MONTH, monthsToAdd);
}
/**
* 设置
* 添加
*
* @param date 时间
* @param amount 周数-1表示减少
* @param date 时间
* @param weeksToAdd 添加的周数
* @return 设置后的时间
*/
public static Date setWeeks(Date date, int amount) {
return set(date, Calendar.WEEK_OF_YEAR, amount);
public static Date plusWeeks(Date date, int weeksToAdd) {
return DateUtil.plus(date, Period.ofWeeks(weeksToAdd));
}
/**
* 设置
* 添加
*
* @param date 时间
* @param amount 天数-1表示减少
* @param date 时间
* @param daysToAdd 添加的天数
* @return 设置后的时间
*/
public static Date setDays(Date date, int amount) {
return set(date, Calendar.DATE, amount);
public static Date plusDays(Date date, long daysToAdd) {
return DateUtil.plus(date, Duration.ofDays(daysToAdd));
}
/**
* 设置小时
* 添加小时
*
* @param date 时间
* @param amount 小时数-1表示减少
* @param date 时间
* @param hoursToAdd 添加的小时数
* @return 设置后的时间
*/
public static Date setHours(Date date, int amount) {
return set(date, Calendar.HOUR_OF_DAY, amount);
public static Date plusHours(Date date, long hoursToAdd) {
return DateUtil.plus(date, Duration.ofHours(hoursToAdd));
}
/**
* 设置分钟
* 添加分钟
*
* @param date 时间
* @param amount 分钟数-1表示减少
* @param date 时间
* @param minutesToAdd 添加的分钟数
* @return 设置后的时间
*/
public static Date setMinutes(Date date, int amount) {
return set(date, Calendar.MINUTE, amount);
public static Date plusMinutes(Date date, long minutesToAdd) {
return DateUtil.plus(date, Duration.ofMinutes(minutesToAdd));
}
/**
* 设置
* 添加
*
* @param date 时间
* @param amount 秒数-1表示减少
* @param date 时间
* @param secondsToAdd 添加的秒数
* @return 设置后的时间
*/
public static Date setSeconds(Date date, int amount) {
return set(date, Calendar.SECOND, amount);
public static Date plusSeconds(Date date, long secondsToAdd) {
return DateUtil.plus(date, Duration.ofSeconds(secondsToAdd));
}
/**
* 设置毫秒
* 添加毫秒
*
* @param date 时间
* @param amount 毫秒数-1表示减少
* @param date 时间
* @param millisToAdd 添加的毫秒数
* @return 设置后的时间
*/
public static Date setMilliseconds(Date date, int amount) {
return set(date, Calendar.MILLISECOND, amount);
public static Date plusMillis(Date date, long millisToAdd) {
return DateUtil.plus(date, Duration.ofMillis(millisToAdd));
}
/**
* 添加纳秒
*
* @param date 时间
* @param nanosToAdd 添加的纳秒数
* @return 设置后的时间
*/
public static Date plusNanos(Date date, long nanosToAdd) {
return DateUtil.plus(date, Duration.ofNanos(nanosToAdd));
}
/**
* 日期添加时间量
*
* @param date 时间
* @param amount 时间量
* @return 设置后的时间
*/
public static Date plus(Date date, TemporalAmount amount) {
Instant instant = date.toInstant();
return Date.from(instant.plus(amount));
}
/**
* 减少年
*
* @param date 时间
* @param years 减少的年数
* @return 设置后的时间
*/
public static Date minusYears(Date date, int years) {
return DateUtil.set(date, Calendar.YEAR, -years);
}
/**
* 减少月
*
* @param date 时间
* @param months 减少的月数
* @return 设置后的时间
*/
public static Date minusMonths(Date date, int months) {
return DateUtil.set(date, Calendar.MONTH, -months);
}
/**
* 减少周
*
* @param date 时间
* @param weeks 减少的周数
* @return 设置后的时间
*/
public static Date minusWeeks(Date date, int weeks) {
return DateUtil.minus(date, Period.ofWeeks(weeks));
}
/**
* 减少天
*
* @param date 时间
* @param days 减少的天数
* @return 设置后的时间
*/
public static Date minusDays(Date date, long days) {
return DateUtil.minus(date, Duration.ofDays(days));
}
/**
* 减少小时
*
* @param date 时间
* @param hours 减少的小时数
* @return 设置后的时间
*/
public static Date minusHours(Date date, long hours) {
return DateUtil.minus(date, Duration.ofHours(hours));
}
/**
* 减少分钟
*
* @param date 时间
* @param minutes 减少的分钟数
* @return 设置后的时间
*/
public static Date minusMinutes(Date date, long minutes) {
return DateUtil.minus(date, Duration.ofMinutes(minutes));
}
/**
* 减少秒
*
* @param date 时间
* @param seconds 减少的秒数
* @return 设置后的时间
*/
public static Date minusSeconds(Date date, long seconds) {
return DateUtil.minus(date, Duration.ofSeconds(seconds));
}
/**
* 减少毫秒
*
* @param date 时间
* @param millis 减少的毫秒数
* @return 设置后的时间
*/
public static Date minusMillis(Date date, long millis) {
return DateUtil.minus(date, Duration.ofMillis(millis));
}
/**
* 减少纳秒
*
* @param date 时间
* @param nanos 减少的纳秒数
* @return 设置后的时间
*/
public static Date minusNanos(Date date, long nanos) {
return DateUtil.minus(date, Duration.ofNanos(nanos));
}
/**
* 日期减少时间量
*
* @param date 时间
* @param amount 时间量
* @return 设置后的时间
*/
public static Date minus(Date date, TemporalAmount amount) {
Instant instant = date.toInstant();
return Date.from(instant.minus(amount));
}
/**
@ -133,14 +278,6 @@ public class DateUtil {
return c.getTime();
}
public static final String PATTERN_DATETIME = "yyyy-MM-dd HH:mm:ss";
public static final String PATTERN_DATE = "yyyy-MM-dd";
public static final String PATTERN_TIME = "HH:mm:ss";
public static final ConcurrentDateFormat DATETIME_FORMAT = ConcurrentDateFormat.of(PATTERN_DATETIME);
public static final ConcurrentDateFormat DATE_FORMAT = ConcurrentDateFormat.of(PATTERN_DATE);
public static final ConcurrentDateFormat TIME_FORMAT = ConcurrentDateFormat.of(PATTERN_TIME);
/**
* 日期时间格式化
*
@ -182,6 +319,47 @@ public class DateUtil {
return ConcurrentDateFormat.of(pattern).format(date);
}
/**
* java8 日期时间格式化
*
* @param temporal 时间
* @return 格式化后的时间
*/
public static String formatDateTime(TemporalAccessor temporal) {
return DATETIME_FORMATTER.format(temporal);
}
/**
* java8 日期时间格式化
*
* @param temporal 时间
* @return 格式化后的时间
*/
public static String formatDate(TemporalAccessor temporal) {
return DATE_FORMATTER.format(temporal);
}
/**
* java8 时间格式化
*
* @param temporal 时间
* @return 格式化后的时间
*/
public static String formatTime(TemporalAccessor temporal) {
return TIME_FORMATTER.format(temporal);
}
/**
* java8 日期格式化
*
* @param temporal 时间
* @param pattern 表达式
* @return 格式化后的时间
*/
public static String format(TemporalAccessor temporal, String pattern) {
return DateTimeFormatter.ofPattern(pattern).format(temporal);
}
/**
* 将字符串转换为时间
*
@ -213,4 +391,196 @@ public class DateUtil {
}
}
/**
* 将字符串转换为时间
*
* @param dateStr 时间字符串
* @param pattern 表达式
* @return 时间
*/
public static <T> T parse(String dateStr, String pattern, TemporalQuery<T> query) {
return DateTimeFormatter.ofPattern(pattern).parse(dateStr, query);
}
/**
* 时间转 Instant
*
* @param dateTime 时间
* @return Instant
*/
public static Instant toInstant(LocalDateTime dateTime) {
return dateTime.atZone(ZoneId.systemDefault()).toInstant();
}
/**
* Instant 时间
*
* @param instant Instant
* @return Instant
*/
public static LocalDateTime toDateTime(Instant instant) {
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
}
/**
* 转换成 date
*
* @param dateTime LocalDateTime
* @return Date
*/
public static Date toDate(LocalDateTime dateTime) {
return Date.from(DateUtil.toInstant(dateTime));
}
/**
* 转换成 date
*
* @param localDate LocalDate
* @return Date
*/
public static Date toDate(final LocalDate localDate) {
return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
}
/**
* Converts local date time to Calendar.
*/
public static Calendar toCalendar(final LocalDateTime localDateTime) {
return GregorianCalendar.from(ZonedDateTime.of(localDateTime, ZoneId.systemDefault()));
}
/**
* localDateTime 转换成毫秒数
*
* @param localDateTime LocalDateTime
* @return long
*/
public static long toMilliseconds(final LocalDateTime localDateTime) {
return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}
/**
* localDate 转换成毫秒数
*
* @param localDate LocalDate
* @return long
*/
public static long toMilliseconds(LocalDate localDate) {
return toMilliseconds(localDate.atStartOfDay());
}
/**
* 转换成java8 时间
*
* @param calendar 日历
* @return LocalDateTime
*/
public static LocalDateTime fromCalendar(final Calendar calendar) {
TimeZone tz = calendar.getTimeZone();
ZoneId zid = tz == null ? ZoneId.systemDefault() : tz.toZoneId();
return LocalDateTime.ofInstant(calendar.toInstant(), zid);
}
/**
* 转换成java8 时间
*
* @param instant Instant
* @return LocalDateTime
*/
public static LocalDateTime fromInstant(final Instant instant) {
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
}
/**
* 转换成java8 时间
*
* @param date Date
* @return LocalDateTime
*/
public static LocalDateTime fromDate(final Date date) {
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
}
/**
* 转换成java8 时间
*
* @param milliseconds 毫秒数
* @return LocalDateTime
*/
public static LocalDateTime fromMilliseconds(final long milliseconds) {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(milliseconds), ZoneId.systemDefault());
}
/**
* 比较2个时间差跨度比较小
*
* @param startInclusive 开始时间
* @param endExclusive 结束时间
* @return 时间间隔
*/
public static Duration between(Temporal startInclusive, Temporal endExclusive) {
return Duration.between(startInclusive, endExclusive);
}
/**
* 比较2个时间差跨度比较大年月日为单位
*
* @param startDate 开始时间
* @param endDate 结束时间
* @return 时间间隔
*/
public static Period between(LocalDate startDate, LocalDate endDate) {
return Period.between(startDate, endDate);
}
/**
* 比较2个 时间差
*
* @param startDate 开始时间
* @param endDate 结束时间
* @return 时间间隔
*/
public static Duration between(Date startDate, Date endDate) {
return Duration.between(startDate.toInstant(), endDate.toInstant());
}
/**
* 将秒数转换为日时分秒
*
* @param second 秒数
* @return 时间
*/
public static String secondToTime(Long second) {
// 判断是否为空
if (second == null || second == 0L) {
return StringPool.EMPTY;
}
//转换天数
long days = second / 86400;
//剩余秒数
second = second % 86400;
//转换小时
long hours = second / 3600;
//剩余秒数
second = second % 3600;
//转换分钟
long minutes = second / 60;
//剩余秒数
second = second % 60;
if (days > 0) {
return StringUtil.format("{}天{}小时{}分{}秒", days, hours, minutes, second);
} else {
return StringUtil.format("{}小时{}分{}秒", hours, minutes, second);
}
}
/**
* 获取今天的日期
*
* @return 时间
*/
public static String today() {
return format(new Date(), "yyyyMMdd");
}
}

View File

@ -0,0 +1,385 @@
/*
* Copyright (c) 2018-2028, DreamLu All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: DreamLu 卢春梦 (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import lombok.experimental.UtilityClass;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.PatternMatchUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
/**
* 文件工具类
*
* @author L.cm
*/
@UtilityClass
public class FileUtil extends org.springframework.util.FileCopyUtils {
/**
* 默认为true
*
* @author L.cm
*/
public static class TrueFilter implements FileFilter, Serializable {
private static final long serialVersionUID = -6420452043795072619L;
public final static TrueFilter TRUE = new TrueFilter();
@Override
public boolean accept(File pathname) {
return true;
}
}
/**
* 扫描目录下的文件
*
* @param path 路径
* @return 文件集合
*/
public static List<File> list(String path) {
File file = new File(path);
return list(file, TrueFilter.TRUE);
}
/**
* 扫描目录下的文件
*
* @param path 路径
* @param fileNamePattern 文件名 *
* @return 文件集合
*/
public static List<File> list(String path, final String fileNamePattern) {
File file = new File(path);
return list(file, pathname -> {
String fileName = pathname.getName();
return PatternMatchUtils.simpleMatch(fileNamePattern, fileName);
});
}
/**
* 扫描目录下的文件
*
* @param path 路径
* @param filter 文件过滤
* @return 文件集合
*/
public static List<File> list(String path, FileFilter filter) {
File file = new File(path);
return list(file, filter);
}
/**
* 扫描目录下的文件
*
* @param file 文件
* @return 文件集合
*/
public static List<File> list(File file) {
List<File> fileList = new ArrayList<>();
return list(file, fileList, TrueFilter.TRUE);
}
/**
* 扫描目录下的文件
*
* @param file 文件
* @param fileNamePattern Spring AntPathMatcher 规则
* @return 文件集合
*/
public static List<File> list(File file, final String fileNamePattern) {
List<File> fileList = new ArrayList<>();
return list(file, fileList, pathname -> {
String fileName = pathname.getName();
return PatternMatchUtils.simpleMatch(fileNamePattern, fileName);
});
}
/**
* 扫描目录下的文件
*
* @param file 文件
* @param filter 文件过滤
* @return 文件集合
*/
public static List<File> list(File file, FileFilter filter) {
List<File> fileList = new ArrayList<>();
return list(file, fileList, filter);
}
/**
* 扫描目录下的文件
*
* @param file 文件
* @param filter 文件过滤
* @return 文件集合
*/
private static List<File> list(File file, List<File> fileList, FileFilter filter) {
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files != null) {
for (File f : files) {
list(f, fileList, filter);
}
}
} else {
// 过滤文件
boolean accept = filter.accept(file);
if (file.exists() && accept) {
fileList.add(file);
}
}
return fileList;
}
/**
* 获取文件后缀名
* @param fullName 文件全名
* @return {String}
*/
public static String getFileExtension(String fullName) {
Assert.notNull(fullName, "file fullName is null.");
String fileName = new File(fullName).getName();
int dotIndex = fileName.lastIndexOf('.');
return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1);
}
/**
* 获取文件名去除后缀名
* @param file 文件
* @return {String}
*/
public static String getNameWithoutExtension(String file) {
Assert.notNull(file, "file is null.");
String fileName = new File(file).getName();
int dotIndex = fileName.lastIndexOf(CharPool.DOT);
return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex);
}
/**
* Returns the path to the system temporary directory.
*
* @return the path to the system temporary directory.
*/
public static String getTempDirPath() {
return System.getProperty("java.io.tmpdir");
}
/**
* Returns a {@link File} representing the system temporary directory.
*
* @return the system temporary directory.
*/
public static File getTempDir() {
return new File(getTempDirPath());
}
/**
* Reads the contents of a file into a String.
* The file is always closed.
*
* @param file the file to read, must not be {@code null}
* @return the file contents, never {@code null}
*/
public static String readToString(final File file) {
return readToString(file, Charsets.UTF_8);
}
/**
* Reads the contents of a file into a String.
* The file is always closed.
*
* @param file the file to read, must not be {@code null}
* @param encoding the encoding to use, {@code null} means platform default
* @return the file contents, never {@code null}
*/
public static String readToString(final File file, final Charset encoding) {
try (InputStream in = Files.newInputStream(file.toPath())) {
return IoUtil.toString(in, encoding);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* Reads the contents of a file into a String.
* The file is always closed.
*
* @param file the file to read, must not be {@code null}
* @return the file contents, never {@code null}
*/
public static byte[] readToByteArray(final File file) {
try (InputStream in = Files.newInputStream(file.toPath())) {
return IoUtil.toByteArray(in);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* Writes a String to a file creating the file if it does not exist.
*
* @param file the file to write
* @param data the content to write to the file
*/
public static void writeToFile(final File file, final String data) {
writeToFile(file, data, Charsets.UTF_8, false);
}
/**
* Writes a String to a file creating the file if it does not exist.
*
* @param file the file to write
* @param data the content to write to the file
* @param append if {@code true}, then the String will be added to the
* end of the file rather than overwriting
*/
public static void writeToFile(final File file, final String data, final boolean append){
writeToFile(file, data, Charsets.UTF_8, append);
}
/**
* Writes a String to a file creating the file if it does not exist.
*
* @param file the file to write
* @param data the content to write to the file
* @param encoding the encoding to use, {@code null} means platform default
*/
public static void writeToFile(final File file, final String data, final Charset encoding) {
writeToFile(file, data, encoding, false);
}
/**
* Writes a String to a file creating the file if it does not exist.
*
* @param file the file to write
* @param data the content to write to the file
* @param encoding the encoding to use, {@code null} means platform default
* @param append if {@code true}, then the String will be added to the
* end of the file rather than overwriting
*/
public static void writeToFile(final File file, final String data, final Charset encoding, final boolean append) {
try (OutputStream out = new FileOutputStream(file, append)) {
IoUtil.write(data, out, encoding);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 转成file
* @param multipartFile MultipartFile
* @param file File
*/
public static void toFile(MultipartFile multipartFile, final File file) {
try {
FileUtil.toFile(multipartFile.getInputStream(), file);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 转成file
* @param in InputStream
* @param file File
*/
public static void toFile(InputStream in, final File file) {
try (OutputStream out = new FileOutputStream(file)) {
FileUtil.copy(in, out);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* Moves a file.
* <p>
* When the destination file is on another file system, do a "copy and delete".
*
* @param srcFile the file to be moved
* @param destFile the destination file
* @throws NullPointerException if source or destination is {@code null}
* @throws IOException if source or destination is invalid
* @throws IOException if an IO error occurs moving the file
*/
public static void moveFile(final File srcFile, final File destFile) throws IOException {
Assert.notNull(srcFile, "Source must not be null");
Assert.notNull(destFile, "Destination must not be null");
if (!srcFile.exists()) {
throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
}
if (srcFile.isDirectory()) {
throw new IOException("Source '" + srcFile + "' is a directory");
}
if (destFile.exists()) {
throw new IOException("Destination '" + destFile + "' already exists");
}
if (destFile.isDirectory()) {
throw new IOException("Destination '" + destFile + "' is a directory");
}
final boolean rename = srcFile.renameTo(destFile);
if (!rename) {
FileUtil.copy(srcFile, destFile);
if (!srcFile.delete()) {
FileUtil.deleteQuietly(destFile);
throw new IOException("Failed to delete original file '" + srcFile + "' after copy to '" + destFile + "'");
}
}
}
/**
* Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories.
* <p>
* The difference between File.delete() and this method are:
* <ul>
* <li>A directory to be deleted does not have to be empty.</li>
* <li>No exceptions are thrown when a file or directory cannot be deleted.</li>
* </ul>
*
* @param file file or directory to delete, can be {@code null}
* @return {@code true} if the file or directory was deleted, otherwise
* {@code false}
*/
public static boolean deleteQuietly(@Nullable final File file) {
if (file == null) {
return false;
}
try {
if (file.isDirectory()) {
FileSystemUtils.deleteRecursively(file);
}
} catch (final Exception ignored) {
}
try {
return file.delete();
} catch (final Exception ignored) {
return false;
}
}
}

View File

@ -27,7 +27,7 @@ public interface StringPool {
String AT = "@";
String ASTERISK = "*";
String STAR = ASTERISK;
char SLASH = '/';
String SLASH = "/";
char BACK_SLASH = '\\';
String DOUBLE_SLASH = "#//";
String COLON = ":";

10
pom.xml
View File

@ -5,7 +5,7 @@
<groupId>org.springblade</groupId>
<artifactId>blade-tool</artifactId>
<version>2.3.1</version>
<version>2.3.2</version>
<packaging>pom</packaging>
<name>blade-tool</name>
<description>
@ -36,19 +36,19 @@
</scm>
<properties>
<blade.tool.version>2.3.1</blade.tool.version>
<blade.tool.version>2.3.2</blade.tool.version>
<java.version>1.8</java.version>
<maven.plugin.version>3.8.0</maven.plugin.version>
<swagger.version>2.9.2</swagger.version>
<swagger.models.version>1.5.21</swagger.models.version>
<swagger.bootstrapui.version>1.9.3</swagger.bootstrapui.version>
<swagger.bootstrapui.version>1.9.4</swagger.bootstrapui.version>
<mybatis.plus.version>3.1.0</mybatis.plus.version>
<curator.framework.version>4.0.1</curator.framework.version>
<protostuff.version>1.6.0</protostuff.version>
<disruptor.version>3.4.2</disruptor.version>
<spring.boot.admin.version>2.1.4</spring.boot.admin.version>
<mica.auto.version>1.0.1</mica.auto.version>
<mica.auto.version>1.1.0</mica.auto.version>
<alibaba.cloud.version>0.9.0.RELEASE</alibaba.cloud.version>
<spring.boot.version>2.1.5.RELEASE</spring.boot.version>
@ -68,7 +68,9 @@
<module>blade-core-mybatis</module>
<module>blade-core-secure</module>
<module>blade-core-swagger</module>
<module>blade-core-test</module>
<module>blade-core-tool</module>
<module>blade-core-oss</module>
</modules>
<dependencyManagement>