├── docs ├── .nojekyll ├── _sidebar.md ├── _navbar.md ├── index.html ├── _coverpage.md └── README.md ├── src └── main │ ├── java │ └── com │ │ └── zhouzifei │ │ ├── .DS_Store │ │ └── tool │ │ ├── config │ │ ├── .DS_Store │ │ ├── FileProperties.java │ │ ├── GithubFileProperties.java │ │ ├── FileAutoConfiguration.java │ │ ├── LocalFileProperties.java │ │ ├── SmmsFileProperties.java │ │ ├── UpaiFileProperties.java │ │ ├── FastDfsFileProperties.java │ │ ├── BosFileProperties.java │ │ ├── QiniuFileProperties.java │ │ ├── ScpFileProperties.java │ │ ├── HuaweiFileProperties.java │ │ ├── AwsFileProperties.java │ │ ├── QcloudFileProperties.java │ │ ├── OssFileProperties.java │ │ ├── ReadProper.java │ │ └── SimpleFsProperties.java │ │ ├── entity │ │ ├── .DS_Store │ │ ├── MetaDataRequest.java │ │ └── FileListRequesr.java │ │ ├── media │ │ └── file │ │ │ └── service │ │ │ └── .DS_Store │ │ ├── dto │ │ ├── FileResult.java │ │ ├── HttpResponses.java │ │ ├── CheckFileResult.java │ │ ├── AbstractEntity.java │ │ ├── SmmFileList.java │ │ ├── GithubFileList.java │ │ ├── M3u8DTO.java │ │ ├── BucketEntity.java │ │ ├── ResponseVO.java │ │ └── VirtualFile.java │ │ ├── annotation │ │ ├── FileTypeName.java │ │ └── FilePropertiesConfig.java │ │ ├── service │ │ ├── DownloadListener.java │ │ ├── ApiClient.java │ │ └── FileUploader.java │ │ ├── listener │ │ ├── UploadCallback.java │ │ ├── ProgressListener.java │ │ ├── uploadProgressListener.java │ │ ├── UploadStream.java │ │ └── a123.java │ │ ├── common │ │ ├── CommonWebConfig.java │ │ ├── fastdfs │ │ │ ├── DownloadCallback.java │ │ │ ├── UploadCallback.java │ │ │ ├── common │ │ │ │ ├── NameValuePair.java │ │ │ │ └── IniFileReader.java │ │ │ ├── ProtoStructDecoder.java │ │ │ ├── pool │ │ │ │ ├── ConnectionFactory.java │ │ │ │ ├── ConnectionPool.java │ │ │ │ ├── Connection.java │ │ │ │ └── ConnectionManager.java │ │ │ ├── DownloadStream.java │ │ │ ├── TrackerServer.java │ │ │ ├── ServerInfo.java │ │ │ ├── StorageServer.java │ │ │ ├── UploadStream.java │ │ │ ├── StructBase.java │ │ │ ├── TrackerGroup.java │ │ │ ├── FileInfo.java │ │ │ └── StructGroupStat.java │ │ ├── ServiceException.java │ │ ├── SimpleCache.java │ │ ├── MimeTypeUtils.java │ │ ├── WebExceptionHandler.java │ │ ├── Response.java │ │ └── upaiyun │ │ │ └── UpYunUtils.java │ │ ├── consts │ │ ├── ResponseStatus.java │ │ ├── UpLoadConstant.java │ │ └── StorageTypeConst.java │ │ ├── util │ │ ├── MediaFormat.java │ │ ├── RandomsUtil.java │ │ ├── HttpData.java │ │ ├── StreamUtil.java │ │ ├── ThreadManager.java │ │ ├── PatternPool.java │ │ ├── StringUtils.java │ │ └── Base64Coder.java │ │ └── fileClient │ │ ├── BaiduBosApiClient.java │ │ ├── LocalApiClient.java │ │ ├── HuaweiCloudOssApiClient.java │ │ ├── UpaiyunOssApiClient.java │ │ ├── QiniuApiClient.java │ │ ├── QCloudOssApiClient.java │ │ ├── BaseApiClient.java │ │ ├── GithubApiClient.java │ │ └── SmMsApiClient.java │ └── resources │ └── META-INF │ └── spring.factories └── .gitignore /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/_sidebar.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | * [首页](README.md) -------------------------------------------------------------------------------- /docs/_navbar.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | * [En](/) 4 | * [中文](/zh-cn/) -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shengdingbox/simpleFS/HEAD/src/main/java/com/zhouzifei/.DS_Store -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/config/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shengdingbox/simpleFS/HEAD/src/main/java/com/zhouzifei/tool/config/.DS_Store -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/entity/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shengdingbox/simpleFS/HEAD/src/main/java/com/zhouzifei/tool/entity/.DS_Store -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | com.zhouzifei.tool.config.FileAutoConfiguration -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/media/file/service/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shengdingbox/simpleFS/HEAD/src/main/java/com/zhouzifei/tool/media/file/service/.DS_Store -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/config/FileProperties.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.config; 2 | 3 | /** 4 | * @author 周子斐 5 | * @date 2022/4/30 6 | * @Description 7 | */ 8 | public class FileProperties { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/dto/FileResult.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.dto; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | @Data 8 | public class FileResult implements Serializable { 9 | //文件名 10 | private String url; 11 | //文件md5 12 | private String md5; 13 | //文件名称 14 | private String name; 15 | //文件大小 16 | private Long lenght; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/annotation/FileTypeName.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.annotation; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.lang.annotation.*; 6 | 7 | /** 8 | * @author Dabao 9 | */ 10 | @Target({ElementType.TYPE}) 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @Documented 13 | @Component 14 | public @interface FileTypeName{ 15 | String value() default ""; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/dto/HttpResponses.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.dto; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | /** 11 | * @author 周子斐 12 | * @date 2021/2/1 13 | * @Description 14 | */ 15 | @Data 16 | public class HttpResponses implements Serializable { 17 | Map> map = new HashMap<>(); 18 | 19 | String body = ""; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/service/DownloadListener.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.media.file.service; 2 | 3 | /** 4 | * m3u8下载监听器 5 | * @author 周子斐 (17600004572@163.com) 6 | * @date 2020/3/8 7 | */ 8 | public interface DownloadListener { 9 | 10 | void prepare(int step, String msg); 11 | 12 | void start(int sum); 13 | 14 | void process(String downloadUrl, int finished, int sum, float percent); 15 | 16 | void speed(String speedPerSecond); 17 | 18 | void end(); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | .mvn/** 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | 23 | ### NetBeans ### 24 | /nbproject/private/ 25 | /nbbuild/ 26 | /dist/ 27 | /nbdist/ 28 | /.nb-gradle/ 29 | build/ 30 | 31 | ### VS Code ### 32 | .vscode/ 33 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/listener/UploadCallback.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.listener; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | 6 | /** 7 | * upload file callback interface 8 | * 9 | * @author Happy Fish / YuQing 10 | * @version Version 1.0 11 | */ 12 | public interface UploadCallback { 13 | /** 14 | * 发送文件内容回调函数,文件上传时只调用一次 15 | * @param out 输出流写入文件内容 16 | * @return 0成功,失败则返回none 0(errno) 17 | */ 18 | public int send(OutputStream out) throws IOException; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/CommonWebConfig.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | @Configuration 8 | public class CommonWebConfig implements WebMvcConfigurer { 9 | public CommonWebConfig() { 10 | } 11 | 12 | @Bean 13 | public WebExceptionHandler exceptionHandler() { 14 | return new WebExceptionHandler(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/entity/MetaDataRequest.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.entity; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * 9 | * @author 周子斐 (17600004572@163.com) 10 | * @version 1.0 11 | * @remark 2019年7月16日 12 | * @since 1.0 13 | */ 14 | @Data 15 | public class MetaDataRequest implements Serializable { 16 | private Integer chunk; 17 | private String fileMd5; 18 | private Integer chunks; 19 | private Integer chunkSize; 20 | private Integer chunkCurr; 21 | private String name; 22 | private Long size; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/dto/CheckFileResult.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.dto; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | 8 | //上传前文件检测 9 | @Data 10 | public class CheckFileResult implements Serializable { 11 | private String fileMd5; 12 | //0:锁未占用,1:锁占用 13 | private Integer lock; 14 | //文件分块数量 15 | private Integer chunkNum; 16 | //每块文件大小 17 | private Integer chunkSize; 18 | //当前已传输到第几块 19 | private Integer chunkCurr; 20 | //当前文件总大小 21 | private Long totalSize; 22 | //访问路径 23 | private String viewPath; 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/listener/ProgressListener.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.listener; 2 | 3 | import com.zhouzifei.tool.dto.VirtualFile; 4 | 5 | /** 6 | * @author 周子斐 7 | * @date 2021/2/3 8 | * @Description 9 | */ 10 | public interface ProgressListener { 11 | 12 | /** 13 | * 开始 14 | * @param msg 15 | */ 16 | void start(String msg); 17 | 18 | /** 19 | * 处理中 20 | * @param finished 目前进度 21 | * @param sum 总进度 22 | */ 23 | void process(int finished, int sum); 24 | 25 | /** 26 | * 结束 27 | */ 28 | void end(VirtualFile virtualFile); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/fastdfs/DownloadCallback.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common.fastdfs; 2 | 3 | /** 4 | * Download file callback interface 5 | * 6 | * @author Happy Fish / YuQing 7 | * @version Version 1.4 8 | */ 9 | public interface DownloadCallback { 10 | /** 11 | * recv file content callback function, may be called more than once when the file downloaded 12 | * 13 | * @param file_size file size 14 | * @param data data buff 15 | * @param bytes data bytes 16 | * @return 0 success, return none zero(errno) if fail 17 | */ 18 | public int recv(long file_size, byte[] data, int bytes); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/fastdfs/UploadCallback.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common.fastdfs; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | 6 | /** 7 | * upload file callback interface 8 | * 9 | * @author Happy Fish / YuQing 10 | * @version Version 1.0 11 | */ 12 | public interface UploadCallback { 13 | /** 14 | * send file content callback function, be called only once when the file uploaded 15 | * 16 | * @param out output stream for writing file content 17 | * @return 0 success, return none zero(errno) if fail 18 | */ 19 | public int send(OutputStream out) throws IOException; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/dto/AbstractEntity.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.dto; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * 10 | * @author 周子斐 (17600004572@163.com) 11 | * @version 1.0 12 | * @remark 2019年7月16日 13 | * @since 1.0 14 | */ 15 | @Data 16 | @EqualsAndHashCode(callSuper = false) 17 | public abstract class AbstractEntity implements Serializable { 18 | private String bucketName; 19 | 20 | public AbstractEntity() { 21 | } 22 | 23 | public AbstractEntity(String bucketName) { 24 | this.bucketName = bucketName; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/dto/SmmFileList.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.dto; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * @author 周子斐 9 | * @date 2022/3/29 10 | * @Description 11 | */ 12 | @Data 13 | public class SmmFileList implements Serializable { 14 | private String width; 15 | private String height; 16 | private String filename; 17 | private String storename; 18 | private String size; 19 | private String path; 20 | private String hash; 21 | private String created_at; 22 | private String url; 23 | private String delete; 24 | private String page; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/dto/GithubFileList.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.dto; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | import java.util.Map; 7 | 8 | /** 9 | * @author 周子斐 10 | * @date 2022/3/29 11 | * @Description 12 | */ 13 | @Data 14 | public class GithubFileList implements Serializable { 15 | private String name; 16 | private String path; 17 | private String sha; 18 | private String size; 19 | private String url; 20 | private String html_url; 21 | private String git_url; 22 | private String download_url; 23 | private String type; 24 | private Map _links; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/entity/FileListRequesr.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.entity; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author 周子斐 7 | * @date 2022/1/12 8 | * @Description 9 | */ 10 | @Data 11 | public class FileListRequesr { 12 | 13 | private String prefix; 14 | //对文件名称进行分组的一个字符。所有名称包含指定的前缀且第一次出现delimiter字符之间的文件作为一组元素(commonPrefixes)。 setDelimiter( 15 | private String delimiter; 16 | private String marker; 17 | private String fold; 18 | //限定此次列举文件的最大个数。默认值为100,最大值为1000。 setMaxKeys( 19 | Integer size; 20 | Integer page; 21 | //请求响应体中文件名称采用的编码方式,目前仅支持URL。 22 | private String encodingType; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/config/GithubFileProperties.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.config; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * @author 周子斐 (17600004572@163.com) 10 | * @remark 2021/1/26 11 | * @Description 12 | */ 13 | @Component 14 | @Data 15 | @EqualsAndHashCode(callSuper = true) 16 | @ConfigurationProperties(prefix = "simple-fs.github") 17 | public class GithubFileProperties extends FileProperties{ 18 | private String token; 19 | private String user; 20 | private String repository; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/config/FileAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.config; 2 | 3 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | /** 8 | * image自动装配 9 | * @author 闫明辉(mh.yan02@zuche.com) 10 | * @date 2021/01/25 15:32 11 | */ 12 | @Configuration 13 | @EnableConfigurationProperties(SimpleFsProperties.class) 14 | public class FileAutoConfiguration { 15 | 16 | @Bean 17 | public SimpleFsProperties simpleFsProperties(SimpleFsProperties simpleFsProperties){ 18 | return simpleFsProperties; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/config/LocalFileProperties.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.config; 2 | 3 | import com.zhouzifei.tool.annotation.FileTypeName; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * @author 周子斐 (17600004572@163.com) 11 | * @remark 2021/1/26 12 | * @Description 13 | */ 14 | @Component 15 | @Data 16 | @EqualsAndHashCode(callSuper = true) 17 | @ConfigurationProperties(prefix = "simple-fs.local") 18 | public class LocalFileProperties extends FileProperties{ 19 | private String domainUrl; 20 | private String localFilePath; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/config/SmmsFileProperties.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.config; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * @author 周子斐 (17600004572@163.com) 10 | * @remark 2021/1/26 11 | * @Description 12 | */ 13 | @Component 14 | @Data 15 | @EqualsAndHashCode(callSuper = true) 16 | @ConfigurationProperties(prefix = "simple-fs.smms") 17 | public class SmmsFileProperties extends FileProperties{ 18 | private String domainUrl; 19 | private String token; 20 | private String userName; 21 | private String passWord; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/config/UpaiFileProperties.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.config; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * @author 周子斐 (17600004572@163.com) 10 | * @remark 2021/1/26 11 | * @Description 12 | */ 13 | @Component 14 | @Data 15 | @EqualsAndHashCode(callSuper = true) 16 | @ConfigurationProperties(prefix = "simple-fs.upai") 17 | public class UpaiFileProperties extends FileProperties{ 18 | private String domainUrl; 19 | private String bucketName; 20 | private String userName; 21 | private String passWord; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/config/FastDfsFileProperties.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.config; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * @author 周子斐 (17600004572@163.com) 10 | * @remark 2021/1/26 11 | * @Description 12 | */ 13 | @Component 14 | @Data 15 | @EqualsAndHashCode(callSuper = true) 16 | @ConfigurationProperties(prefix = "simple-fs.fast") 17 | public class FastDfsFileProperties extends FileProperties{ 18 | private String domainUrl; 19 | private String serverUrl; 20 | private String userName; 21 | private String passWord; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/config/BosFileProperties.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.config; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * @author 周子斐 (17600004572@163.com) 10 | * @remark 2021/1/26 11 | * @Description 12 | */ 13 | @Component 14 | @Data 15 | @EqualsAndHashCode(callSuper = true) 16 | @ConfigurationProperties(prefix = "simple-fs.bos") 17 | public class BosFileProperties extends FileProperties{ 18 | private String domainUrl; 19 | private String endpoint; 20 | private String bucketName; 21 | private String accessKey; 22 | private String secretKey; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/config/QiniuFileProperties.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.config; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * @author 周子斐 (17600004572@163.com) 10 | * @remark 2021/1/26 11 | * @Description 12 | */ 13 | @Component 14 | @Data 15 | @EqualsAndHashCode(callSuper = true) 16 | @ConfigurationProperties(prefix = "simple-fs.qiniu") 17 | public class QiniuFileProperties extends FileProperties{ 18 | private String domainUrl; 19 | private String bucketName; 20 | private String accessKey; 21 | private String secretKey; 22 | private String region; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/config/ScpFileProperties.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.config; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * @author 周子斐 (17600004572@163.com) 10 | * @remark 2021/1/26 11 | * @Description 12 | */ 13 | @Component 14 | @Data 15 | @EqualsAndHashCode(callSuper = true) 16 | @ConfigurationProperties(prefix = "simple-fs.scp") 17 | public class ScpFileProperties extends FileProperties{ 18 | private String domainUrl; 19 | private String localFilePath; 20 | private String remotePath; 21 | private String userName; 22 | private String passWord; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/config/HuaweiFileProperties.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.config; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * @author 周子斐 (17600004572@163.com) 10 | * @remark 2021/1/26 11 | * @Description 12 | */ 13 | @Component 14 | @Data 15 | @EqualsAndHashCode(callSuper = true) 16 | @ConfigurationProperties(prefix = "simple-fs.huawei") 17 | public class HuaweiFileProperties extends FileProperties{ 18 | private String domainUrl; 19 | private String endpoint; 20 | private String bucketName; 21 | private String accessKey; 22 | private String secretKey; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/config/AwsFileProperties.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.config; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * @author 周子斐 (17600004572@163.com) 10 | * @remark 2021/1/26 11 | * @Description 12 | */ 13 | @EqualsAndHashCode(callSuper = true) 14 | @Component 15 | @Data 16 | @ConfigurationProperties(prefix = "simple-fs.aws") 17 | public class AwsFileProperties extends FileProperties{ 18 | private String domainUrl; 19 | private String accessKey; 20 | private String secretKey; 21 | private String endpoint; 22 | private String region; 23 | private String bucketName; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/config/QcloudFileProperties.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.config; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * @author 周子斐 (17600004572@163.com) 10 | * @remark 2021/1/26 11 | * @Description 12 | */ 13 | @Component 14 | @Data 15 | @EqualsAndHashCode(callSuper = true) 16 | @ConfigurationProperties(prefix = "simple-fs.qcloud") 17 | public class QcloudFileProperties extends FileProperties{ 18 | private String domainUrl; 19 | private String endpoint; 20 | private String bucketName; 21 | private String accessKey; 22 | private String secretKey; 23 | private String region; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/dto/M3u8DTO.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.dto; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * @author 周子斐 10 | * @date 2021/1/28 11 | * @Description 12 | */ 13 | @Data 14 | @Builder 15 | public class M3u8DTO implements Serializable { 16 | /** 17 | * 文件保存的路径 18 | */ 19 | private String filePath; 20 | /** 21 | * 要下载的index.m3u8的url 22 | */ 23 | private String m3u8Url; 24 | /** 25 | * //保存的视频文件名,不带后缀名 26 | */ 27 | private String fileName; 28 | /** 29 | * //线程数 30 | */ 31 | private String threadCount; 32 | /** 33 | * //重试次数 34 | */ 35 | private String retryCount; 36 | /** 37 | * //连接超时时间(单位:毫秒) 38 | */ 39 | private String timeout; 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/config/OssFileProperties.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.config; 2 | 3 | import com.zhouzifei.tool.annotation.FileTypeName; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * @author 周子斐 (17600004572@163.com) 11 | * @remark 2021/1/26 12 | * @Description 13 | */ 14 | @EqualsAndHashCode(callSuper = true) 15 | @Component 16 | @Data 17 | @ConfigurationProperties(prefix = "simple-fs.oss") 18 | @FileTypeName("阿里云OSS") 19 | public class OssFileProperties extends FileProperties{ 20 | private String domainUrl; 21 | private String accessKey; 22 | private String secretKey; 23 | private String endpoint; 24 | private String bucketName; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/fastdfs/common/NameValuePair.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common.fastdfs.common; 2 | 3 | /** 4 | * name(key) and value pair model 5 | * 6 | * @author Happy Fish / YuQing 7 | * @version Version 1.0 8 | */ 9 | public class NameValuePair { 10 | protected String name; 11 | protected String value; 12 | 13 | public NameValuePair() { 14 | } 15 | 16 | public NameValuePair(String name) { 17 | this.name = name; 18 | } 19 | 20 | public NameValuePair(String name, String value) { 21 | this.name = name; 22 | this.value = value; 23 | } 24 | 25 | public String getName() { 26 | return this.name; 27 | } 28 | 29 | public void setName(String name) { 30 | this.name = name; 31 | } 32 | 33 | public String getValue() { 34 | return this.value; 35 | } 36 | 37 | public void setValue(String value) { 38 | this.value = value; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/dto/BucketEntity.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.dto; 2 | 3 | import com.aliyun.oss.model.CannedAccessControlList; 4 | 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | 8 | /** 9 | * 10 | * @author 周子斐 (17600004572@163.com) 11 | * @version 1.0 12 | * @remark 2019年7月16日 13 | * @since 1.0 14 | */ 15 | @EqualsAndHashCode(callSuper = true) 16 | @Data 17 | public class BucketEntity extends AbstractEntity { 18 | 19 | /** 20 | * 私有读写 CannedAccessControlList.Private
21 | * 公共读私有写 CannedAccessControlList.PublicRead
22 | * 公共读写 CannedAccessControlList.PublicReadWrite 23 | */ 24 | private CannedAccessControlList acl; 25 | 26 | public BucketEntity(String bucketName) { 27 | super(bucketName); 28 | } 29 | 30 | public BucketEntity setAcl(CannedAccessControlList acl) { 31 | this.acl = acl; 32 | return this; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/annotation/FilePropertiesConfig.java: -------------------------------------------------------------------------------- 1 | //package com.zhouzifei.tool.annotation; 2 | // 3 | //import lombok.Data; 4 | //import lombok.EqualsAndHashCode; 5 | //import org.springframework.boot.context.properties.ConfigurationProperties; 6 | //import org.springframework.core.annotation.AliasFor; 7 | //import org.springframework.stereotype.Component; 8 | // 9 | //import java.lang.annotation.*; 10 | // 11 | //@Target({ElementType.TYPE}) 12 | //@Retention(RetentionPolicy.RUNTIME) 13 | //@Documented 14 | //@Component 15 | //@Data 16 | //@EqualsAndHashCode(callSuper = true) 17 | //@ConfigurationProperties(prefix = "simple-fs.local") 18 | //public @interface FilePropertiesConfig { 19 | // 20 | // /** 21 | // * The value may indicate a suggestion for a logical component name, 22 | // * to be turned into a Spring bean in case of an autodetected component. 23 | // * @return the suggested component name, if any (or empty String otherwise) 24 | // */ 25 | // @AliasFor(annotation = Component.class) 26 | // String value() default ""; 27 | // 28 | //} -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/consts/ResponseStatus.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.consts; 2 | 3 | /** 4 | * 5 | * @author 周子斐 (17600004572@163.com) 6 | * @version 1.0 7 | * @remark 2019年7月16日 8 | * @since 1.0 9 | */ 10 | public enum ResponseStatus { 11 | 12 | SUCCESS(200, "操作成功!"), 13 | ERROR(500, "服务器未知错误!"), 14 | INVALID_PARAMS(500, "操作失败,无效的参数,请检查参数格式、类型是否正确!"), 15 | UPLOAD_FILE_ERROR(500, "文件上传失败!"), 16 | ; 17 | 18 | private Integer code; 19 | private String message; 20 | 21 | ResponseStatus(Integer code, String message) { 22 | this.code = code; 23 | this.message = message; 24 | } 25 | 26 | public static ResponseStatus getResponseStatus(String message) { 27 | for (ResponseStatus ut : ResponseStatus.values()) { 28 | if (ut.getMessage() == message) { 29 | return ut; 30 | } 31 | } 32 | return null; 33 | } 34 | 35 | public Integer getCode() { 36 | return code; 37 | } 38 | 39 | public String getMessage() { 40 | return message; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/fastdfs/ProtoStructDecoder.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common.fastdfs; 2 | 3 | 4 | import java.io.IOException; 5 | import java.lang.reflect.Array; 6 | 7 | /** 8 | * C struct body decoder 9 | * 10 | * @author Happy Fish / YuQing 11 | * @version Version 1.17 12 | */ 13 | public class ProtoStructDecoder { 14 | /** 15 | * Constructor 16 | */ 17 | public ProtoStructDecoder() { 18 | } 19 | 20 | /** 21 | * decode byte buffer 22 | */ 23 | public T[] decode(byte[] bs, Class clazz, int fieldsTotalSize) throws Exception { 24 | if (bs.length % fieldsTotalSize != 0) { 25 | throw new IOException("byte array length: " + bs.length + " is invalid!"); 26 | } 27 | 28 | int count = bs.length / fieldsTotalSize; 29 | int offset; 30 | T[] results = (T[]) Array.newInstance(clazz, count); 31 | 32 | offset = 0; 33 | for (int i = 0; i < results.length; i++) { 34 | results[i] = clazz.newInstance(); 35 | results[i].setFields(bs, offset); 36 | offset += fieldsTotalSize; 37 | } 38 | 39 | return results; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/fastdfs/pool/ConnectionFactory.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common.fastdfs.pool; 2 | 3 | 4 | import com.zhouzifei.tool.common.ServiceException; 5 | import com.zhouzifei.tool.common.fastdfs.ClientGlobal; 6 | 7 | import java.io.IOException; 8 | import java.net.InetSocketAddress; 9 | import java.net.Socket; 10 | 11 | public class ConnectionFactory { 12 | /** 13 | * create from InetSocketAddress 14 | * 15 | * @param socketAddress 16 | * @return 17 | * @throws IOException 18 | */ 19 | public static Connection create(InetSocketAddress socketAddress) throws ServiceException { 20 | try { 21 | Socket sock = new Socket(); 22 | sock.setReuseAddress(true); 23 | sock.setSoTimeout(ClientGlobal.g_network_timeout); 24 | sock.connect(socketAddress, ClientGlobal.g_connect_timeout); 25 | return new Connection(sock, socketAddress); 26 | } catch (Exception e) { 27 | throw new ServiceException("connect to server " + socketAddress.getAddress().getHostAddress() + ":" + socketAddress.getPort() + " fail, emsg:" + e.getMessage()); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/fastdfs/DownloadStream.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common.fastdfs; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | 6 | /** 7 | * Download file by stream (download callback class) 8 | * 9 | * @author zhouzezhong & Happy Fish / YuQing 10 | * @version Version 1.11 11 | */ 12 | public class DownloadStream implements DownloadCallback { 13 | private OutputStream out; 14 | private long currentBytes = 0; 15 | 16 | public DownloadStream(OutputStream out) { 17 | super(); 18 | this.out = out; 19 | } 20 | 21 | /** 22 | * recv file content callback function, may be called more than once when the file downloaded 23 | * 24 | * @param fileSize file size 25 | * @param data data buff 26 | * @param bytes data bytes 27 | * @return 0 success, return none zero(errno) if fail 28 | */ 29 | @Override 30 | public int recv(long fileSize, byte[] data, int bytes) { 31 | try { 32 | out.write(data, 0, bytes); 33 | } catch (IOException ex) { 34 | ex.printStackTrace(); 35 | return -1; 36 | } 37 | 38 | currentBytes += bytes; 39 | if (this.currentBytes == fileSize) { 40 | this.currentBytes = 0; 41 | } 42 | 43 | return 0; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/config/ReadProper.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.config; 2 | 3 | import java.io.InputStream; 4 | import java.util.Properties; 5 | 6 | 7 | public class ReadProper { 8 | 9 | public static String getResourceValue(String key) { 10 | try { 11 | InputStream input = ReadProper.class.getResourceAsStream("/application.properties"); 12 | Properties p = new Properties(); 13 | p.load(input); 14 | input.close(); 15 | if (!p.containsKey(key)) { 16 | return null; 17 | } 18 | return p.getProperty(key); 19 | } catch (Exception e) { 20 | e.printStackTrace(); 21 | return null; 22 | } 23 | 24 | } 25 | 26 | //读取国际化 27 | public static String getI18n(String key) { 28 | try { 29 | InputStream input = ReadProper.class 30 | .getResourceAsStream("/i18n_zh_CN.properties"); 31 | Properties p = new Properties(); 32 | p.load(input); 33 | input.close(); 34 | if (!p.containsKey(key)) { 35 | return null; 36 | } 37 | return p.getProperty(key); 38 | } catch (Exception e) { 39 | e.printStackTrace(); 40 | return null; 41 | } 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/listener/uploadProgressListener.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.listener; 2 | 3 | import com.zhouzifei.tool.dto.VirtualFile; 4 | import lombok.extern.slf4j.Slf4j; 5 | 6 | import java.math.BigDecimal; 7 | 8 | /** 9 | * @author 周子斐 10 | * @date 2021/2/3 11 | * @Description 12 | */ 13 | @Slf4j 14 | public class uploadProgressListener implements ProgressListener { 15 | @Override 16 | public void start(String msg) { 17 | log.info("开始处理任务,文件名为{}",msg); 18 | } 19 | 20 | @Override 21 | public void process(int finished, int sum) { 22 | float percent = new BigDecimal(finished).divide(new BigDecimal(sum), 4, BigDecimal.ROUND_HALF_UP) 23 | .multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP) 24 | .floatValue(); 25 | // BigDecimal bigDecimal = new BigDecimal(downloadBytes.toString()); 26 | // final String speed = StringUtils.convertToDownloadSpeed(new BigDecimal(downloadBytes.toString()).subtract(bigDecimal), 2) + "/s"; 27 | log.info("已下载" + finished + "个\t一共" + sum + "个\t已完成" + percent + "%"+percent + "% (" + finished + "/" + sum + ")"); 28 | // ""+"下载速度为"+speed); 29 | 30 | } 31 | 32 | @Override 33 | public void end(VirtualFile virtualFile) { 34 | log.info("任务处理完成,完成的地址为{}",virtualFile); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/util/MediaFormat.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.util; 2 | 3 | import com.zhouzifei.tool.common.ServiceException; 4 | import com.zhouzifei.tool.util.StringUtils; 5 | 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | 9 | /** 10 | * @author 周子斐 (17600004572@163.com) 11 | * @remark 2019/12/21 0:08 12 | */ 13 | 14 | public class MediaFormat { 15 | 16 | private static Set set = new HashSet<>(); 17 | 18 | static { 19 | set.add("mp4"); 20 | set.add("mkv"); 21 | set.add("webm"); 22 | set.add("gif"); 23 | set.add("mov"); 24 | set.add("ogg"); 25 | set.add("flv"); 26 | set.add("avi"); 27 | set.add("3gp"); 28 | set.add("wmv"); 29 | set.add("mpg"); 30 | set.add("vob"); 31 | set.add("swf"); 32 | set.add("m3u8"); 33 | } 34 | 35 | private MediaFormat() { 36 | 37 | } 38 | 39 | public static String getMediaFormat(String url) { 40 | if (!StringUtils.isUrl(url)) { 41 | throw new ServiceException(url + "不是一个完整URL链接!"); 42 | } 43 | url = url.substring(url.lastIndexOf("/") - 1); 44 | for (String s : set) { 45 | if (url.contains(s)) { 46 | return s; 47 | } 48 | } 49 | throw new ServiceException("非视频链接!"); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/consts/UpLoadConstant.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.consts; 2 | 3 | public class UpLoadConstant { 4 | private UpLoadConstant() { 5 | 6 | } 7 | 8 | private final static String uploading="Uploading:"; 9 | private final static String lock="lock"; 10 | private final static String file="file"; 11 | //当前所有锁(用在不同用户的上传前或重传前对整个文件的锁) 12 | public final static String currLocks=lock+"currLocks"; 13 | //当前锁的拥有者 14 | public final static String lockOwner=lock+"lockOwner"; 15 | 16 | //当前文件传输到第几块 17 | public final static String chunkCurr=file+"chunkCurr"; 18 | 19 | //当前文件上传到fastdfs路径 20 | public final static String fastDfsPath=file+"fastDfsPath"; 21 | 22 | //默认分组 23 | public final static String DEFAULT_GROUP = "group1"; 24 | public final static int ZERO_INT = 0; 25 | public static final Long ZERO_LONG = 0L; 26 | 27 | //全部上传成功已完成 28 | public final static String completedList=uploading+"completedList"; 29 | 30 | //文件块锁(解决同一个用户正在上传时并发解决,比如后端正在上传一个大文件块,前端页面点击删除按钮, 31 | // 继续添加删除的文件,这时候要加锁来阻止其上传,否则会出现丢块问题, 32 | // 因为fastdfs上传不像迅雷下载一样,下载时会创建一个完整的文件,如果上传第一块时,服务器能快速创建一个大文件0填充,那么这样可以支持并发乱序来下载文件块,上传速度会成倍提升,要实现乱序下载文件块,估计就得研究fastdfs源码了) 33 | public final static String chunkLock=lock+"chunkLock"; 34 | 35 | public final static String historyUpload="historyUpload"; 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/config/SimpleFsProperties.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.config; 2 | 3 | import com.zhouzifei.tool.annotation.FileTypeName; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.core.annotation.Order; 8 | import org.springframework.stereotype.Component; 9 | 10 | /** 11 | * @author 周子斐 (17600004572@163.com) 12 | * @remark 2021/1/26 13 | * @Description 14 | */ 15 | @Component 16 | @ConfigurationProperties(prefix = "simple-fs") 17 | @Data 18 | @EqualsAndHashCode(callSuper = false) 19 | @Order(-1) 20 | public class SimpleFsProperties { 21 | public String storageType; 22 | LocalFileProperties local = new LocalFileProperties(); 23 | OssFileProperties aliYunOss = new OssFileProperties(); 24 | FastDfsFileProperties fast = new FastDfsFileProperties(); 25 | HuaweiFileProperties huawei = new HuaweiFileProperties(); 26 | BosFileProperties bos = new BosFileProperties(); 27 | QcloudFileProperties tengxun = new QcloudFileProperties(); 28 | QiniuFileProperties qiniu = new QiniuFileProperties(); 29 | UpaiFileProperties upai = new UpaiFileProperties(); 30 | SmmsFileProperties smms = new SmmsFileProperties(); 31 | GithubFileProperties github = new GithubFileProperties(); 32 | AwsFileProperties aws = new AwsFileProperties(); 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/ServiceException.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common; 2 | 3 | import java.io.PrintWriter; 4 | import java.io.StringWriter; 5 | 6 | public class ServiceException extends RuntimeException { 7 | private static final long serialVersionUID = 1L; 8 | private String errorCode; 9 | private String message; 10 | 11 | public ServiceException(String errorCode, String errorMsg) { 12 | super(errorCode); 13 | this.errorCode = errorCode; 14 | this.message = errorMsg; 15 | } 16 | public ServiceException(String errorMsg) { 17 | super("999999"); 18 | this.errorCode = "999999"; 19 | this.message = errorMsg; 20 | } 21 | public ServiceException(String errorCode, String errorMsg, Throwable t) { 22 | super(errorCode, t); 23 | this.errorCode = errorCode; 24 | this.message = errorMsg; 25 | } 26 | 27 | public String getErrorCode() { 28 | return this.errorCode; 29 | } 30 | 31 | public String getMessage() { 32 | return this.message; 33 | } 34 | public static String getTrace(Throwable t) { 35 | StringWriter stringWriter = new StringWriter(); 36 | PrintWriter writer = new PrintWriter(stringWriter); 37 | t.printStackTrace(writer); 38 | StringBuffer buffer = stringWriter.getBuffer(); 39 | return buffer.toString(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/fastdfs/TrackerServer.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common.fastdfs; 2 | 3 | 4 | 5 | import com.zhouzifei.tool.common.ServiceException; 6 | import com.zhouzifei.tool.common.fastdfs.pool.Connection; 7 | import com.zhouzifei.tool.common.fastdfs.pool.ConnectionFactory; 8 | import com.zhouzifei.tool.common.fastdfs.pool.ConnectionPool; 9 | 10 | import java.io.IOException; 11 | import java.net.InetSocketAddress; 12 | 13 | /** 14 | * Tracker Server Info 15 | * 16 | * @author Happy Fish / YuQing 17 | * @version Version 1.11 18 | */ 19 | public class TrackerServer { 20 | protected InetSocketAddress inetSockAddr; 21 | 22 | 23 | public TrackerServer(InetSocketAddress inetSockAddr) throws IOException { 24 | this.inetSockAddr = inetSockAddr; 25 | } 26 | 27 | public Connection getConnection() throws ServiceException, IOException { 28 | Connection connection; 29 | if (ClientGlobal.g_connection_pool_enabled) { 30 | connection = ConnectionPool.getConnection(this.inetSockAddr); 31 | } else { 32 | connection = ConnectionFactory.create(this.inetSockAddr); 33 | } 34 | return connection; 35 | } 36 | /** 37 | * get the server info 38 | * 39 | * @return the server info 40 | */ 41 | public InetSocketAddress getInetSocketAddress() { 42 | return this.inetSockAddr; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/dto/ResponseVO.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.dto; 2 | 3 | import java.io.Serializable; 4 | import java.util.Collection; 5 | import java.util.List; 6 | 7 | import com.alibaba.fastjson.JSONObject; 8 | import com.alibaba.fastjson.serializer.SerializerFeature; 9 | 10 | import com.zhouzifei.tool.consts.ResponseStatus; 11 | import lombok.Data; 12 | import lombok.EqualsAndHashCode; 13 | /** 14 | * controller返回json 15 | * @author 周子斐 (17600004572@163.com) 16 | * @version 1.0 17 | * @remark 2019年7月16日 18 | * @since 1.0 19 | */ 20 | @Data 21 | @EqualsAndHashCode(callSuper = false) 22 | public class ResponseVO implements Serializable { 23 | private Integer status; 24 | private String message; 25 | private T data; 26 | 27 | public ResponseVO(Integer status, String message, T data) { 28 | this.status = status; 29 | this.message = message; 30 | this.data = data; 31 | } 32 | 33 | public ResponseVO(ResponseStatus status, T data){ 34 | this(status.getCode(), status.getMessage(), data); 35 | } 36 | 37 | public String toJson() { 38 | T t = this.getData(); 39 | if (t instanceof List || t instanceof Collection) { 40 | return JSONObject.toJSONString(this, SerializerFeature.WriteNullListAsEmpty); 41 | } else { 42 | return JSONObject.toJSONString(this, SerializerFeature.WriteMapNullValue); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/fastdfs/ServerInfo.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common.fastdfs; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | import java.net.Socket; 6 | 7 | /** 8 | * Server Info 9 | * 10 | * @author Happy Fish / YuQing 11 | * @version Version 1.7 12 | */ 13 | public class ServerInfo { 14 | protected String ip_addr; 15 | protected int port; 16 | 17 | /** 18 | * Constructor 19 | * 20 | * @param ip_addr address of the server 21 | * @param port the port of the server 22 | */ 23 | public ServerInfo(String ip_addr, int port) { 24 | this.ip_addr = ip_addr; 25 | this.port = port; 26 | } 27 | 28 | /** 29 | * return the ip address 30 | * 31 | * @return the ip address 32 | */ 33 | public String getIpAddr() { 34 | return this.ip_addr; 35 | } 36 | 37 | /** 38 | * return the port of the server 39 | * 40 | * @return the port of the server 41 | */ 42 | public int getPort() { 43 | return this.port; 44 | } 45 | 46 | /** 47 | * connect to server 48 | * 49 | * @return connected Socket object 50 | */ 51 | public Socket connect() throws IOException { 52 | Socket sock = new Socket(); 53 | sock.setReuseAddress(true); 54 | sock.setSoTimeout(ClientGlobal.g_network_timeout); 55 | sock.connect(new InetSocketAddress(this.ip_addr, this.port), ClientGlobal.g_connect_timeout); 56 | return sock; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 21 |
加载中
22 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/util/RandomsUtil.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.util; 2 | 3 | import java.util.Random; 4 | 5 | /** 6 | * 随机工具类 7 | * 8 | * @author 周子斐 (17600004572@163.com) 9 | * @version 1.0 10 | * @remark 2019年7月16日 11 | * @since 1.0 12 | */ 13 | public class RandomsUtil { 14 | /** 15 | * 定义验证码字符.去除了O和I等容易混淆的字母 16 | */ 17 | private static final char[] ALPHA = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '2', '3', '4', '5', '6', '7', '8', '9'}; 18 | private static final Random RANDOM = new Random(); 19 | 20 | /** 21 | * 产生两个数之间的随机数 22 | * 23 | * @param min 小数 24 | * @param max 比min大的数 25 | * @return int 随机数字 26 | */ 27 | public static int num(int min, int max) { 28 | return min + RANDOM.nextInt(max - min); 29 | } 30 | 31 | /** 32 | * 产生0--num的随机数,不包括num 33 | * 34 | * @param num 数字 35 | * @return int 随机数字 36 | */ 37 | public static int num(int num) { 38 | return RANDOM.nextInt(num); 39 | } 40 | 41 | public static String alpha(int num) { 42 | StringBuilder alpha = new StringBuilder(); 43 | num = Math.max(num, 1); 44 | for (int i = 0; i < num; i++) { 45 | alpha.append(ALPHA[num(0, ALPHA.length)]); 46 | } 47 | return alpha.toString(); 48 | } 49 | 50 | public static void main(String[] args) { 51 | System.out.println(alpha(10)); 52 | } 53 | } -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/fastdfs/StorageServer.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common.fastdfs; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | 6 | /** 7 | * Storage Server Info 8 | * 9 | * @author Happy Fish / YuQing 10 | * @version Version 1.11 11 | */ 12 | public class StorageServer extends TrackerServer { 13 | protected int store_path_index = 0; 14 | 15 | /** 16 | * Constructor 17 | * 18 | * @param ip_addr the ip address of storage server 19 | * @param port the port of storage server 20 | * @param store_path the store path index on the storage server 21 | */ 22 | public StorageServer(String ip_addr, int port, int store_path) throws IOException { 23 | super(new InetSocketAddress(ip_addr, port)); 24 | this.store_path_index = store_path; 25 | } 26 | 27 | /** 28 | * Constructor 29 | * 30 | * @param ip_addr the ip address of storage server 31 | * @param port the port of storage server 32 | * @param store_path the store path index on the storage server 33 | */ 34 | public StorageServer(String ip_addr, int port, byte store_path) throws IOException { 35 | super(new InetSocketAddress(ip_addr, port)); 36 | if (store_path < 0) { 37 | this.store_path_index = 256 + store_path; 38 | } else { 39 | this.store_path_index = store_path; 40 | } 41 | } 42 | 43 | /** 44 | * @return the store path index on the storage server 45 | */ 46 | public int getStorePathIndex() { 47 | return this.store_path_index; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/listener/UploadStream.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.listener; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | 7 | /** 8 | * 上传文件 9 | * @author zhouzezhong & Happy Fish YuQing 10 | * @version 1.11版 11 | */ 12 | public class UploadStream implements UploadCallback { 13 | private final InputStream inputStream; 14 | private long fileSize = 0; 15 | 16 | /** 17 | * 构造函数 18 | * @param inputStream 用于上传的输入流 19 | * @param fileSize 上传文件的大小 20 | */ 21 | public UploadStream(InputStream inputStream, long fileSize) { 22 | super(); 23 | this.inputStream = inputStream; 24 | this.fileSize = fileSize; 25 | } 26 | 27 | /** 28 | * 结束文件内容回调函数,仅在文件上传时调用一次 29 | * @param out 输出流写入文件内容 30 | * @return 0 成功,失败则返回none 0(errno) 31 | */ 32 | @Override 33 | public int send(OutputStream out) throws IOException { 34 | long remainBytes = fileSize; 35 | byte[] buff = new byte[256 * 1024]; 36 | int bytes; 37 | while (remainBytes > 0) { 38 | try { 39 | if ((bytes = inputStream.read(buff, 0, remainBytes > buff.length ? buff.length : (int) remainBytes)) < 0) { 40 | return -1; 41 | } 42 | } catch (IOException ex) { 43 | ex.printStackTrace(); 44 | return -1; 45 | } 46 | 47 | out.write(buff, 0, bytes); 48 | remainBytes -= bytes; 49 | } 50 | 51 | return 0; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/fastdfs/UploadStream.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common.fastdfs; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | 7 | /** 8 | * Upload file by stream 9 | * 10 | * @author zhouzezhong & Happy Fish / YuQing 11 | * @version Version 1.11 12 | */ 13 | public class UploadStream implements UploadCallback { 14 | private InputStream inputStream; //input stream for reading 15 | private long fileSize = 0; //size of the uploaded file 16 | 17 | /** 18 | * constructor 19 | * 20 | * @param inputStream input stream for uploading 21 | * @param fileSize size of uploaded file 22 | */ 23 | public UploadStream(InputStream inputStream, long fileSize) { 24 | super(); 25 | this.inputStream = inputStream; 26 | this.fileSize = fileSize; 27 | } 28 | 29 | /** 30 | * send file content callback function, be called only once when the file uploaded 31 | * 32 | * @param out output stream for writing file content 33 | * @return 0 success, return none zero(errno) if fail 34 | */ 35 | @Override 36 | public int send(OutputStream out) throws IOException { 37 | long remainBytes = fileSize; 38 | byte[] buff = new byte[256 * 1024]; 39 | int bytes; 40 | while (remainBytes > 0) { 41 | try { 42 | if ((bytes = inputStream.read(buff, 0, remainBytes > buff.length ? buff.length : (int) remainBytes)) < 0) { 43 | return -1; 44 | } 45 | } catch (IOException ex) { 46 | ex.printStackTrace(); 47 | return -1; 48 | } 49 | 50 | out.write(buff, 0, bytes); 51 | remainBytes -= bytes; 52 | } 53 | 54 | return 0; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/SimpleCache.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common; 2 | 3 | import java.util.Map; 4 | import java.util.WeakHashMap; 5 | import java.util.concurrent.locks.ReentrantReadWriteLock; 6 | import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; 7 | import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; 8 | 9 | public class SimpleCache { 10 | private final Map cache = new WeakHashMap(); 11 | private final ReentrantReadWriteLock cacheLock = new ReentrantReadWriteLock(); 12 | private final ReadLock readLock; 13 | private final WriteLock writeLock; 14 | 15 | public SimpleCache() { 16 | this.readLock = this.cacheLock.readLock(); 17 | this.writeLock = this.cacheLock.writeLock(); 18 | } 19 | 20 | public V get(K key) { 21 | this.readLock.lock(); 22 | 23 | Object value; 24 | try { 25 | value = this.cache.get(key); 26 | } finally { 27 | this.readLock.unlock(); 28 | } 29 | 30 | return (V) value; 31 | } 32 | 33 | public V put(K key, V value) { 34 | this.writeLock.lock(); 35 | 36 | try { 37 | this.cache.put(key, value); 38 | } finally { 39 | this.writeLock.unlock(); 40 | } 41 | 42 | return value; 43 | } 44 | 45 | public V remove(K key) { 46 | this.writeLock.lock(); 47 | 48 | Object var2; 49 | try { 50 | var2 = this.cache.remove(key); 51 | } finally { 52 | this.writeLock.unlock(); 53 | } 54 | 55 | return (V) var2; 56 | } 57 | 58 | public void clear() { 59 | this.writeLock.lock(); 60 | 61 | try { 62 | this.cache.clear(); 63 | } finally { 64 | this.writeLock.unlock(); 65 | } 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /docs/_coverpage.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ![logo](_media/icon.svg) 4 | 5 | # simpleFS 1.2 6 | 7 | > 这是一个小型整合型的工具类,整合阿里云,七牛云,又拍云,腾讯云,华为云,百度云,本地上传等OSS上传,它可以让我们脱离繁琐的开发流程,让开发变得**So easy!** 8 | 9 |

10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | star 33 | 34 | 35 | github star 36 | 37 |

38 | 39 | [GitHub](https://github.com/docsifyjs/docsify/) 40 | [GitHub](https://github.com/docsifyjs/docsify/) 41 | [Get Started](README.md) -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/MimeTypeUtils.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common; 2 | 3 | /** 4 | * 媒体类型工具类 5 | * 6 | * @author ruoyi 7 | */ 8 | public class MimeTypeUtils { 9 | public static final String IMAGE_PNG = "image/png"; 10 | 11 | public static final String IMAGE_JPG = "image/jpg"; 12 | 13 | public static final String IMAGE_JPEG = "image/jpeg"; 14 | 15 | public static final String IMAGE_BMP = "image/bmp"; 16 | 17 | public static final String IMAGE_GIF = "image/gif"; 18 | 19 | public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"}; 20 | 21 | public static final String[] FLASH_EXTENSION = {"swf", "flv"}; 22 | 23 | public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", 24 | "asf", "rm", "rmvb"}; 25 | 26 | public static final String[] VIDEO_EXTENSION = {"mp4", "avi", "rmvb"}; 27 | 28 | public static final String[] IMAGE_VIDEO_EXTENSION = { 29 | // 图片 30 | "bmp", "gif", "jpg", "jpeg", "png", 31 | // 视频格式 32 | "mp4", "avi", "rmvb", 33 | }; 34 | 35 | public static final String[] DEFAULT_ALLOWED_EXTENSION = { 36 | // 图片 37 | "bmp", "gif", "jpg", "jpeg", "png", 38 | // word excel powerpoint 39 | "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", 40 | // 压缩文件 41 | "rar", "zip", "gz", "bz2", 42 | // 视频格式 43 | "mp4", "avi", "rmvb", 44 | // pdf 45 | "pdf"}; 46 | 47 | public static String getExtension(String prefix) { 48 | switch (prefix) { 49 | case IMAGE_PNG: 50 | return "png"; 51 | case IMAGE_JPG: 52 | return "jpg"; 53 | case IMAGE_JPEG: 54 | return "jpeg"; 55 | case IMAGE_BMP: 56 | return "bmp"; 57 | case IMAGE_GIF: 58 | return "gif"; 59 | default: 60 | return ""; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/fastdfs/StructBase.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common.fastdfs; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.util.Date; 5 | 6 | /** 7 | * C struct body decoder 8 | * 9 | * @author Happy Fish / YuQing 10 | * @version Version 1.17 11 | */ 12 | public abstract class StructBase { 13 | /** 14 | * set fields 15 | * 16 | * @param bs byte array 17 | * @param offset start offset 18 | */ 19 | public abstract void setFields(byte[] bs, int offset); 20 | 21 | protected String stringValue(byte[] bs, int offset, FieldInfo filedInfo) { 22 | try { 23 | return (new String(bs, offset + filedInfo.offset, filedInfo.size, ClientGlobal.g_charset)).trim(); 24 | } catch (UnsupportedEncodingException ex) { 25 | ex.printStackTrace(); 26 | return null; 27 | } 28 | } 29 | 30 | protected long longValue(byte[] bs, int offset, FieldInfo filedInfo) { 31 | return ProtoCommon.buff2long(bs, offset + filedInfo.offset); 32 | } 33 | 34 | protected int intValue(byte[] bs, int offset, FieldInfo filedInfo) { 35 | return (int) ProtoCommon.buff2long(bs, offset + filedInfo.offset); 36 | } 37 | 38 | protected int int32Value(byte[] bs, int offset, FieldInfo filedInfo) { 39 | return ProtoCommon.buff2int(bs, offset + filedInfo.offset); 40 | } 41 | 42 | protected byte byteValue(byte[] bs, int offset, FieldInfo filedInfo) { 43 | return bs[offset + filedInfo.offset]; 44 | } 45 | 46 | protected boolean booleanValue(byte[] bs, int offset, FieldInfo filedInfo) { 47 | return bs[offset + filedInfo.offset] != 0; 48 | } 49 | 50 | protected Date dateValue(byte[] bs, int offset, FieldInfo filedInfo) { 51 | return new Date(ProtoCommon.buff2long(bs, offset + filedInfo.offset) * 1000); 52 | } 53 | 54 | protected static class FieldInfo { 55 | protected String name; 56 | protected int offset; 57 | protected int size; 58 | 59 | public FieldInfo(String name, int offset, int size) { 60 | this.name = name; 61 | this.offset = offset; 62 | this.size = size; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/util/HttpData.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.util; 2 | 3 | import com.zhouzifei.tool.common.Response; 4 | import com.zhouzifei.tool.media.file.util.StreamUtil; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.net.HttpURLConnection; 9 | import java.net.URL; 10 | import java.net.URLConnection; 11 | import java.util.Map; 12 | import java.util.Set; 13 | 14 | public class HttpData { 15 | 16 | /** 17 | * 直接获取接口返回值 18 | * @param urlPath 要访问的地址 19 | * @return 20 | */ 21 | public static Response getData(String urlPath, Map hears,Integer readTime) { 22 | StringBuilder sb = new StringBuilder(); 23 | try { 24 | // 统一资源 25 | URL url = new URL(urlPath); 26 | // 连接类的父类,抽象类 27 | URLConnection urlConnection = url.openConnection(); 28 | // http的连接类 29 | HttpURLConnection connection = (HttpURLConnection) urlConnection; 30 | // 设定请求的方法,默认是GET 31 | connection.setRequestMethod("GET"); 32 | if(null != readTime){ 33 | connection.setReadTimeout(readTime); 34 | } 35 | // 设置字符编码 36 | connection.setRequestProperty("Charset", "UTF-8"); 37 | connection.setRequestProperty("Content-type", "application/x-www-form-urlencoded"); 38 | final Set set = hears.keySet(); 39 | for (String key : set) { 40 | connection.setRequestProperty(key, hears.get(key)); 41 | } 42 | // connection.setInstanceFollowRedirects(false); 43 | // 打开到此 URL 引用的资源的通信链接(如果尚未建立这样的连接)。 44 | connection.connect(); 45 | //BufferedInputStream bin = new BufferedInputStream(connection.getInputStream()); 46 | final InputStream inputStream = connection.getInputStream(); 47 | final String string = StreamUtil.toString(inputStream, "UTF-8"); 48 | connection.disconnect(); 49 | final int responseCode = connection.getResponseCode(); 50 | if(200 == responseCode){ 51 | return new Response().withCode("200").withData(string); 52 | }else{ 53 | return new Response().withCode(String.valueOf(responseCode)); 54 | } 55 | } catch (IOException e) { 56 | return null; 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/util/StreamUtil.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.media.file.util; 2 | 3 | 4 | import com.zhouzifei.tool.common.ServiceException; 5 | 6 | import java.io.BufferedReader; 7 | import java.io.ByteArrayInputStream; 8 | import java.io.ByteArrayOutputStream; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.io.InputStreamReader; 12 | 13 | /** 14 | * @author 周子斐 (17600004572@163.com) 15 | * @version 1.0 16 | * @remark 2019年7月16日 17 | * @since 1.0 18 | */ 19 | public class StreamUtil { 20 | 21 | /** 22 | * 将InputStream转换为字符串 23 | * 24 | * @param is InputStream 25 | * @return 26 | */ 27 | public static String toString(InputStream is) { 28 | return toString(is, "UTF-8"); 29 | } 30 | 31 | /** 32 | * 将InputStream转换为字符串 33 | * 34 | * @param is InputStream 35 | * @return 36 | */ 37 | public static String toString(InputStream is, String encoding) { 38 | if (null == is) { 39 | return null; 40 | } 41 | encoding = encoding == null ? "UTF-8" : encoding; 42 | StringBuilder fileContent = new StringBuilder(); 43 | try ( 44 | InputStream inputStream = is; 45 | BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, encoding)) 46 | ) { 47 | String line; 48 | while ((line = reader.readLine()) != null) { 49 | fileContent.append(line); 50 | fileContent.append("\n"); 51 | } 52 | } catch (Exception e) { 53 | e.printStackTrace(); 54 | } 55 | return fileContent.toString(); 56 | } 57 | 58 | /** 59 | * 复制InputStream 60 | * 61 | * @param is InputStream 62 | * @return 63 | */ 64 | public static InputStream clone(InputStream is) { 65 | if(null == is){ 66 | throw new ServiceException("无法获取文件流,文件不可用!"); 67 | } 68 | try { 69 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 70 | byte[] buffer = new byte[1024]; 71 | int len; 72 | while ((len = is.read(buffer)) > -1) { 73 | baos.write(buffer, 0, len); 74 | } 75 | baos.flush(); 76 | return new ByteArrayInputStream(baos.toByteArray()); 77 | } catch (IOException e) { 78 | throw new ServiceException("无法复制当前文件流!{}", e.getMessage()); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/consts/StorageTypeConst.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.consts; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * @author 周子斐 (17600004572@163.com) 8 | * @remark 2021/1/27 9 | 10 | * @Description 11 | */ 12 | public enum StorageTypeConst { 13 | 14 | LOCAL("local", "本地上传"), 15 | ALIYUN("aliyun","阿里云OSS"), 16 | AWSS3("awss3","AWSS3"), 17 | BAIDUBOS("baidu","百度BOS"), 18 | QINIUYUN("qiniu", "七牛云存储"), 19 | TENGXUNYUN("tengxunyun", "腾讯云COS"), 20 | YOUPAIYUN("youpaiyun", "又拍云存储"), 21 | HUAWEIYUN("huaweiyun", "华为云存储"), 22 | FASTDFS("fastdfs", "FAST-DFS"), 23 | SMMS("smms", "SMMS图床"), 24 | GITHUB("github", "github存储"), 25 | SCP("scp", "SCP存储"), 26 | ; 27 | 28 | private String storageType; 29 | private String desc; 30 | 31 | StorageTypeConst(String storageType, String desc) { 32 | this.storageType = storageType; 33 | this.desc = desc; 34 | } 35 | public String getStorageType() { 36 | return storageType; 37 | } 38 | 39 | public String getDesc() { 40 | return desc; 41 | } 42 | 43 | public void setDesc(String desc) { 44 | this.desc = desc; 45 | } 46 | 47 | public static Map getMap() { 48 | StorageTypeConst[] alarmGrades = StorageTypeConst.values(); 49 | Map map = new HashMap<>(); 50 | for (int i = 0; i < alarmGrades.length; i++) { 51 | final StorageTypeConst alarmGrade = alarmGrades[i]; 52 | map.put(alarmGrade.getStorageType(), alarmGrade.getDesc()); 53 | } 54 | return map; 55 | } 56 | /** 57 | * 根据Key得到枚举的Value 58 | * 普通for循环遍历,比较判断 59 | * 60 | * @param key 61 | * @return 62 | */ 63 | public static StorageTypeConst getEnumType(String key) { 64 | StorageTypeConst[] alarmGrades = StorageTypeConst.values(); 65 | for (int i = 0; i < alarmGrades.length; i++) { 66 | if (alarmGrades[i].getStorageType().equals(key)) { 67 | return alarmGrades[i]; 68 | } 69 | } 70 | return StorageTypeConst.ALIYUN; 71 | } 72 | //public static final Map list = Arrays.stream(values()).collect(Collectors.toMap(StorageTypeConst::getStorageType, StorageTypeConst::getDesc)); 73 | 74 | public static void main(String[] args) { 75 | final Map values = StorageTypeConst.getMap(); 76 | System.out.println(values); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/WebExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.validation.BindingResult; 6 | import org.springframework.validation.FieldError; 7 | import org.springframework.web.bind.MethodArgumentNotValidException; 8 | import org.springframework.web.bind.annotation.ControllerAdvice; 9 | import org.springframework.web.bind.annotation.ExceptionHandler; 10 | import org.springframework.web.bind.annotation.ResponseBody; 11 | import org.springframework.web.bind.annotation.ResponseStatus; 12 | 13 | import javax.servlet.http.HttpServletRequest; 14 | import java.util.Iterator; 15 | 16 | @ControllerAdvice 17 | @Slf4j 18 | public class WebExceptionHandler { 19 | 20 | 21 | @ExceptionHandler({ServiceException.class}) 22 | @ResponseStatus(code = HttpStatus.OK) 23 | @ResponseBody 24 | public Response handleServiceException(HttpServletRequest request, ServiceException e) { 25 | log.error("error ", e); 26 | return Response.code(e.getErrorCode()).withMessage(e.getMessage()); 27 | } 28 | 29 | @ExceptionHandler({Exception.class}) 30 | @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR) 31 | @ResponseBody 32 | public Response defaultExceptionHandler(HttpServletRequest request, Exception e) { 33 | log.error("error ", e); 34 | return Response.code("999999").withMessage("internal error"); 35 | } 36 | 37 | @ResponseStatus(code = HttpStatus.OK) 38 | @ExceptionHandler({MethodArgumentNotValidException.class}) 39 | @ResponseBody 40 | public Response methodArgumentNotValidException(MethodArgumentNotValidException e) { 41 | log.error("验证异常:", e); 42 | BindingResult bindingResult = e.getBindingResult(); 43 | String firstErrorMsg = null; 44 | Iterator var4 = bindingResult.getFieldErrors().iterator(); 45 | if (var4.hasNext()) { 46 | FieldError fieldError = (FieldError)var4.next(); 47 | firstErrorMsg = fieldError.getDefaultMessage(); 48 | } 49 | return Response.code("999998").withMessage(firstErrorMsg); 50 | } 51 | 52 | @ResponseStatus(code = HttpStatus.OK) 53 | @ExceptionHandler({IllegalArgumentException.class}) 54 | @ResponseBody 55 | public Response illegalArgmentException(IllegalArgumentException e) { 56 | log.error("参数异常:", e); 57 | return Response.code("999998").withMessage(e.getMessage()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/util/ThreadManager.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.util; 2 | 3 | import java.util.concurrent.Executors; 4 | import java.util.concurrent.LinkedBlockingQueue; 5 | import java.util.concurrent.ThreadPoolExecutor; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | /** 9 | * @author 周子斐 10 | * @date 2021/2/18 11 | * @Description 12 | */ 13 | public class ThreadManager { 14 | 15 | /** 16 | * 通过ThreadPoolExecutor的代理类来对线程池的管理 17 | */ 18 | private static ThreadPollProxy mThreadPollProxy; 19 | 20 | /** 21 | * 单列对象 22 | * @param corePoolSize 23 | * @param maximumPoolSize 24 | * @param keepAliveTime 25 | * @return ThreadPollProxy 26 | */ 27 | public static ThreadPollProxy getThreadPollProxy(int corePoolSize,int maximumPoolSize,long keepAliveTime){ 28 | synchronized (ThreadPollProxy.class) { 29 | if(mThreadPollProxy==null){ 30 | mThreadPollProxy=new ThreadPollProxy(corePoolSize,maximumPoolSize, keepAliveTime); 31 | } 32 | } 33 | return mThreadPollProxy; 34 | } 35 | 36 | /** 37 | * 单列对象 38 | * @return ThreadPollProxy 39 | */ 40 | public static ThreadPollProxy getThreadPollProxy(){ 41 | synchronized (ThreadPollProxy.class) { 42 | if(mThreadPollProxy==null){ 43 | mThreadPollProxy=new ThreadPollProxy(3,6,1000); 44 | } 45 | } 46 | return mThreadPollProxy; 47 | } 48 | 49 | /** 50 | * 通过ThreadPoolExecutor的代理类来对线程池的管理 51 | */ 52 | public static class ThreadPollProxy{ 53 | //线程池执行者 ,java内部通过该api实现对线程池管理 54 | private ThreadPoolExecutor poolExecutor; 55 | private int corePoolSize; 56 | private int maximumPoolSize; 57 | private long keepAliveTime; 58 | 59 | public ThreadPollProxy(int corePoolSize,int maximumPoolSize,long keepAliveTime){ 60 | this.corePoolSize=corePoolSize; 61 | this.maximumPoolSize=maximumPoolSize; 62 | this.keepAliveTime=keepAliveTime; 63 | } 64 | //对外提供一个执行任务的方法 65 | public void execute(Runnable r){ 66 | if(poolExecutor==null||poolExecutor.isShutdown()){ 67 | poolExecutor=new ThreadPoolExecutor( 68 | //核心线程数量 69 | corePoolSize, 70 | //最大线程数量 71 | maximumPoolSize, 72 | //当线程空闲时,保持活跃的时间 73 | keepAliveTime, 74 | //时间单元 ,毫秒级 75 | TimeUnit.MILLISECONDS, 76 | //线程任务队列 77 | new LinkedBlockingQueue(), 78 | //创建线程的工厂 79 | Executors.defaultThreadFactory()); 80 | } 81 | poolExecutor.execute(r); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/dto/VirtualFile.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.dto; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.BOBYQAOptimizer; 10 | 11 | /** 12 | * 13 | * @author 周子斐 (17600004572@163.com) 14 | * @version 1.0 15 | * @remark 2019年7月16日 16 | * @since 1.0 17 | */ 18 | @Data 19 | @Builder 20 | @EqualsAndHashCode(callSuper = false) 21 | public class VirtualFile implements Serializable { 22 | /** 23 | * 文件大小 24 | */ 25 | public Long size; 26 | /** 27 | * 文件后缀(Suffix) 28 | */ 29 | public String suffix; 30 | /** 31 | * 文件hash 32 | */ 33 | private String fileHash; 34 | /** 35 | * 文件路径 (不带域名) 36 | */ 37 | private String filePath; 38 | /** 39 | * 文件全路径 (带域名) 40 | */ 41 | private String fullFilePath; 42 | /** 43 | * 原始文件名 44 | */ 45 | private String originalFileName; 46 | /** 47 | * 文件上传开始的时间 48 | */ 49 | private Date uploadStartTime; 50 | /** 51 | * 文件上传结束的时间 52 | */ 53 | private Date uploadEndTime; 54 | /** 55 | * 文件是否为文件夹 56 | */ 57 | private Boolean isFold; 58 | 59 | public VirtualFile setFileHash(String fileHash) { 60 | this.fileHash = fileHash; 61 | return this; 62 | } 63 | 64 | public VirtualFile setFilePath(String filePath) { 65 | this.filePath = filePath; 66 | return this; 67 | } 68 | 69 | public VirtualFile setFullFilePath(String fullFilePath) { 70 | this.fullFilePath = fullFilePath; 71 | return this; 72 | } 73 | 74 | public VirtualFile setOriginalFileName(String originalFileName) { 75 | this.originalFileName = originalFileName; 76 | return this; 77 | } 78 | 79 | public VirtualFile setUploadStartTime(Date uploadStartTime) { 80 | this.uploadStartTime = uploadStartTime; 81 | return this; 82 | } 83 | 84 | public VirtualFile setUploadEndTime(Date uploadEndTime) { 85 | this.uploadEndTime = uploadEndTime; 86 | return this; 87 | } 88 | 89 | public long getUseTime() { 90 | Date uploadEndTime = this.getUploadEndTime(); 91 | Date uploadStartTime = this.getUploadStartTime(); 92 | if (null == uploadStartTime || null == uploadEndTime) { 93 | return -1; 94 | } 95 | return uploadEndTime.getTime() - uploadStartTime.getTime(); 96 | } 97 | 98 | public VirtualFile setSize(long size) { 99 | this.size = size; 100 | return this; 101 | } 102 | 103 | public VirtualFile setSuffix(String suffix) { 104 | this.suffix = suffix; 105 | return this; 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/fastdfs/TrackerGroup.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common.fastdfs; 2 | 3 | import java.io.IOException; 4 | import java.net.InetSocketAddress; 5 | 6 | /** 7 | * Tracker server group 8 | * 9 | * @author Happy Fish / YuQing 10 | * @version Version 1.17 11 | */ 12 | public class TrackerGroup { 13 | public int tracker_server_index; 14 | public InetSocketAddress[] tracker_servers; 15 | protected Integer lock; 16 | 17 | /** 18 | * Constructor 19 | * 20 | * @param tracker_servers tracker servers 21 | */ 22 | public TrackerGroup(InetSocketAddress[] tracker_servers) { 23 | this.tracker_servers = tracker_servers; 24 | this.lock = new Integer(0); 25 | this.tracker_server_index = 0; 26 | } 27 | 28 | /** 29 | * return connected tracker server 30 | * 31 | * @return connected tracker server, null for fail 32 | */ 33 | public TrackerServer getTrackerServer(int serverIndex) throws IOException { 34 | return new TrackerServer(this.tracker_servers[serverIndex]); 35 | } 36 | 37 | /** 38 | * return connected tracker server 39 | * 40 | * @return connected tracker server, null for fail 41 | */ 42 | public TrackerServer getTrackerServer() throws IOException { 43 | int current_index; 44 | 45 | synchronized (this.lock) { 46 | this.tracker_server_index++; 47 | if (this.tracker_server_index >= this.tracker_servers.length) { 48 | this.tracker_server_index = 0; 49 | } 50 | 51 | current_index = this.tracker_server_index; 52 | } 53 | 54 | try { 55 | return this.getTrackerServer(current_index); 56 | } catch (IOException ex) { 57 | System.err.println("connect to server " + this.tracker_servers[current_index].getAddress().getHostAddress() + ":" + this.tracker_servers[current_index].getPort() + " fail"); 58 | ex.printStackTrace(System.err); 59 | } 60 | 61 | for (int i = 0; i < this.tracker_servers.length; i++) { 62 | if (i == current_index) { 63 | continue; 64 | } 65 | 66 | try { 67 | TrackerServer trackerServer = this.getTrackerServer(i); 68 | 69 | synchronized (this.lock) { 70 | if (this.tracker_server_index == current_index) { 71 | this.tracker_server_index = i; 72 | } 73 | } 74 | 75 | return trackerServer; 76 | } catch (IOException ex) { 77 | System.err.println("connect to server " + this.tracker_servers[i].getAddress().getHostAddress() + ":" + this.tracker_servers[i].getPort() + " fail"); 78 | ex.printStackTrace(System.err); 79 | } 80 | } 81 | 82 | return null; 83 | } 84 | 85 | public Object clone() { 86 | InetSocketAddress[] trackerServers = new InetSocketAddress[this.tracker_servers.length]; 87 | for (int i = 0; i < trackerServers.length; i++) { 88 | trackerServers[i] = new InetSocketAddress(this.tracker_servers[i].getAddress().getHostAddress(), this.tracker_servers[i].getPort()); 89 | } 90 | 91 | return new TrackerGroup(trackerServers); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/fastdfs/pool/ConnectionPool.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common.fastdfs.pool; 2 | 3 | 4 | import com.zhouzifei.tool.common.ServiceException; 5 | 6 | import java.io.IOException; 7 | import java.net.InetSocketAddress; 8 | import java.util.Map; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | public class ConnectionPool { 12 | /** 13 | * key is ip:port, value is ConnectionManager 14 | */ 15 | private final static ConcurrentHashMap CP = new ConcurrentHashMap(); 16 | 17 | public static Connection getConnection(InetSocketAddress socketAddress) throws ServiceException { 18 | if (socketAddress == null) { 19 | return null; 20 | } 21 | String key = getKey(socketAddress); 22 | ConnectionManager connectionManager; 23 | connectionManager = CP.get(key); 24 | if (connectionManager == null) { 25 | synchronized (ConnectionPool.class) { 26 | connectionManager = CP.get(key); 27 | if (connectionManager == null) { 28 | connectionManager = new ConnectionManager(socketAddress); 29 | CP.put(key, connectionManager); 30 | } 31 | } 32 | } 33 | return connectionManager.getConnection(); 34 | } 35 | 36 | public static void releaseConnection(Connection connection) throws IOException { 37 | if (connection == null) { 38 | return; 39 | } 40 | String key = getKey(connection.getInetSocketAddress()); 41 | ConnectionManager connectionManager = CP.get(key); 42 | if (connectionManager != null) { 43 | connectionManager.releaseConnection(connection); 44 | } else { 45 | connection.closeDirectly(); 46 | } 47 | 48 | } 49 | 50 | public static void closeConnection(Connection connection) throws IOException { 51 | if (connection == null) { 52 | return; 53 | } 54 | String key = getKey(connection.getInetSocketAddress()); 55 | ConnectionManager connectionManager = CP.get(key); 56 | if (connectionManager != null) { 57 | connectionManager.closeConnection(connection); 58 | connectionManager.setActiveTestFlag(); 59 | } else { 60 | connection.closeDirectly(); 61 | } 62 | } 63 | 64 | private static String getKey(InetSocketAddress socketAddress) { 65 | if (socketAddress == null) { 66 | return null; 67 | } 68 | return String.format("%s:%s", socketAddress.getAddress().getHostAddress(), socketAddress.getPort()); 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | if (!CP.isEmpty()) { 74 | StringBuilder builder = new StringBuilder(); 75 | for (Map.Entry managerEntry : CP.entrySet()) { 76 | builder.append("key:[" + managerEntry.getKey() + " ]-------- entry:" + managerEntry.getValue() + "\n"); 77 | } 78 | return builder.toString(); 79 | } 80 | return null; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/service/ApiClient.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.service; 2 | 3 | import com.zhouzifei.tool.dto.CheckFileResult; 4 | import com.zhouzifei.tool.dto.VirtualFile; 5 | import com.zhouzifei.tool.entity.FileListRequesr; 6 | import com.zhouzifei.tool.entity.MetaDataRequest; 7 | import org.springframework.web.multipart.MultipartFile; 8 | 9 | import javax.servlet.http.HttpServletRequest; 10 | import java.io.File; 11 | import java.io.InputStream; 12 | import java.io.UnsupportedEncodingException; 13 | import java.util.List; 14 | 15 | /** 16 | * 17 | * @author 周子斐 (17600004572@163.com) 18 | * @version 1.0 19 | * @remark 2019年7月16日 20 | * @since 1.0 21 | */ 22 | public interface ApiClient { 23 | /** 24 | * MultipartFile类型的文件上传 25 | * @param file 26 | * @return 完成的文件信息 27 | */ 28 | VirtualFile uploadFile(MultipartFile file); 29 | 30 | /** 31 | * File类型的文件上传 32 | * @param file 33 | * @return 完成的文件信息 34 | */ 35 | VirtualFile uploadFile(File file); 36 | /** 37 | * InputStream类型的文件上传 38 | * @param is 文件流 39 | * @param fileName 文件名称 40 | * @return 完成的文件信息 41 | */ 42 | VirtualFile uploadFile(InputStream is, String fileName); 43 | 44 | /** 45 | * 删除文件 46 | * @param fileName 文件名称 47 | * @return 是否删除 48 | */ 49 | boolean removeFile(String fileName); 50 | 51 | /** 52 | * 下载文件 53 | * @param fileName 文件名称 54 | * @param localFile 本地路径 55 | */ 56 | void downloadFileToLocal(String fileName, String localFile); 57 | 58 | /** 59 | * 下载文件 60 | * @param fileName 文件名称 61 | */ 62 | InputStream downloadFileStream(String fileName); 63 | 64 | /** 65 | * 网络文件转存 66 | * @param fileUrl 文件地址 67 | * @param referer 鉴权信息 68 | * @param fileName 文件名称 69 | * @return 70 | */ 71 | VirtualFile saveToCloudStorage(String fileUrl, String referer,String fileName); 72 | 73 | /** 74 | * MultipartFile类型的文件分片上传 75 | * @param file 文件 76 | * @return 完成的文件信息 77 | */ 78 | VirtualFile multipartUpload(File file,MetaDataRequest metaDataRequest); 79 | /** 80 | * InputStream 81 | * @param inputStream 文件 82 | * @param metaDataRequest 文件名称 83 | * @return 完成的文件信息 84 | */ 85 | VirtualFile multipartUpload(InputStream inputStream, MetaDataRequest metaDataRequest); 86 | /** 87 | * InputStream 88 | * @param file 文件 89 | * @param metaDataRequest 文件名称 90 | * @return 完成的文件信息 91 | */ 92 | VirtualFile multipartUpload(MultipartFile file, MetaDataRequest metaDataRequest); 93 | /** 94 | * InputStream 95 | * @param inputStream 文件 96 | * @param fileName 文件名称 97 | * @return 断点续传 98 | */ 99 | VirtualFile resumeUpload(InputStream inputStream,String fileName); 100 | /** 101 | * InputStream 102 | * @param metaDataRequest 文件 103 | * @param request 文件名称 104 | * @return 断点续传 105 | */ 106 | abstract CheckFileResult checkFile(MetaDataRequest metaDataRequest, HttpServletRequest request); 107 | 108 | /** 109 | * 文件列表 110 | * @param fileListRequesr 111 | * @return 112 | */ 113 | abstract List fileList(FileListRequesr fileListRequesr); 114 | 115 | boolean exists(String fileName); 116 | 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/fileClient/BaiduBosApiClient.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.fileClient; 2 | 3 | import com.baidubce.Protocol; 4 | import com.baidubce.auth.DefaultBceCredentials; 5 | import com.baidubce.services.bos.BosClient; 6 | import com.baidubce.services.bos.BosClientConfiguration; 7 | import com.baidubce.services.bos.model.BosObject; 8 | import com.zhouzifei.tool.common.ServiceException; 9 | import com.zhouzifei.tool.config.BosFileProperties; 10 | import com.zhouzifei.tool.config.FileProperties; 11 | import com.zhouzifei.tool.dto.VirtualFile; 12 | import com.zhouzifei.tool.entity.FileListRequesr; 13 | import com.zhouzifei.tool.entity.MetaDataRequest; 14 | import com.zhouzifei.tool.util.StringUtils; 15 | 16 | import java.io.IOException; 17 | import java.io.InputStream; 18 | import java.util.List; 19 | 20 | /** 21 | * @author 周子斐 22 | * @date 2022/1/18 23 | * @Description 24 | */ 25 | public class BaiduBosApiClient extends BaseApiClient { 26 | 27 | private BosClient bos; 28 | 29 | @Override 30 | public BaiduBosApiClient init(FileProperties fileProperties) { 31 | BosFileProperties bosFileProperties = (BosFileProperties) fileProperties; 32 | checkDomainUrl(bosFileProperties.getDomainUrl()); 33 | this.bucketName = bosFileProperties.getBucketName(); 34 | this.endpoint = bosFileProperties.getEndpoint(); 35 | this.accessKey = bosFileProperties.getAccessKey(); 36 | this.secretKey = bosFileProperties.getSecretKey(); 37 | return this; 38 | } 39 | 40 | public BaiduBosApiClient(FileProperties fileProperties) { 41 | super("百度云"); 42 | init(fileProperties); 43 | } 44 | 45 | public BaiduBosApiClient() { 46 | super("百度云"); 47 | } 48 | 49 | @Override 50 | protected void check() { 51 | if (StringUtils.isNullOrEmpty(accessKey) || StringUtils.isNullOrEmpty(secretKey) || StringUtils.isNullOrEmpty(bucketName)) { 52 | throw new ServiceException("[" + this.storageType + "]尚未配置,文件上传功能暂时不可用!"); 53 | } 54 | BosClientConfiguration config = new BosClientConfiguration(); 55 | config.setCredentials(new DefaultBceCredentials(accessKey, secretKey)); 56 | config.setEndpoint(endpoint); 57 | config.setProtocol(Protocol.HTTPS); 58 | this.bos = new BosClient(config); 59 | boolean bucketExist = bos.doesBucketExist(bucketName); 60 | if (!bucketExist) { 61 | bos.createBucket(bucketName); 62 | } 63 | } 64 | 65 | @Override 66 | protected String uploadInputStream(InputStream is, String fileName) { 67 | bos.putObject(bucketName, fileName, is); 68 | return fileName; 69 | } 70 | 71 | @Override 72 | public boolean removeFile(String fileName) { 73 | bos.deleteObject(bucketName, fileName); 74 | return true; 75 | } 76 | 77 | @Override 78 | public InputStream downloadFileStream(String fileName) { 79 | BosObject object = bos.getObject(bucketName, fileName); 80 | try (InputStream in = object.getObjectContent()) { 81 | return in; 82 | } catch (IOException e) { 83 | throw new ServiceException("文件下载失败!platform:" + e); 84 | } 85 | } 86 | 87 | @Override 88 | public VirtualFile multipartUpload(InputStream inputStream, MetaDataRequest metaDataRequest) { 89 | return null; 90 | } 91 | 92 | @Override 93 | public List fileList(FileListRequesr fileListRequesr) { 94 | return null; 95 | } 96 | 97 | @Override 98 | public boolean exists(String fileName) { 99 | this.check(); 100 | return bos.doesObjectExist(bucketName, fileName); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/Response.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common; 2 | 3 | import java.util.Objects; 4 | 5 | public class Response { 6 | private String code; 7 | private T data; 8 | private String message; 9 | 10 | public Response withCode(String code) { 11 | this.code = code; 12 | return this; 13 | } 14 | 15 | public Response withData(T data) { 16 | this.data = data; 17 | return this; 18 | } 19 | 20 | public Response withMessage(String message) { 21 | this.message = message; 22 | return this; 23 | } 24 | 25 | public static Response ok() { 26 | return ok(null); 27 | } 28 | 29 | public static Response ok(T data) { 30 | return (new Response()).withCode("000000").withData(data); 31 | } 32 | 33 | public static Response code(String code) { 34 | return (new Response()).withCode(code); 35 | } 36 | 37 | public Response() { 38 | } 39 | 40 | public String getCode() { 41 | return this.code; 42 | } 43 | 44 | public T getData() { 45 | return this.data; 46 | } 47 | 48 | public String getMessage() { 49 | return this.message; 50 | } 51 | 52 | public void setCode(final String code) { 53 | this.code = code; 54 | } 55 | 56 | public void setData(final T data) { 57 | this.data = data; 58 | } 59 | 60 | public void setMessage(final String message) { 61 | this.message = message; 62 | } 63 | 64 | @Override 65 | public boolean equals(final Object o) { 66 | if (o == this) { 67 | return true; 68 | } else if (!(o instanceof Response)) { 69 | return false; 70 | } else { 71 | Response other = (Response)o; 72 | if (!other.canEqual(this)) { 73 | return false; 74 | } else { 75 | label47: { 76 | Object this$code = this.getCode(); 77 | Object other$code = other.getCode(); 78 | if (this$code == null) { 79 | if (other$code == null) { 80 | break label47; 81 | } 82 | } else if (this$code.equals(other$code)) { 83 | break label47; 84 | } 85 | 86 | return false; 87 | } 88 | 89 | Object this$data = this.getData(); 90 | Object other$data = other.getData(); 91 | if (this$data == null) { 92 | if (other$data != null) { 93 | return false; 94 | } 95 | } else if (!this$data.equals(other$data)) { 96 | return false; 97 | } 98 | 99 | Object this$message = this.getMessage(); 100 | Object other$message = other.getMessage(); 101 | if (this$message == null) { 102 | if (other$message != null) { 103 | return false; 104 | } 105 | } else if (!this$message.equals(other$message)) { 106 | return false; 107 | } 108 | 109 | return true; 110 | } 111 | } 112 | } 113 | 114 | protected boolean canEqual(final Object other) { 115 | return other instanceof Response; 116 | } 117 | 118 | @Override 119 | public int hashCode() { 120 | return Objects.hash(code, data, message); 121 | } 122 | 123 | @Override 124 | public String toString() { 125 | return "Response(code=" + this.getCode() + ", data=" + this.getData() + ", message=" + this.getMessage() + ")"; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/util/PatternPool.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.util; 2 | 3 | import com.zhouzifei.tool.common.SimpleCache; 4 | 5 | import java.util.regex.Pattern; 6 | 7 | public class PatternPool { 8 | public static final Pattern GENERAL = Pattern.compile("^\\w+$"); 9 | public static final Pattern NUMBERS = Pattern.compile("\\d+"); 10 | public static final Pattern WORD = Pattern.compile("[a-zA-Z]+"); 11 | public static final Pattern CHINESE = Pattern.compile("[一-\u9fff]"); 12 | public static final Pattern CHINESES = Pattern.compile("[一-\u9fff]+"); 13 | public static final Pattern GROUP_VAR = Pattern.compile("\\$(\\d+)"); 14 | public static final Pattern IPV4 = Pattern.compile("\\b((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\b"); 15 | public static final Pattern MONEY = Pattern.compile("^(\\d+(?:\\.\\d+)?)$"); 16 | public static final Pattern EMAIL = Pattern.compile("(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])", 2); 17 | public static final Pattern MOBILE = Pattern.compile("(?:0|86|\\+86)?1[3456789]\\d{9}"); 18 | public static final Pattern CITIZEN_ID = Pattern.compile("[1-9]\\d{5}[1-2]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}(\\d|X|x)"); 19 | public static final Pattern ZIP_CODE = Pattern.compile("[1-9]\\d{5}(?!\\d)"); 20 | public static final Pattern BIRTHDAY = Pattern.compile("^(\\d{2,4})([/\\-\\.年]?)(\\d{1,2})([/\\-\\.月]?)(\\d{1,2})日?$"); 21 | public static final Pattern URL = Pattern.compile("[a-zA-z]+://[^\\s]*"); 22 | public static final Pattern URL_HTTP = Pattern.compile("(https://|http://)?([\\w-]+\\.)+[\\w-]+(/[\\w- ./?%&=]*)?"); 23 | public static final Pattern GENERAL_WITH_CHINESE = Pattern.compile("^[一-\u9fff\\w]+$"); 24 | public static final Pattern UUID = Pattern.compile("^[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}$"); 25 | public static final Pattern UUID_SIMPLE = Pattern.compile("^[0-9a-z]{32}$"); 26 | public static final Pattern PLATE_NUMBER = Pattern.compile("^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$"); 27 | public static final Pattern MAC_ADDRESS = Pattern.compile("((?:[A-F0-9]{1,2}[:-]){5}[A-F0-9]{1,2})|(?:0x)(\\d{12})(?:.+ETHER)", 2); 28 | private static final SimpleCache POOL = new SimpleCache(); 29 | 30 | public PatternPool() { 31 | } 32 | 33 | public static Pattern get(String regex) { 34 | return get(regex, 0); 35 | } 36 | 37 | public static Pattern get(String regex, int flags) { 38 | PatternPool.RegexWithFlag regexWithFlag = new PatternPool.RegexWithFlag(regex, flags); 39 | Pattern pattern = (Pattern)POOL.get(regexWithFlag); 40 | if (null == pattern) { 41 | pattern = Pattern.compile(regex, flags); 42 | POOL.put(regexWithFlag, pattern); 43 | } 44 | 45 | return pattern; 46 | } 47 | 48 | public static Pattern remove(String regex, int flags) { 49 | return (Pattern)POOL.remove(new PatternPool.RegexWithFlag(regex, flags)); 50 | } 51 | 52 | public static void clear() { 53 | POOL.clear(); 54 | } 55 | 56 | private static class RegexWithFlag { 57 | private String regex; 58 | private int flag; 59 | 60 | public RegexWithFlag(String regex, int flag) { 61 | this.regex = regex; 62 | this.flag = flag; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/util/StringUtils.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.util; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.regex.Pattern; 5 | 6 | /** 7 | * @author 周子斐 (17600004572@163.com) 8 | * @remark 2019/12/14 16:27 9 | * @version 1.0 10 | */ 11 | 12 | public class StringUtils{ 13 | 14 | public static boolean isBlank(String str) { 15 | return str == null || str.length() == 0; 16 | } 17 | public static boolean isBlank(String... strs) { 18 | for (String str : strs) { 19 | return str == null || str.length() == 0; 20 | } 21 | return true; 22 | } 23 | 24 | public static boolean isEmpty(String str) { 25 | return str == null || str.trim().length() == 0; 26 | } 27 | 28 | public static boolean isNotBlank(String str) { 29 | return !isBlank(str); 30 | } 31 | 32 | public static boolean isNotEmpty(String str) { 33 | return !isEmpty(str); 34 | } 35 | 36 | public static boolean isUrl(String str) { 37 | if (isEmpty(str)) { 38 | return false; 39 | } 40 | 41 | str = str.trim(); 42 | return str.matches("^(http|https)://.+"); 43 | } 44 | 45 | public static String convertToDownloadSpeed(BigDecimal bigDecimal, int scale) { 46 | BigDecimal unit = new BigDecimal(1); 47 | BigDecimal kb = new BigDecimal(1 << 10); 48 | BigDecimal mb = new BigDecimal(1 << 10).multiply(kb); 49 | BigDecimal gb = new BigDecimal(1 << 10).multiply(mb); 50 | BigDecimal tb = new BigDecimal(1 << 10).multiply(gb); 51 | BigDecimal pb = new BigDecimal(1 << 10).multiply(tb); 52 | BigDecimal eb = new BigDecimal(1 << 10).multiply(pb); 53 | if (bigDecimal.divide(kb, scale, BigDecimal.ROUND_HALF_UP).compareTo(unit) < 0) { 54 | return bigDecimal.divide(unit, scale, BigDecimal.ROUND_HALF_UP).toString() + " B"; 55 | } else if (bigDecimal.divide(mb, scale, BigDecimal.ROUND_HALF_UP).compareTo(unit) < 0) { 56 | return bigDecimal.divide(kb, scale, BigDecimal.ROUND_HALF_UP).toString() + " KB"; 57 | } else if (bigDecimal.divide(gb, scale, BigDecimal.ROUND_HALF_UP).compareTo(unit) < 0) { 58 | return bigDecimal.divide(mb, scale, BigDecimal.ROUND_HALF_UP).toString() + " MB"; 59 | } else if (bigDecimal.divide(tb, scale, BigDecimal.ROUND_HALF_UP).compareTo(unit) < 0) { 60 | return bigDecimal.divide(gb, scale, BigDecimal.ROUND_HALF_UP).toString() + " GB"; 61 | } else if (bigDecimal.divide(pb, scale, BigDecimal.ROUND_HALF_UP).compareTo(unit) < 0) { 62 | return bigDecimal.divide(tb, scale, BigDecimal.ROUND_HALF_UP).toString() + " TB"; 63 | } else if (bigDecimal.divide(eb, scale, BigDecimal.ROUND_HALF_UP).compareTo(unit) < 0) { 64 | return bigDecimal.divide(pb, scale, BigDecimal.ROUND_HALF_UP).toString() + " PB"; 65 | } 66 | return bigDecimal.divide(eb, scale, BigDecimal.ROUND_HALF_UP).toString() + " EB"; 67 | } 68 | 69 | public static byte[] hexStringToByteArray(String s) { 70 | int len = s.length(); 71 | if ((len & 1) == 1) { 72 | s = "0" + s; 73 | len++; 74 | } 75 | byte[] data = new byte[len / 2]; 76 | for (int i = 0; i < len; i += 2) { 77 | data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) 78 | + Character.digit(s.charAt(i + 1), 16)); 79 | } 80 | return data; 81 | } 82 | public static boolean isMatch(String regex, String content) { 83 | if (content == null) { 84 | return false; 85 | } else if (isEmpty(regex)) { 86 | return true; 87 | } else { 88 | Pattern pattern = PatternPool.get(regex, 32); 89 | return isMatch(pattern, content); 90 | } 91 | } 92 | public static boolean isMatch(Pattern pattern, String content) { 93 | return (content != null && pattern != null) && pattern.matcher(content).matches(); 94 | } 95 | public static boolean isNullOrEmpty(String s) { 96 | return isBlank(s) || isEmpty(s); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/fileClient/LocalApiClient.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.fileClient; 2 | 3 | import com.zhouzifei.tool.common.ServiceException; 4 | import com.zhouzifei.tool.config.FileProperties; 5 | import com.zhouzifei.tool.config.LocalFileProperties; 6 | import com.zhouzifei.tool.dto.VirtualFile; 7 | import com.zhouzifei.tool.entity.FileListRequesr; 8 | import com.zhouzifei.tool.entity.MetaDataRequest; 9 | import com.zhouzifei.tool.media.file.util.StreamUtil; 10 | import com.zhouzifei.tool.service.ApiClient; 11 | import com.zhouzifei.tool.util.FileUtil; 12 | import com.zhouzifei.tool.util.MediaFormat; 13 | import com.zhouzifei.tool.util.StringUtils; 14 | import org.springframework.util.DigestUtils; 15 | import org.springframework.util.FileCopyUtils; 16 | 17 | import java.io.*; 18 | import java.util.Date; 19 | import java.util.List; 20 | 21 | /** 22 | * @author 周子斐 (17600004572@163.com) 23 | * @version 1.0 24 | * @remark 2019年7月16日 25 | * @since 1.0 26 | */ 27 | public class LocalApiClient extends BaseApiClient { 28 | 29 | private String localFilePath; 30 | 31 | public LocalApiClient() { 32 | super("Nginx文件服务器"); 33 | } 34 | public LocalApiClient(FileProperties fileProperties) { 35 | super("Nginx文件服务器"); 36 | init(fileProperties); 37 | } 38 | 39 | @Override 40 | public ApiClient init(FileProperties fileProperties) { 41 | final LocalFileProperties localFileProperties = (LocalFileProperties)fileProperties; 42 | domainUrl = localFileProperties.getDomainUrl(); 43 | String localFilePath = localFileProperties.getLocalFilePath(); 44 | if (StringUtils.isEmpty(domainUrl) || StringUtils.isEmpty(localFilePath)) { 45 | throw new ServiceException("[" + this.storageType + "]尚未配置Nginx文件服务器,文件上传功能暂时不可用!"); 46 | } 47 | checkDomainUrl(domainUrl); 48 | final File file = new File(localFilePath); 49 | if(!file.exists()){ 50 | file.mkdirs(); 51 | } 52 | this.localFilePath = localFilePath; 53 | return this; 54 | } 55 | 56 | @Override 57 | public String uploadInputStream(InputStream is, String userName) { 58 | String realFilePath = this.localFilePath + this.newFileName; 59 | try (FileOutputStream fos = new FileOutputStream(realFilePath)) { 60 | FileCopyUtils.copy(is, fos); 61 | return this.newFileName; 62 | } catch (Exception e) { 63 | throw new ServiceException("[" + this.storageType + "]文件上传失败:" + e.getMessage() + userName); 64 | } 65 | } 66 | 67 | @Override 68 | public boolean removeFile(String key) { 69 | if (StringUtils.isEmpty(key)) { 70 | throw new ServiceException("[" + this.storageType + "]删除文件失败:文件key为空"); 71 | } 72 | File file = new File(this.localFilePath + key); 73 | if (!file.exists()) { 74 | throw new ServiceException("[" + this.storageType + "]删除文件失败:文件不存在[" + this.localFilePath + key + "]"); 75 | } 76 | try { 77 | return file.delete(); 78 | } catch (Exception e) { 79 | throw new ServiceException("[" + this.storageType + "]删除文件失败:" + e.getMessage()); 80 | } 81 | } 82 | 83 | @Override 84 | public VirtualFile multipartUpload(InputStream inputStream, MetaDataRequest metaDataRequest) { 85 | return null; 86 | } 87 | 88 | @Override 89 | public List fileList(FileListRequesr fileListRequesr){ 90 | return null; 91 | } 92 | 93 | @Override 94 | public boolean exists(String fileName) { 95 | return false; 96 | } 97 | 98 | @Override 99 | protected void check() { 100 | String realFilePath = this.localFilePath + this.newFileName; 101 | FileUtil.newFiles(realFilePath); 102 | } 103 | 104 | @Override 105 | public InputStream downloadFileStream(String userName) { 106 | return FileUtil.getInputStreamByUrl(domainUrl + userName, ""); 107 | } 108 | @Override 109 | public ApiClient getAwsApiClient(){ 110 | throw new ServiceException("[" + this.storageType + "]暂不支持AWS3协议!"); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/fastdfs/pool/Connection.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common.fastdfs.pool; 2 | 3 | import com.zhouzifei.tool.common.fastdfs.ClientGlobal; 4 | import com.zhouzifei.tool.common.fastdfs.ProtoCommon; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.io.OutputStream; 9 | import java.net.InetSocketAddress; 10 | import java.net.Socket; 11 | 12 | public class Connection { 13 | 14 | private Socket sock; 15 | private InetSocketAddress inetSockAddr; 16 | private Long lastAccessTime = System.currentTimeMillis(); 17 | 18 | private boolean needActiveTest = false; 19 | 20 | public Connection(Socket sock, InetSocketAddress inetSockAddr) { 21 | this.sock = sock; 22 | this.inetSockAddr = inetSockAddr; 23 | } 24 | 25 | /** 26 | * get the server info 27 | * 28 | * @return the server info 29 | */ 30 | public InetSocketAddress getInetSocketAddress() { 31 | return this.inetSockAddr; 32 | } 33 | 34 | public OutputStream getOutputStream() throws IOException { 35 | return this.sock.getOutputStream(); 36 | } 37 | 38 | public InputStream getInputStream() throws IOException { 39 | return this.sock.getInputStream(); 40 | } 41 | 42 | public Long getLastAccessTime() { 43 | return lastAccessTime; 44 | } 45 | 46 | public void setLastAccessTime(Long lastAccessTime) { 47 | this.lastAccessTime = lastAccessTime; 48 | } 49 | 50 | /** 51 | * 52 | * @throws IOException 53 | */ 54 | public void close() throws IOException { 55 | //if connection enabled get from connection pool 56 | if (ClientGlobal.g_connection_pool_enabled) { 57 | ConnectionPool.closeConnection(this); 58 | } else { 59 | this.closeDirectly(); 60 | } 61 | } 62 | 63 | public void release() throws IOException { 64 | if (ClientGlobal.g_connection_pool_enabled) { 65 | ConnectionPool.releaseConnection(this); 66 | } else { 67 | this.closeDirectly(); 68 | } 69 | } 70 | 71 | /** 72 | * force close socket, 73 | */ 74 | public void closeDirectly() throws IOException { 75 | if (this.sock != null) { 76 | try { 77 | ProtoCommon.closeSocket(this.sock); 78 | } finally { 79 | this.sock = null; 80 | } 81 | } 82 | } 83 | 84 | public boolean activeTest() throws IOException { 85 | if (this.sock == null) { 86 | return false; 87 | } 88 | return ProtoCommon.activeTest(this.sock); 89 | } 90 | 91 | public boolean isConnected() { 92 | boolean isConnected = false; 93 | if (sock != null) { 94 | if (sock.isConnected()) { 95 | isConnected = true; 96 | } 97 | } 98 | return isConnected; 99 | } 100 | public boolean isAvaliable() { 101 | if (isConnected()) { 102 | if (sock.getPort() == 0) { 103 | return false; 104 | } 105 | if (sock.getInetAddress() == null) { 106 | return false; 107 | } 108 | if (sock.getRemoteSocketAddress() == null) { 109 | return false; 110 | } 111 | if (sock.isInputShutdown()) { 112 | return false; 113 | } 114 | if (sock.isOutputShutdown()) { 115 | return false; 116 | } 117 | return true; 118 | } 119 | return false; 120 | } 121 | 122 | public boolean isNeedActiveTest() { 123 | return needActiveTest; 124 | } 125 | 126 | public void setNeedActiveTest(boolean needActiveTest) { 127 | this.needActiveTest = needActiveTest; 128 | } 129 | 130 | @Override 131 | public String toString() { 132 | return "Connection{" + 133 | "sock=" + sock + 134 | ", inetSockAddr=" + inetSockAddr + 135 | ", lastAccessTime=" + lastAccessTime + 136 | ", needActiveTest=" + needActiveTest + 137 | '}'; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/fileClient/HuaweiCloudOssApiClient.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.fileClient; 2 | 3 | 4 | import com.obs.services.ObsClient; 5 | import com.obs.services.model.DeleteObjectResult; 6 | import com.obs.services.model.ObsObject; 7 | import com.obs.services.model.PutObjectResult; 8 | import com.zhouzifei.tool.common.ServiceException; 9 | import com.zhouzifei.tool.config.FileProperties; 10 | import com.zhouzifei.tool.config.HuaweiFileProperties; 11 | import com.zhouzifei.tool.dto.CheckFileResult; 12 | import com.zhouzifei.tool.dto.VirtualFile; 13 | import com.zhouzifei.tool.entity.FileListRequesr; 14 | import com.zhouzifei.tool.entity.MetaDataRequest; 15 | import com.zhouzifei.tool.util.FileUtil; 16 | import com.zhouzifei.tool.util.RandomsUtil; 17 | import com.zhouzifei.tool.util.StringUtils; 18 | import org.springframework.web.multipart.MultipartFile; 19 | 20 | import javax.servlet.http.HttpServletRequest; 21 | import java.io.File; 22 | import java.io.IOException; 23 | import java.io.InputStream; 24 | import java.io.UnsupportedEncodingException; 25 | import java.util.Date; 26 | import java.util.List; 27 | 28 | /** 29 | * @author 周子斐 30 | * @date 2021/1/30 31 | * @Description 32 | */ 33 | public class HuaweiCloudOssApiClient extends BaseApiClient { 34 | 35 | private ObsClient obsClient; 36 | 37 | public HuaweiCloudOssApiClient() { 38 | super("华为云"); 39 | } 40 | 41 | public HuaweiCloudOssApiClient(FileProperties fileProperties) { 42 | super("华为云"); 43 | init(fileProperties); 44 | } 45 | 46 | @Override 47 | public HuaweiCloudOssApiClient init(FileProperties fileProperties) { 48 | HuaweiFileProperties huaweiFileProperties = (HuaweiFileProperties) fileProperties; 49 | this.accessKey = huaweiFileProperties.getAccessKey(); 50 | this.secretKey = huaweiFileProperties.getSecretKey(); 51 | this.endpoint = huaweiFileProperties.getEndpoint(); 52 | this.domainUrl = huaweiFileProperties.getDomainUrl(); 53 | this.bucketName = huaweiFileProperties.getBucketName(); 54 | if (StringUtils.isNullOrEmpty(accessKey) || StringUtils.isNullOrEmpty(secretKey) || StringUtils.isNullOrEmpty(endpoint)) { 55 | throw new ServiceException("[" + this.storageType + "]尚未配置华为云,文件上传功能暂时不可用!"); 56 | } 57 | // 创建ObsClient实例 58 | obsClient = new ObsClient(accessKey, secretKey, endpoint); 59 | checkDomainUrl(domainUrl); 60 | return this; 61 | } 62 | 63 | @Override 64 | public String uploadInputStream(InputStream is, String fileName) { 65 | PutObjectResult putObjectResult = obsClient.putObject(bucketName, this.newFileName, is); 66 | return putObjectResult.getObjectKey(); 67 | } 68 | 69 | @Override 70 | public boolean removeFile(String fileName) { 71 | if (StringUtils.isNullOrEmpty(fileName)) { 72 | throw new ServiceException("[" + this.storageType + "]删除文件失败:文件key为空"); 73 | } 74 | if (!exists(fileName)) { 75 | throw new ServiceException("[阿里云OSS] 文件删除失败!文件不存在:" + bucketName + "/" + fileName); 76 | } 77 | // 删除文件 78 | DeleteObjectResult deleteObjectResult = obsClient.deleteObject(bucketName, fileName); 79 | return deleteObjectResult.isDeleteMarker(); 80 | } 81 | 82 | @Override 83 | public VirtualFile multipartUpload(InputStream inputStream, MetaDataRequest metaDataRequest) { 84 | return null; 85 | } 86 | 87 | @Override 88 | public CheckFileResult checkFile(MetaDataRequest metaDataRequest, HttpServletRequest request) { 89 | return null; 90 | } 91 | 92 | @Override 93 | public List fileList(FileListRequesr fileListRequesr) { 94 | return null; 95 | } 96 | 97 | @Override 98 | public boolean exists(String fileName) { 99 | return obsClient.doesObjectExist(bucketName, fileName); 100 | } 101 | 102 | @Override 103 | protected void check() { 104 | 105 | } 106 | 107 | @Override 108 | public InputStream downloadFileStream(String key) { 109 | ObsObject obsObject = obsClient.getObject(bucketName, key); 110 | System.out.println("Object content:"); 111 | return obsObject.getObjectContent(); 112 | 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/service/FileUploader.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.service; 2 | 3 | import com.zhouzifei.tool.common.ServiceException; 4 | import com.zhouzifei.tool.config.FileProperties; 5 | import com.zhouzifei.tool.config.SimpleFsProperties; 6 | import com.zhouzifei.tool.consts.StorageTypeConst; 7 | import com.zhouzifei.tool.fileClient.*; 8 | import com.zhouzifei.tool.listener.ProgressListener; 9 | import com.zhouzifei.tool.util.StringUtils; 10 | import lombok.Builder; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Component; 13 | 14 | 15 | /** 16 | * @author 周子斐 (17600004572@163.com) 17 | * @version 1.0 18 | * @remark 2019年7月16日 19 | * @since 1.0 20 | */ 21 | @Builder 22 | public class FileUploader { 23 | 24 | 25 | SimpleFsProperties simpleFsProperties; 26 | ProgressListener progressListener; 27 | private String domainUrl; 28 | private String accessKey; 29 | private String secretKey; 30 | private String endpoint; 31 | private String region; 32 | private String bucketName; 33 | public String storageType; 34 | 35 | public ApiClient execute() { 36 | final BaseApiClient apiClient = getApiClient(); 37 | this.storageType = StringUtils.isEmpty(storageType)?simpleFsProperties.getStorageType():storageType; 38 | final FileProperties fileProperties = getFileProperties(); 39 | return apiClient.init(fileProperties); 40 | } 41 | 42 | private BaseApiClient getApiClient() { 43 | if (StorageTypeConst.LOCAL.getStorageType().equals(storageType)) { 44 | return new LocalApiClient(); 45 | } else if (StorageTypeConst.QINIUYUN.getStorageType().equals(storageType)) { 46 | return new QiniuApiClient(); 47 | } else if (StorageTypeConst.ALIYUN.getStorageType().equals(storageType)) { 48 | return new AliyunOssApiClient(); 49 | } else if (StorageTypeConst.AWSS3.getStorageType().equals(storageType)) { 50 | return new AwsS3ApiClient(); 51 | } else if (StorageTypeConst.BAIDUBOS.getStorageType().equals(storageType)) { 52 | return new BaiduBosApiClient(); 53 | } else if (StorageTypeConst.YOUPAIYUN.getStorageType().equals(storageType)) { 54 | return new UpaiyunOssApiClient(); 55 | } else if (StorageTypeConst.TENGXUNYUN.getStorageType().equals(storageType)) { 56 | return new QCloudOssApiClient(); 57 | } else if (StorageTypeConst.HUAWEIYUN.getStorageType().equals(storageType)) { 58 | return new HuaweiCloudOssApiClient(); 59 | } else if (StorageTypeConst.FASTDFS.getStorageType().equals(storageType)) { 60 | return new FastDfsOssApiClient(); 61 | } else if (StorageTypeConst.SMMS.getStorageType().equals(storageType)) { 62 | return new SmMsApiClient(); 63 | } else if (StorageTypeConst.GITHUB.getStorageType().equals(storageType)) { 64 | return new GithubApiClient(); 65 | } else { 66 | throw new ServiceException("[文件服务]请选择文件存储类型!"); 67 | } 68 | } 69 | 70 | private FileProperties getFileProperties() { 71 | if (StorageTypeConst.LOCAL.getStorageType().equals(storageType)) { 72 | return simpleFsProperties.getLocal(); 73 | } else if (StorageTypeConst.QINIUYUN.getStorageType().equals(storageType)) { 74 | return simpleFsProperties.getQiniu(); 75 | } else if (StorageTypeConst.ALIYUN.getStorageType().equals(storageType)) { 76 | return simpleFsProperties.getAliYunOss(); 77 | } else if (StorageTypeConst.AWSS3.getStorageType().equals(storageType)) { 78 | return simpleFsProperties.getAws(); 79 | } else if (StorageTypeConst.BAIDUBOS.getStorageType().equals(storageType)) { 80 | return simpleFsProperties.getBos(); 81 | } else if (StorageTypeConst.YOUPAIYUN.getStorageType().equals(storageType)) { 82 | return simpleFsProperties.getUpai(); 83 | } else if (StorageTypeConst.TENGXUNYUN.getStorageType().equals(storageType)) { 84 | return simpleFsProperties.getTengxun(); 85 | } else if (StorageTypeConst.HUAWEIYUN.getStorageType().equals(storageType)) { 86 | return simpleFsProperties.getHuawei(); 87 | } else if (StorageTypeConst.FASTDFS.getStorageType().equals(storageType)) { 88 | return simpleFsProperties.getFast(); 89 | } else if (StorageTypeConst.SMMS.getStorageType().equals(storageType)) { 90 | return simpleFsProperties.getSmms(); 91 | } else if (StorageTypeConst.GITHUB.getStorageType().equals(storageType)) { 92 | return simpleFsProperties.getGithub(); 93 | } else { 94 | throw new ServiceException("[文件服务]请选择文件存储类型!"); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/fastdfs/FileInfo.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common.fastdfs; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | 6 | /** 7 | * Server Info 8 | * 9 | * @author Happy Fish / YuQing 10 | * @version Version 1.23 11 | */ 12 | public class FileInfo { 13 | public static final short FILE_TYPE_NORMAL = 1; 14 | public static final short FILE_TYPE_APPENDER = 2; 15 | public static final short FILE_TYPE_SLAVE = 4; 16 | 17 | protected boolean fetch_from_server; 18 | protected short file_type; 19 | protected String source_ip_addr; 20 | protected long file_size; 21 | protected Date create_timestamp; 22 | protected int crc32; 23 | 24 | /** 25 | * Constructor 26 | * 27 | * @param fetch_from_server if fetch from server flag 28 | * @param file_type the file type 29 | * @param file_size the file size 30 | * @param create_timestamp create timestamp in seconds 31 | * @param crc32 the crc32 signature 32 | * @param source_ip_addr the source storage ip address 33 | */ 34 | public FileInfo(boolean fetch_from_server, short file_type, long file_size, 35 | int create_timestamp, int crc32, String source_ip_addr) 36 | { 37 | this.fetch_from_server = fetch_from_server; 38 | this.file_type = file_type; 39 | this.file_size = file_size; 40 | this.create_timestamp = new Date(create_timestamp * 1000L); 41 | this.crc32 = crc32; 42 | this.source_ip_addr = source_ip_addr; 43 | } 44 | 45 | /** 46 | * get the fetch_from_server flag 47 | * 48 | * @return the fetch_from_server flag 49 | */ 50 | public boolean getFetchFromServer() { 51 | return this.fetch_from_server; 52 | } 53 | 54 | /** 55 | * set the fetch_from_server flag 56 | * 57 | * @param fetch_from_server the fetch from server flag 58 | */ 59 | public void setFetchFromServer(boolean fetch_from_server) { 60 | this.fetch_from_server = fetch_from_server; 61 | } 62 | 63 | /** 64 | * get the file type 65 | * 66 | * @return the file type 67 | */ 68 | public short getFileType() { 69 | return this.file_type; 70 | } 71 | 72 | /** 73 | * set the file type 74 | * 75 | * @param file_type the file type 76 | */ 77 | public void setFileType(short file_type) { 78 | this.file_type = file_type; 79 | } 80 | 81 | /** 82 | * get the source ip address of the file uploaded to 83 | * 84 | * @return the source ip address of the file uploaded to 85 | */ 86 | public String getSourceIpAddr() { 87 | return this.source_ip_addr; 88 | } 89 | 90 | /** 91 | * set the source ip address of the file uploaded to 92 | * 93 | * @param source_ip_addr the source ip address 94 | */ 95 | public void setSourceIpAddr(String source_ip_addr) { 96 | this.source_ip_addr = source_ip_addr; 97 | } 98 | 99 | /** 100 | * get the file size 101 | * 102 | * @return the file size 103 | */ 104 | public long getFileSize() { 105 | return this.file_size; 106 | } 107 | 108 | /** 109 | * set the file size 110 | * 111 | * @param file_size the file size 112 | */ 113 | public void setFileSize(long file_size) { 114 | this.file_size = file_size; 115 | } 116 | 117 | /** 118 | * get the create timestamp of the file 119 | * 120 | * @return the create timestamp of the file 121 | */ 122 | public Date getCreateTimestamp() { 123 | return this.create_timestamp; 124 | } 125 | 126 | /** 127 | * set the create timestamp of the file 128 | * 129 | * @param create_timestamp create timestamp in seconds 130 | */ 131 | public void setCreateTimestamp(int create_timestamp) { 132 | this.create_timestamp = new Date(create_timestamp * 1000L); 133 | } 134 | 135 | /** 136 | * get the file CRC32 signature 137 | * 138 | * @return the file CRC32 signature 139 | */ 140 | public long getCrc32() { 141 | return this.crc32; 142 | } 143 | 144 | /** 145 | * set the create timestamp of the file 146 | * 147 | * @param crc32 the crc32 signature 148 | */ 149 | public void setCrc32(int crc32) { 150 | this.crc32 = crc32; 151 | } 152 | 153 | /** 154 | * to string 155 | * 156 | * @return string 157 | */ 158 | @Override 159 | public String toString() { 160 | SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 161 | return "fetch_from_server = " + this.fetch_from_server + ", " + 162 | "file_type = " + this.file_type + ", " + 163 | "source_ip_addr = " + this.source_ip_addr + ", " + 164 | "file_size = " + this.file_size + ", " + 165 | "create_timestamp = " + df.format(this.create_timestamp) + ", " + 166 | "crc32 = " + this.crc32; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/fileClient/UpaiyunOssApiClient.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.fileClient; 2 | 3 | 4 | import com.zhouzifei.tool.common.ServiceException; 5 | import com.zhouzifei.tool.common.upaiyun.UpaiManager; 6 | import com.zhouzifei.tool.config.FileProperties; 7 | import com.zhouzifei.tool.config.UpaiFileProperties; 8 | import com.zhouzifei.tool.dto.VirtualFile; 9 | import com.zhouzifei.tool.entity.FileListRequesr; 10 | import com.zhouzifei.tool.entity.MetaDataRequest; 11 | import com.zhouzifei.tool.service.ApiClient; 12 | import com.zhouzifei.tool.util.StringUtils; 13 | import okhttp3.Response; 14 | 15 | import java.io.IOException; 16 | import java.io.InputStream; 17 | import java.util.HashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | import java.util.Objects; 21 | 22 | /** 23 | * @author 周子斐 24 | * @date 2021/1/30 25 | * @Description 26 | */ 27 | public class UpaiyunOssApiClient extends BaseApiClient { 28 | 29 | private UpaiManager upaiManager; 30 | 31 | public UpaiyunOssApiClient() { 32 | super("又拍云"); 33 | } 34 | 35 | public UpaiyunOssApiClient(FileProperties fileProperties) { 36 | super("又拍云"); 37 | init(fileProperties); 38 | } 39 | 40 | @Override 41 | public UpaiyunOssApiClient init(FileProperties fileProperties) { 42 | final UpaiFileProperties upaiFileProperties = (UpaiFileProperties) fileProperties; 43 | this.username = upaiFileProperties.getUserName(); 44 | this.password = upaiFileProperties.getPassWord(); 45 | this.domainUrl = upaiFileProperties.getDomainUrl(); 46 | this.bucketName = upaiFileProperties.getBucketName(); 47 | checkDomainUrl(domainUrl); 48 | if (StringUtils.isNullOrEmpty(username) || StringUtils.isNullOrEmpty(password) || StringUtils.isNullOrEmpty(bucketName)) { 49 | throw new ServiceException("[" + this.storageType + "]尚未配置又拍云,文件上传功能暂时不可用!"); 50 | } 51 | upaiManager = new UpaiManager(bucketName, username, password); 52 | return this; 53 | } 54 | 55 | @Override 56 | public String uploadInputStream(InputStream is, String imageUrl) { 57 | // 切换 API 接口的域名接入点,默认为自动识别接入点 58 | upaiManager.setApiDomain(UpaiManager.ED_AUTO); 59 | // 设置连接超时时间,默认为30秒 60 | upaiManager.setTimeout(60); 61 | try { 62 | Map param = new HashMap<>(); 63 | final Response response = upaiManager.writeFile(this.newFileName, is, param); 64 | if (!response.isSuccessful()) { 65 | throw new ServiceException("[" + this.storageType + "]文件上传失败."); 66 | } 67 | return this.newFileName; 68 | } catch (IOException ex) { 69 | throw new ServiceException("[" + this.storageType + "]文件上传失败:" + ex.getMessage()); 70 | } 71 | } 72 | 73 | @Override 74 | public boolean removeFile(String key) { 75 | if (StringUtils.isNullOrEmpty(key)) { 76 | throw new ServiceException("[" + this.storageType + "]删除文件失败:文件key为空"); 77 | } 78 | try { 79 | upaiManager.deleteFile(key, null); 80 | return true; 81 | } catch (IOException e) { 82 | throw new ServiceException("[" + this.storageType + "]文件删除失败:" + e.getMessage()); 83 | } 84 | } 85 | 86 | @Override 87 | public VirtualFile multipartUpload(InputStream inputStream, MetaDataRequest metaDataRequest) { 88 | return null; 89 | } 90 | 91 | @Override 92 | public List fileList(FileListRequesr fileListRequesr) { 93 | final String fold = fileListRequesr.getFold(); 94 | final Map params = new HashMap<>(); 95 | try { 96 | upaiManager.readDirIter(fold, params); 97 | } catch (IOException e) { 98 | e.printStackTrace(); 99 | } 100 | return null; 101 | } 102 | 103 | @Override 104 | public boolean exists(String fileName) { 105 | try (Response response = upaiManager.getFileInfo(this.domainUrl + fileName)) { 106 | return StringUtils.isNotBlank(response.header("x-upyun-file-size")); 107 | } catch (IOException e) { 108 | throw new ServiceException("判断文件是否存在失败!fileInfo:" + fileName); 109 | } 110 | } 111 | 112 | @Override 113 | protected void check() { 114 | 115 | } 116 | 117 | @Override 118 | public InputStream downloadFileStream(String key) { 119 | if (StringUtils.isNullOrEmpty(key)) { 120 | throw new ServiceException("[" + this.storageType + "]下载文件失败:文件key为空"); 121 | } 122 | try { 123 | return Objects.requireNonNull(upaiManager.readFile(key).body()).byteStream(); 124 | } catch (IOException e) { 125 | throw new ServiceException("[" + this.storageType + "]文件下载失败:" + e.getMessage()); 126 | } 127 | } 128 | 129 | @Override 130 | public ApiClient getAwsApiClient() { 131 | throw new ServiceException("[" + this.storageType + "]暂不支持AWS3协议!"); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/listener/a123.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.listener; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.zhouzifei.tool.util.RandomsUtil; 5 | 6 | import javax.net.ssl.HostnameVerifier; 7 | import javax.net.ssl.HttpsURLConnection; 8 | import javax.net.ssl.SSLContext; 9 | import javax.net.ssl.SSLSession; 10 | import java.io.*; 11 | import java.net.HttpURLConnection; 12 | import java.net.URL; 13 | import java.security.KeyManagementException; 14 | import java.security.NoSuchAlgorithmException; 15 | import java.security.SecureRandom; 16 | 17 | import static com.zhouzifei.tool.util.HttpUtils.trustAllCerts; 18 | 19 | /** 20 | * @author 周子斐 21 | * @date 2022/3/30 22 | * @Description 23 | */ 24 | public class a123 { 25 | public static void main(String[] args) throws IOException, NoSuchAlgorithmException, KeyManagementException { 26 | final String fileName = "IMG_9541.JPG"; 27 | final File file1 = new File("/Users/Dabao/Downloads/IMG_9541.JPG"); 28 | final FileInputStream is = new FileInputStream(file1); 29 | final String newLine = "\r\n"; 30 | final String boundaryPrefix = "--"; 31 | // 定义数据分隔线 32 | String BOUNDARY = "Ju5tH77P15Aw350m3"; 33 | // 服务器的域名 34 | HttpsURLConnection conn = (HttpsURLConnection) new URL("https://picupload.weibo.com/interface/pic_upload.php?&mime=image/jpeg&data=base64&url=0&markpos=1&logo=&nick=0&marks=1&app=miniblog").openConnection(); 35 | // 设置为POST情 36 | conn.setRequestMethod("POST"); 37 | SSLContext sc = SSLContext.getInstance("SSL"); 38 | sc.init(null, trustAllCerts, new SecureRandom()); 39 | conn.setHostnameVerifier(new HostnameVerifier() { 40 | @Override 41 | public boolean verify(String arg0, SSLSession arg1) { 42 | return true; 43 | } 44 | }); 45 | conn.setSSLSocketFactory(sc.getSocketFactory()); 46 | // 发送POST请求必须设置如下两行 47 | conn.setDoOutput(true); 48 | conn.setDoInput(true); 49 | conn.setUseCaches(false); 50 | // 设置请求头参数 51 | conn.setRequestProperty("connection", "Keep-Alive"); 52 | conn.setRequestProperty("Charsert", "UTF-8"); 53 | conn.setRequestProperty("Cookie", "weibo.com=WBStorage=; SINAGLOBAL=831693548685.6259.1582860680784; UOR=,,login.sina.com.cn; _s_tentry=login.sina.com.cn; Apache=544756383396.53186.1582891591657; login=9249710d0a28c0029265c8b1a2d26525; route=d434df472bb5ab59af1d4577b2b5d916; login_sid_t=ca1be90e898a7dc7280b23edce20666f; cross_origin_proto=SSL; BAYEUX_BROWSER=a100-8hqrtjphin35k97zu3f4vsw; Ugrow-G0=5c7144e56a57a456abed1d1511ad79e8; MCN-G0=5b5bf693a8d1e44e3ac6227524cf3cdf; YF-V5-G0=b588ba2d01e18f0a91ee89335e0afaeb; TC-V5-G0=4de7df00d4dc12eb0897c97413797808; YF-Page-G0=86b4280420ced6d22f1c1e4dc25fe846|1601023551|1601023551; WBtopGlobal_register_version=2020102212; XSRF-TOKEN=7LOrIMXxbj9s0DuwEEIjdV-T; MCN-G0=ad3d8bbbac043ef39edd0800b5622522; SAFEIT-G0=806692d0215763995d725bf01f60132c; SSOLoginState=1645010062; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9WWTLM2YIwvmxSGuVzGnZMW25JpX5KMhUgL.FoeRS0241hqESKB2dJLoIpHiH8YLxK.LBK2LB.-LxK.L1KMLB--t; SCF=Ap0YeTLdfh7jheQk0NWltIJl5bVSJQxUB5m0VxzSFQD6oAH0cJctFHDFcmBMjsjTGUqiHc50Dma3ad_yzajkJB8.; SUB=_2A25PRp2RDeRhGeVG7FMY-CjOzjiIHXVsNYhZrDV8PUNbmtAKLRWhkW9NT4KMlpqK8-V-MZjBukZk0wO4j0R1UeyV; SRT=D.QqHBJZPA4evp4!Mb4cYGSPSGirSidNsDT!ywTcbHNEYdVruSTE9pMERt4EPKRcsrA4kJ4bWfTsVuObArNrsjVGEIKCsdI4sTT!HsTs9hJGWLiFfn4ZEPSQbd*B.vAflW-P9Rc0lR-yk5DvnJqiQVbiRVPBtS!r3JZPQVqbgVdWiMZ4siOzu4DbmKPWQPmoiJbbHW3Sk5EiGWPSwM-uTOPVpi49ndDPIOdYPSrnlMcyb4bJnJZAoTGEE4rkCJcM1OFyHVmY9NDYSOGYII!oCNqHJA8tkOGYII!noNrsJA8rkWv77; SRF=1648553409; ALF=1680089409; WBPSESS=3erGLKQsbWUwm7fv_RdLQUxuAqnOnw5GIBR4mnUQx1SN84HnGBrpWqKSg0ZqCpvuFrrCj2cjD8cHRDNn_mnm0igjEKb39rg3e_wl_DhtaQ5Bfl9Y7iXEcETGSypEYQh9"); 54 | conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY); 55 | OutputStream out = new DataOutputStream(conn.getOutputStream()); 56 | // 上传文件 57 | StringBuilder sb = new StringBuilder(); 58 | sb.append(boundaryPrefix); 59 | sb.append(BOUNDARY); 60 | sb.append(newLine); 61 | // 文件参数,photo参数名可以随意修改 62 | sb.append("Content-Disposition: form-data;name=\"pic1\";filename=\"" + fileName + "\"" + newLine); 63 | //sb.append("Content-Type:multipart/form-data"); 64 | // 参数头设置完以后需要两个换行,然后才是参数内容 65 | sb.append(newLine); 66 | //sb.append(newLine); 67 | // 将参数头的数据写入到输出流中 68 | out.write(sb.toString().getBytes()); 69 | byte[] bufferOut = new byte[1024]; 70 | int bytes = 0; 71 | // 每次读1KB数据,并且将文件数据写入到输出流中 72 | while ((bytes = is.read(bufferOut)) != -1) { 73 | out.write(bufferOut, 0, bytes); 74 | } 75 | // 最后添加换行 76 | out.write(newLine.getBytes()); 77 | is.close(); 78 | // 定义最后数据分隔线,即--加上BOUNDARY再加上--。 79 | String end_data = (boundaryPrefix + BOUNDARY + boundaryPrefix); 80 | // 写上结尾标识 81 | out.write(end_data.getBytes("UTF-8")); 82 | out.flush(); 83 | out.close(); 84 | 85 | // 定义BufferedReader输入流来读取URL的响应 86 | BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); 87 | String line = null; 88 | StringBuilder result = new StringBuilder(); 89 | while ((line = reader.readLine()) != null) { 90 | result.append(line); 91 | } 92 | final JSONObject parse = JSONObject.parseObject(result.toString()); 93 | final Object data = parse.get("data"); 94 | final JSONObject dataJosn = JSONObject.parseObject((String) data); 95 | final Object newFileUrl = dataJosn.get("url"); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/upaiyun/UpYunUtils.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common.upaiyun; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.zhouzifei.tool.common.ServiceException; 5 | import com.zhouzifei.tool.util.Base64Coder; 6 | 7 | import javax.crypto.Mac; 8 | import javax.crypto.spec.SecretKeySpec; 9 | import java.io.*; 10 | import java.security.InvalidKeyException; 11 | import java.security.MessageDigest; 12 | import java.security.NoSuchAlgorithmException; 13 | import java.security.SignatureException; 14 | import java.util.Map; 15 | 16 | public class UpYunUtils { 17 | 18 | public static final String VERSION = "upyun-java-sdk/4.2.0"; 19 | 20 | /** 21 | * 计算policy 22 | * 23 | * @param paramMap 24 | * @return 25 | */ 26 | public static String getPolicy(Map paramMap) { 27 | 28 | JSONObject obj = new JSONObject(paramMap); 29 | return Base64Coder.encodeString(obj.toString()); 30 | } 31 | 32 | /** 33 | * 计算签名 34 | * 35 | * @param policy 36 | * @param secretKey 37 | * @return 38 | */ 39 | public static String getSignature(String policy, 40 | String secretKey) { 41 | return md5(policy + "&" + secretKey); 42 | } 43 | 44 | /** 45 | * 计算md5Ø 46 | * 47 | * @param string 48 | * @return 49 | */ 50 | public static String md5(String string) { 51 | byte[] hash; 52 | try { 53 | hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8")); 54 | } catch (UnsupportedEncodingException e) { 55 | throw new RuntimeException("UTF-8 is unsupported", e); 56 | } catch (NoSuchAlgorithmException e) { 57 | throw new RuntimeException("MessageDigest不支持MD5Util", e); 58 | } 59 | StringBuilder hex = new StringBuilder(hash.length * 2); 60 | for (byte b : hash) { 61 | if ((b & 0xFF) < 0x10) { 62 | hex.append("0"); 63 | } 64 | hex.append(Integer.toHexString(b & 0xFF)); 65 | } 66 | return hex.toString(); 67 | } 68 | 69 | public static String md5(File file, int blockSize) { 70 | try { 71 | MessageDigest messageDigest = MessageDigest.getInstance("MD5"); 72 | FileInputStream in = new FileInputStream(file); 73 | byte[] buffer = new byte[blockSize]; 74 | int length; 75 | while ((length = in.read(buffer)) > 0) { 76 | messageDigest.update(buffer, 0, length); 77 | } 78 | byte[] hash = messageDigest.digest(); 79 | StringBuilder hex = new StringBuilder(hash.length * 2); 80 | for (byte b : hash) { 81 | if ((b & 0xFF) < 0x10) { 82 | hex.append("0"); 83 | } 84 | hex.append(Integer.toHexString(b & 0xFF)); 85 | } 86 | return hex.toString(); 87 | } catch (FileNotFoundException e) { 88 | throw new RuntimeException("file not found", e); 89 | } catch (IOException e) { 90 | throw new RuntimeException("file get md5 failed", e); 91 | } catch (NoSuchAlgorithmException e) { 92 | throw new RuntimeException("MessageDigest不支持MD5Util", e); 93 | } 94 | } 95 | 96 | public static String md5(byte[] bytes) { 97 | byte[] hash; 98 | try { 99 | hash = MessageDigest.getInstance("MD5").digest(bytes); 100 | } catch (NoSuchAlgorithmException e) { 101 | throw new RuntimeException("MessageDigest不支持MD5Util", e); 102 | } 103 | StringBuilder hex = new StringBuilder(hash.length * 2); 104 | for (byte b : hash) { 105 | if ((b & 0xFF) < 0x10) { 106 | hex.append("0"); 107 | } 108 | hex.append(Integer.toHexString(b & 0xFF)); 109 | } 110 | return hex.toString(); 111 | } 112 | 113 | private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1"; 114 | 115 | public static String sign(String method, String date, String path, String userName, String password, String md5) throws ServiceException { 116 | 117 | StringBuilder sb = new StringBuilder(); 118 | String sp = "&"; 119 | sb.append(method); 120 | sb.append(sp); 121 | sb.append(path); 122 | 123 | sb.append(sp); 124 | sb.append(date); 125 | 126 | if (md5 != null && md5.length() > 0) { 127 | sb.append(sp); 128 | sb.append(md5); 129 | } 130 | String raw = sb.toString().trim(); 131 | byte[] hmac = null; 132 | try { 133 | hmac = calculateRFC2104HMACRaw(password, raw); 134 | } catch (Exception e) { 135 | throw new ServiceException("calculate SHA1 wrong."); 136 | } 137 | 138 | if (hmac != null) { 139 | return "UPYUN " + userName + ":" + Base64Coder.encodeLines(hmac).trim(); 140 | } 141 | 142 | return null; 143 | } 144 | 145 | public static byte[] calculateRFC2104HMACRaw(String key, String data) 146 | throws SignatureException, NoSuchAlgorithmException, InvalidKeyException { 147 | byte[] keyBytes = key.getBytes(); 148 | SecretKeySpec signingKey = new SecretKeySpec(keyBytes, HMAC_SHA1_ALGORITHM); 149 | 150 | // Get an hmac_sha1 Mac instance and initialize with the signing key 151 | Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM); 152 | mac.init(signingKey); 153 | // Compute the hmac on input data bytes 154 | return mac.doFinal(data.getBytes()); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/fastdfs/pool/ConnectionManager.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common.fastdfs.pool; 2 | 3 | import com.zhouzifei.tool.common.ServiceException; 4 | import com.zhouzifei.tool.common.fastdfs.ClientGlobal; 5 | 6 | import java.io.IOException; 7 | import java.net.InetSocketAddress; 8 | import java.util.LinkedList; 9 | import java.util.concurrent.TimeUnit; 10 | import java.util.concurrent.atomic.AtomicInteger; 11 | import java.util.concurrent.locks.Condition; 12 | import java.util.concurrent.locks.ReentrantLock; 13 | 14 | public class ConnectionManager { 15 | 16 | private InetSocketAddress inetSocketAddress; 17 | 18 | /** 19 | * total create connection pool 20 | */ 21 | private AtomicInteger totalCount = new AtomicInteger(); 22 | 23 | /** 24 | * free connection count 25 | */ 26 | private AtomicInteger freeCount = new AtomicInteger(); 27 | 28 | /** 29 | * lock 30 | */ 31 | private ReentrantLock lock = new ReentrantLock(true); 32 | 33 | private Condition condition = lock.newCondition(); 34 | 35 | /** 36 | * free connections 37 | */ 38 | private LinkedList freeConnections = new LinkedList(); 39 | 40 | private ConnectionManager() { 41 | 42 | } 43 | 44 | public ConnectionManager(InetSocketAddress socketAddress) { 45 | this.inetSocketAddress = socketAddress; 46 | } 47 | 48 | public Connection getConnection() throws ServiceException { 49 | lock.lock(); 50 | try { 51 | Connection connection = null; 52 | while (true) { 53 | if (freeCount.get() > 0) { 54 | freeCount.decrementAndGet(); 55 | connection = freeConnections.poll(); 56 | if (!connection.isAvaliable() || (System.currentTimeMillis() - connection.getLastAccessTime()) > ClientGlobal.g_connection_pool_max_idle_time) { 57 | closeConnection(connection); 58 | continue; 59 | } 60 | if (connection.isNeedActiveTest()) { 61 | boolean isActive = false; 62 | try { 63 | isActive = connection.activeTest(); 64 | } catch (IOException e) { 65 | System.err.println("send to server[" + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort() + "] active test error ,emsg:" + e.getMessage()); 66 | isActive = false; 67 | } 68 | if (!isActive) { 69 | closeConnection(connection); 70 | continue; 71 | } else { 72 | connection.setNeedActiveTest(false); 73 | } 74 | } 75 | } else if (ClientGlobal.g_connection_pool_max_count_per_entry == 0 || totalCount.get() < ClientGlobal.g_connection_pool_max_count_per_entry) { 76 | connection = ConnectionFactory.create(this.inetSocketAddress); 77 | totalCount.incrementAndGet(); 78 | } else { 79 | try { 80 | if (condition.await(ClientGlobal.g_connection_pool_max_wait_time_in_ms, TimeUnit.MILLISECONDS)) { 81 | //wait single success 82 | continue; 83 | } 84 | throw new ServiceException("connect to server " + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort() + " fail, wait_time > " + ClientGlobal.g_connection_pool_max_wait_time_in_ms + "ms"); 85 | } catch (InterruptedException e) { 86 | e.printStackTrace(); 87 | throw new ServiceException("connect to server " + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort() + " fail, emsg:" + e.getMessage()); 88 | } 89 | } 90 | return connection; 91 | } 92 | } finally { 93 | lock.unlock(); 94 | } 95 | } 96 | 97 | public void releaseConnection(Connection connection) { 98 | if (connection == null) { 99 | return; 100 | } 101 | lock.lock(); 102 | try { 103 | connection.setLastAccessTime(System.currentTimeMillis()); 104 | freeConnections.add(connection); 105 | freeCount.incrementAndGet(); 106 | condition.signal(); 107 | } finally { 108 | lock.unlock(); 109 | } 110 | 111 | } 112 | 113 | public void closeConnection(Connection connection) { 114 | try { 115 | if (connection != null) { 116 | totalCount.decrementAndGet(); 117 | connection.closeDirectly(); 118 | } 119 | } catch (IOException e) { 120 | System.err.println("close socket[" + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort() + "] error ,emsg:" + e.getMessage()); 121 | e.printStackTrace(); 122 | } 123 | } 124 | 125 | public void setActiveTestFlag() { 126 | if (freeCount.get() > 0) { 127 | lock.lock(); 128 | try { 129 | for (Connection freeConnection : freeConnections) { 130 | freeConnection.setNeedActiveTest(true); 131 | } 132 | } finally { 133 | lock.unlock(); 134 | } 135 | } 136 | } 137 | 138 | 139 | @Override 140 | public String toString() { 141 | return "ConnectionManager{" + 142 | "ip:port='" + inetSocketAddress.getAddress().getHostAddress() + ":" + inetSocketAddress.getPort() + 143 | ", totalCount=" + totalCount + 144 | ", freeCount=" + freeCount + 145 | ", freeConnections =" + freeConnections + 146 | '}'; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/fileClient/QiniuApiClient.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.fileClient; 2 | 3 | 4 | import com.alibaba.fastjson.JSON; 5 | import com.qiniu.common.QiniuException; 6 | import com.qiniu.http.Response; 7 | import com.qiniu.storage.BucketManager; 8 | import com.qiniu.storage.Configuration; 9 | import com.qiniu.storage.Region; 10 | import com.qiniu.storage.UploadManager; 11 | import com.qiniu.storage.model.DefaultPutRet; 12 | import com.qiniu.storage.model.FileInfo; 13 | import com.qiniu.util.Auth; 14 | import com.zhouzifei.tool.common.ServiceException; 15 | import com.zhouzifei.tool.config.FileProperties; 16 | import com.zhouzifei.tool.config.QiniuFileProperties; 17 | import com.zhouzifei.tool.dto.VirtualFile; 18 | import com.zhouzifei.tool.entity.FileListRequesr; 19 | import com.zhouzifei.tool.entity.MetaDataRequest; 20 | import com.zhouzifei.tool.util.FileUtil; 21 | import com.zhouzifei.tool.util.StringUtils; 22 | 23 | import java.io.InputStream; 24 | import java.io.UnsupportedEncodingException; 25 | import java.net.URLEncoder; 26 | import java.util.List; 27 | 28 | /** 29 | * Qiniu云操作文件的api:v1 30 | * 31 | * @author 周子斐 (17600004572@163.com) 32 | * @version 1.0 33 | * @remark 2019年7月16日 34 | * @since 1.0 35 | */ 36 | public class QiniuApiClient extends BaseApiClient { 37 | 38 | private BucketManager bucketManager; 39 | private Auth auth; 40 | private Configuration cfg; 41 | 42 | public QiniuApiClient() { 43 | super("七牛云"); 44 | } 45 | 46 | public QiniuApiClient(FileProperties fileProperties) { 47 | super("七牛云"); 48 | init(fileProperties); 49 | } 50 | 51 | @Override 52 | public QiniuApiClient init(FileProperties fileProperties) { 53 | 54 | final QiniuFileProperties qiniuFileProperties = (QiniuFileProperties) fileProperties; 55 | this.accessKey = qiniuFileProperties.getAccessKey(); 56 | this.secretKey = qiniuFileProperties.getSecretKey(); 57 | this.bucketName = qiniuFileProperties.getBucketName(); 58 | this.domainUrl = qiniuFileProperties.getDomainUrl(); 59 | checkDomainUrl(domainUrl); 60 | if (StringUtils.isNullOrEmpty(accessKey) 61 | || StringUtils.isNullOrEmpty(secretKey) 62 | || StringUtils.isNullOrEmpty(bucketName)) { 63 | throw new ServiceException("[" + this.storageType + "]尚未配置七牛云,文件上传功能暂时不可用!"); 64 | } 65 | if (StringUtils.isEmpty(qiniuFileProperties.getRegion())) { 66 | cfg = new Configuration(Region.autoRegion()); 67 | } else { 68 | String zone = qiniuFileProperties.getRegion(); 69 | Region region = new Region.Builder() 70 | .region(zone) 71 | .accUpHost("up-" + zone + ".qiniup.com") 72 | .iovipHost("iovip-" + zone + ".qiniuio.com") 73 | .rsHost("rs-" + zone + ".qiniuapi.com") 74 | .rsfHost("rsf-" + zone + ".qiniuapi.com") 75 | .apiHost("api.qiniuapi.com") 76 | .build(); 77 | cfg = new Configuration(region); 78 | } 79 | auth = Auth.create(accessKey, secretKey); 80 | bucketManager = new BucketManager(auth, cfg); 81 | Configuration cfg = new Configuration(Region.autoRegion()); 82 | 83 | //实例化一个BucketManager对象 84 | bucketManager = new BucketManager(auth, cfg); 85 | return this; 86 | } 87 | 88 | /** 89 | * 上传图片 90 | * 91 | * @param is 图片流 92 | * @param fileName 图片路径 93 | * @return 上传后的路径 94 | */ 95 | @Override 96 | public String uploadInputStream(InputStream is, String fileName) { 97 | //Zone.zone0:华东 98 | //Zone.zone1:华北 99 | //Zone.zone2:华南 100 | //Zone.zoneNa0:北美 101 | UploadManager uploadManager = new UploadManager(cfg); 102 | try { 103 | String upToken = auth.uploadToken(this.bucketName); 104 | Response response = uploadManager.put(is, this.newFileName, upToken, null, null); 105 | //解析上传成功的结果 106 | DefaultPutRet putRet = JSON.parseObject(response.bodyString(), DefaultPutRet.class); 107 | return putRet.key; 108 | } catch (QiniuException ex) { 109 | throw new ServiceException("[" + this.storageType + "]文件上传失败:" + ex.error()); 110 | } 111 | } 112 | 113 | /** 114 | * 删除七牛空间图片方法 115 | * 116 | * @param key 七牛空间中文件名称 117 | */ 118 | @Override 119 | public boolean removeFile(String key) { 120 | if (StringUtils.isNullOrEmpty(key)) { 121 | throw new ServiceException("[" + this.storageType + "]删除文件失败:文件key为空"); 122 | } 123 | try { 124 | Response re = bucketManager.delete(this.bucketName, key); 125 | return re.isOK(); 126 | } catch (QiniuException e) { 127 | Response r = e.response; 128 | throw new ServiceException("[" + this.storageType + "]删除文件发生异常:" + r.toString()); 129 | } 130 | } 131 | 132 | @Override 133 | public VirtualFile multipartUpload(InputStream inputStream, MetaDataRequest metaDataRequest) { 134 | return null; 135 | } 136 | 137 | @Override 138 | public List fileList(FileListRequesr fileListRequesr) { 139 | return null; 140 | } 141 | 142 | @Override 143 | public boolean exists(String fileName) { 144 | try { 145 | FileInfo stat = bucketManager.stat(bucketName,this.domainUrl + fileName); 146 | if (stat != null && stat.md5 != null) { 147 | return true; 148 | } 149 | } catch (QiniuException e) { 150 | return false; 151 | } 152 | return false; 153 | } 154 | 155 | @Override 156 | protected void check() { 157 | 158 | } 159 | 160 | @Override 161 | public InputStream downloadFileStream(String key) { 162 | try { 163 | String encodedFileName = URLEncoder.encode(key, "utf-8").replace("+", "%20"); 164 | String publicUrl = String.format("%s/%s", this.domainUrl, encodedFileName); 165 | long expireInSeconds = 3600; 166 | String finalUrl = auth.privateDownloadUrl(publicUrl, expireInSeconds); 167 | return FileUtil.getInputStreamByUrl(finalUrl, ""); 168 | } catch (UnsupportedEncodingException e) { 169 | throw new ServiceException("[" + this.storageType + "]下载文件发生异常:" + e); 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/fastdfs/common/IniFileReader.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2008 Happy Fish / YuQing 3 | *

4 | * FastDFS Java Client may be copied only under the terms of the GNU Lesser 5 | * General Public License (LGPL). 6 | * Please visit the FastDFS Home Page https://github.com/happyfish100/fastdfs for more detail. 7 | **/ 8 | 9 | package com.zhouzifei.tool.media.file.common.fastdfs.common; 10 | 11 | import java.io.*; 12 | import java.util.ArrayList; 13 | import java.util.Hashtable; 14 | 15 | /** 16 | * ini file reader / parser 17 | * 18 | * @author Happy Fish / YuQing 19 | * @version Version 1.0 20 | */ 21 | public class IniFileReader { 22 | private Hashtable paramTable; 23 | private String conf_filename; 24 | 25 | /** 26 | * @param conf_filename config filename 27 | */ 28 | public IniFileReader(String conf_filename) throws IOException { 29 | this.conf_filename = conf_filename; 30 | loadFromFile(conf_filename); 31 | } 32 | 33 | public static ClassLoader classLoader() { 34 | ClassLoader loader = Thread.currentThread().getContextClassLoader(); 35 | if (loader == null) { 36 | loader = ClassLoader.getSystemClassLoader(); 37 | } 38 | return loader; 39 | } 40 | 41 | public static InputStream loadFromOsFileSystemOrClasspathAsStream(String filePath) { 42 | InputStream in = null; 43 | try { 44 | // 优先从文件系统路径加载 45 | if (new File(filePath).exists()) { 46 | in = new FileInputStream(filePath); 47 | //System.out.println("loadFrom...file path done"); 48 | } 49 | // 从类路径加载 50 | else { 51 | in = classLoader().getResourceAsStream(filePath); 52 | //System.out.println("loadFrom...class path done"); 53 | } 54 | } catch (Exception ex) { 55 | ex.printStackTrace(); 56 | } 57 | return in; 58 | } 59 | 60 | /** 61 | * get the config filename 62 | * 63 | * @return config filename 64 | */ 65 | public String getConfFilename() { 66 | return this.conf_filename; 67 | } 68 | 69 | /** 70 | * get string value from config file 71 | * 72 | * @param name item name in config file 73 | * @return string value 74 | */ 75 | public String getStrValue(String name) { 76 | Object obj; 77 | obj = this.paramTable.get(name); 78 | if (obj == null) { 79 | return null; 80 | } 81 | 82 | if (obj instanceof String) { 83 | return (String) obj; 84 | } 85 | 86 | return (String) ((ArrayList) obj).get(0); 87 | } 88 | 89 | /** 90 | * get int value from config file 91 | * 92 | * @param name item name in config file 93 | * @param default_value the default value 94 | * @return int value 95 | */ 96 | public int getIntValue(String name, int default_value) { 97 | String szValue = this.getStrValue(name); 98 | if (szValue == null) { 99 | return default_value; 100 | } 101 | 102 | return Integer.parseInt(szValue); 103 | } 104 | 105 | /** 106 | * get boolean value from config file 107 | * 108 | * @param name item name in config file 109 | * @param default_value the default value 110 | * @return boolean value 111 | */ 112 | public boolean getBoolValue(String name, boolean default_value) { 113 | String szValue = this.getStrValue(name); 114 | if (szValue == null) { 115 | return default_value; 116 | } 117 | 118 | return szValue.equalsIgnoreCase("yes") || szValue.equalsIgnoreCase("on") || 119 | szValue.equalsIgnoreCase("true") || szValue.equals("1"); 120 | } 121 | 122 | /** 123 | * get all values from config file 124 | * 125 | * @param name item name in config file 126 | * @return string values (array) 127 | */ 128 | public String[] getValues(String name) { 129 | Object obj; 130 | String[] values; 131 | 132 | obj = this.paramTable.get(name); 133 | if (obj == null) { 134 | return null; 135 | } 136 | 137 | if (obj instanceof String) { 138 | values = new String[1]; 139 | values[0] = (String) obj; 140 | return values; 141 | } 142 | 143 | Object[] objs = ((ArrayList) obj).toArray(); 144 | values = new String[objs.length]; 145 | System.arraycopy(objs, 0, values, 0, objs.length); 146 | return values; 147 | } 148 | 149 | private void loadFromFile(String confFilePath) throws IOException { 150 | InputStream in = loadFromOsFileSystemOrClasspathAsStream(confFilePath); 151 | try { 152 | readToParamTable(in); 153 | } catch (Exception ex) { 154 | ex.printStackTrace(); 155 | } finally { 156 | try { 157 | if (in != null) in.close(); 158 | //System.out.println("loadFrom...finally...in.close(); done"); 159 | } catch (Exception ex) { 160 | ex.printStackTrace(); 161 | } 162 | } 163 | } 164 | 165 | private void readToParamTable(InputStream in) throws IOException { 166 | this.paramTable = new Hashtable(); 167 | if (in == null) return; 168 | String line; 169 | String[] parts; 170 | String name; 171 | String value; 172 | Object obj; 173 | ArrayList valueList; 174 | InputStreamReader inReader = null; 175 | BufferedReader bufferedReader = null; 176 | try { 177 | inReader = new InputStreamReader(in); 178 | bufferedReader = new BufferedReader(inReader); 179 | while ((line = bufferedReader.readLine()) != null) { 180 | line = line.trim(); 181 | if (line.length() == 0 || line.charAt(0) == '#') { 182 | continue; 183 | } 184 | parts = line.split("=", 2); 185 | if (parts.length != 2) { 186 | continue; 187 | } 188 | name = parts[0].trim(); 189 | value = parts[1].trim(); 190 | obj = this.paramTable.get(name); 191 | if (obj == null) { 192 | this.paramTable.put(name, value); 193 | } else if (obj instanceof String) { 194 | valueList = new ArrayList(); 195 | valueList.add(obj); 196 | valueList.add(value); 197 | this.paramTable.put(name, valueList); 198 | } else { 199 | valueList = (ArrayList) obj; 200 | valueList.add(value); 201 | } 202 | } 203 | } catch (Exception ex) { 204 | ex.printStackTrace(); 205 | } finally { 206 | try { 207 | if (bufferedReader != null) bufferedReader.close(); 208 | if (inReader != null) inReader.close(); 209 | //System.out.println("readToParamTable...finally...bufferedReader.close();inReader.close(); done"); 210 | } catch (Exception ex) { 211 | ex.printStackTrace(); 212 | } 213 | } 214 | } 215 | 216 | } 217 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/fileClient/QCloudOssApiClient.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.fileClient; 2 | 3 | 4 | import com.qcloud.cos.COSClient; 5 | import com.qcloud.cos.ClientConfig; 6 | import com.qcloud.cos.auth.BasicCOSCredentials; 7 | import com.qcloud.cos.auth.COSCredentials; 8 | import com.qcloud.cos.exception.CosClientException; 9 | import com.qcloud.cos.model.*; 10 | import com.qcloud.cos.region.Region; 11 | import com.qcloud.cos.transfer.TransferManager; 12 | import com.qcloud.cos.transfer.TransferManagerConfiguration; 13 | import com.qcloud.cos.transfer.Upload; 14 | import com.zhouzifei.tool.common.ServiceException; 15 | import com.zhouzifei.tool.config.FileProperties; 16 | import com.zhouzifei.tool.config.QcloudFileProperties; 17 | import com.zhouzifei.tool.dto.VirtualFile; 18 | import com.zhouzifei.tool.entity.FileListRequesr; 19 | import com.zhouzifei.tool.entity.MetaDataRequest; 20 | import com.zhouzifei.tool.util.StringUtils; 21 | import org.junit.Test; 22 | 23 | import java.io.InputStream; 24 | import java.io.UnsupportedEncodingException; 25 | import java.net.URLDecoder; 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | import java.util.concurrent.ExecutorService; 29 | import java.util.concurrent.Executors; 30 | 31 | /** 32 | * @author 周子斐 33 | * @date 2021/1/30 34 | * @Description 35 | */ 36 | public class QCloudOssApiClient extends BaseApiClient { 37 | 38 | private COSClient cosClient; 39 | private TransferManager transferManager; 40 | 41 | public QCloudOssApiClient() { 42 | super("腾讯云"); 43 | } 44 | public QCloudOssApiClient(FileProperties fileProperties) { 45 | super("腾讯云"); 46 | init(fileProperties); 47 | } 48 | 49 | @Override 50 | public QCloudOssApiClient init(FileProperties fileProperties) { 51 | final QcloudFileProperties qcloudFileProperties = (QcloudFileProperties) fileProperties; 52 | this.accessKey = qcloudFileProperties.getAccessKey(); 53 | this.secretKey = qcloudFileProperties.getSecretKey(); 54 | this.endpoint = qcloudFileProperties.getEndpoint(); 55 | this.domainUrl = qcloudFileProperties.getDomainUrl(); 56 | this.bucketName = qcloudFileProperties.getBucketName(); 57 | checkDomainUrl(this.domainUrl); 58 | if (StringUtils.isNullOrEmpty(accessKey) || StringUtils.isNullOrEmpty(secretKey) || StringUtils.isNullOrEmpty(bucketName)) { 59 | throw new ServiceException("[" + this.storageType + "]尚未配置腾讯云,文件上传功能暂时不可用!"); 60 | } 61 | COSCredentials cred = new BasicCOSCredentials(accessKey, secretKey); 62 | Region region = new Region(endpoint); 63 | ClientConfig clientConfig = new ClientConfig(region); 64 | cosClient = new COSClient(cred, clientConfig); 65 | return this; 66 | } 67 | 68 | @Override 69 | public String uploadInputStream(InputStream is, String fileName) { 70 | ObjectMetadata objectMetadata = new ObjectMetadata(); 71 | PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, this.newFileName, is, objectMetadata); 72 | Upload upload = transferManager.upload(putObjectRequest); 73 | try { 74 | UploadResult uploadResult = upload.waitForUploadResult(); 75 | return uploadResult.getKey(); 76 | } catch (InterruptedException e) { 77 | e.printStackTrace(); 78 | } 79 | return fileName; 80 | } 81 | 82 | @Override 83 | public boolean removeFile(String key) { 84 | if (StringUtils.isNullOrEmpty(key)) { 85 | throw new ServiceException("[" + this.storageType + "]删除文件失败:文件key为空"); 86 | } 87 | // 删除文件 88 | cosClient.deleteObject(bucketName, key); 89 | return true; 90 | } 91 | 92 | @Override 93 | public VirtualFile multipartUpload(InputStream inputStream, MetaDataRequest metaDataRequest) { 94 | return null; 95 | } 96 | 97 | @Override 98 | public List fileList(FileListRequesr fileListRequesr) { 99 | List virtualFiles = new ArrayList<>(); 100 | // 指定返回结果使用URL编码,则您需要对结果中的prefix、delemiter、startAfter、key和commonPrefix进行URL解码。 101 | ObjectListing objectListing = null; 102 | do { 103 | ListObjectsRequest listObjectsRequest = new ListObjectsRequest(); 104 | // 设置 bucket 名称 105 | listObjectsRequest.setBucketName(bucketName); 106 | // 设置列出的对象名以 prefix 为前缀 107 | listObjectsRequest.setPrefix(fileListRequesr.getPrefix()); 108 | // 设置最大列出多少个对象, 一次 listobject 最大支持1000 109 | listObjectsRequest.setMaxKeys(fileListRequesr.getSize()); 110 | try { 111 | objectListing = cosClient.listObjects(listObjectsRequest); 112 | } catch (CosClientException e) { 113 | e.printStackTrace(); 114 | } 115 | // 文件名称解码。 116 | for (COSObjectSummary s : objectListing.getObjectSummaries()) { 117 | String decodedKey = null; 118 | try { 119 | decodedKey = URLDecoder.decode(s.getKey(), "UTF-8"); 120 | } catch (UnsupportedEncodingException e) { 121 | e.printStackTrace(); 122 | } 123 | VirtualFile virtualFile = VirtualFile.builder().originalFileName(decodedKey).suffix(this.suffix).uploadStartTime(s.getLastModified()).uploadEndTime(s.getLastModified()).filePath(this.newFileName).size(s.getSize()).fileHash(s.getETag()).fullFilePath(decodedKey).build(); 124 | virtualFiles.add(virtualFile); 125 | } 126 | String nextContinuationToken = objectListing.getNextMarker(); 127 | } while (objectListing.isTruncated()); 128 | // 确认本进程不再使用 cosClient 实例之后,关闭之 129 | cosClient.shutdown(); 130 | return virtualFiles; 131 | } 132 | 133 | @Override 134 | public boolean exists(String fileName) { 135 | return cosClient.doesObjectExist(bucketName,fileName); 136 | } 137 | 138 | @Override 139 | protected void check() { 140 | // 自定义线程池大小,建议在客户端与 COS 网络充足(例如使用腾讯云的 CVM,同地域上传 COS)的情况下,设置成16或32即可,可较充分的利用网络资源 141 | // 对于使用公网传输且网络带宽质量不高的情况,建议减小该值,避免因网速过慢,造成请求超时。 142 | ExecutorService threadPool = Executors.newFixedThreadPool(32); 143 | // 传入一个 threadpool, 若不传入线程池,默认 TransferManager 中会生成一个单线程的线程池。 144 | TransferManager transferManager = new TransferManager(cosClient, threadPool); 145 | // 设置高级接口的配置项 146 | // 分块上传阈值和分块大小分别为 5MB 和 1MB 147 | TransferManagerConfiguration transferManagerConfiguration = new TransferManagerConfiguration(); 148 | transferManagerConfiguration.setMultipartUploadThreshold(5 * 1024 * 1024); 149 | transferManagerConfiguration.setMinimumUploadPartSize(1 * 1024 * 1024); 150 | transferManager.setConfiguration(transferManagerConfiguration); 151 | this.transferManager = transferManager; 152 | } 153 | 154 | @Override 155 | public InputStream downloadFileStream(String key) { 156 | GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, key); 157 | COSObject object = cosClient.getObject(getObjectRequest); 158 | return object.getObjectContent(); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/fileClient/BaseApiClient.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.fileClient; 2 | 3 | import com.zhouzifei.cache.FileCacheEngine; 4 | import com.zhouzifei.tool.common.ServiceException; 5 | import com.zhouzifei.tool.config.FileProperties; 6 | import com.zhouzifei.tool.config.SimpleFsProperties; 7 | import com.zhouzifei.tool.dto.CheckFileResult; 8 | import com.zhouzifei.tool.dto.VirtualFile; 9 | import com.zhouzifei.tool.entity.MetaDataRequest; 10 | import com.zhouzifei.tool.listener.ProgressListener; 11 | import com.zhouzifei.tool.media.file.util.StreamUtil; 12 | import com.zhouzifei.tool.service.ApiClient; 13 | import com.zhouzifei.tool.util.FileUtil; 14 | import com.zhouzifei.tool.util.RandomsUtil; 15 | import com.zhouzifei.tool.util.StringUtils; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.stereotype.Component; 18 | import org.springframework.util.DigestUtils; 19 | import org.springframework.web.multipart.MultipartFile; 20 | 21 | import javax.servlet.http.HttpServletRequest; 22 | import java.io.*; 23 | import java.util.Date; 24 | 25 | /** 26 | * @author 周子斐 (17600004572@163.com) 27 | * @version 1.0 28 | * @remark 2019年7月16日 29 | * @since 1.0 30 | */ 31 | @Component 32 | public abstract class BaseApiClient implements ApiClient { 33 | 34 | protected String storageType; 35 | protected String folder = ""; 36 | public ProgressListener progressListener = newListener(); 37 | protected String suffix; 38 | protected String newFileName; 39 | protected String domainUrl; 40 | protected String accessKey; 41 | protected String secretKey; 42 | protected String region; 43 | protected String endpoint; 44 | protected String bucketName; 45 | protected String username; 46 | protected String password; 47 | protected String token; 48 | protected final Object LOCK = new Object(); 49 | protected final FileCacheEngine cacheEngine = new FileCacheEngine(); 50 | protected static final String SLASH = "/"; 51 | protected static final String TAG = "PartETag"; 52 | protected static final Integer ONE_INT = 1; 53 | SimpleFsProperties simpleFsProperties; 54 | 55 | protected ProgressListener newListener() { 56 | return new ProgressListener() { 57 | @Override 58 | public void start(String msg) { 59 | } 60 | 61 | @Override 62 | public void process(int finished, int sum) { 63 | } 64 | 65 | @Override 66 | public void end(VirtualFile virtualFile) { 67 | } 68 | }; 69 | } 70 | 71 | public BaseApiClient(String storageType) { 72 | this.storageType = storageType; 73 | } 74 | 75 | public abstract ApiClient init(FileProperties fileProperties); 76 | public ApiClient setProgressListener(ProgressListener progressListener) { 77 | this.progressListener = progressListener; 78 | return this; 79 | } 80 | 81 | @Override 82 | public VirtualFile uploadFile(MultipartFile file) { 83 | if (file == null) { 84 | throw new ServiceException("[" + this.storageType + "]文件上传失败:文件不可为空"); 85 | } 86 | try { 87 | this.newFileName = file.getOriginalFilename(); 88 | return this.uploadFile(file.getInputStream(),this.newFileName); 89 | } catch (IOException e) { 90 | throw new ServiceException("[" + this.storageType + "]文件上传失败:" + e.getMessage()); 91 | } 92 | } 93 | 94 | @Override 95 | public VirtualFile uploadFile(File file) { 96 | if (file == null) { 97 | throw new ServiceException("[" + this.storageType + "]文件上传失败:文件不可为空"); 98 | } 99 | this.newFileName = file.getName(); 100 | try (InputStream is = new BufferedInputStream(new FileInputStream(file))) { 101 | return this.uploadFile(is, file.getName()); 102 | } catch (IOException e) { 103 | throw new ServiceException("[" + this.storageType + "]文件上传失败:" + e.getMessage()); 104 | } 105 | } 106 | 107 | @Override 108 | public VirtualFile uploadFile(InputStream is, String fileName) { 109 | Date startTime = new Date(); 110 | this.newFileName = fileName; 111 | this.checkName(); 112 | getMeta(); 113 | try (InputStream uploadIs = StreamUtil.clone(is); 114 | InputStream fileHashIs = StreamUtil.clone(is)) { 115 | final String filePath = this.uploadInputStream(uploadIs, newFileName); 116 | return VirtualFile.builder().originalFileName(this.newFileName).suffix(this.suffix).uploadStartTime(startTime).uploadEndTime(new Date()).filePath(filePath).fileHash(DigestUtils.md5DigestAsHex(fileHashIs)).fullFilePath(this.domainUrl+filePath).build(); 117 | } catch (IOException ex) { 118 | throw new ServiceException("[" + this.storageType + "]文件上传失败:" + ex.getMessage()); 119 | } 120 | } 121 | 122 | private void getMeta() { 123 | 124 | } 125 | 126 | /** 127 | * 将网络图片转存到云存储中 128 | * 129 | * @param userName 网络图片地址 130 | * @param referer 为了预防某些网站做了权限验证,不加referer可能会403 131 | */ 132 | @Override 133 | public VirtualFile saveToCloudStorage(String userName, String referer, String fileName) { 134 | try (InputStream is = FileUtil.getInputStreamByUrl(userName, referer)) { 135 | if (StringUtils.isEmpty(fileName)) { 136 | fileName = userName; 137 | } 138 | return this.uploadFile(is, fileName); 139 | } catch (Exception e) { 140 | throw new ServiceException("[" + this.storageType + "]文件上传失败:" + e.getMessage()); 141 | } 142 | } 143 | 144 | protected abstract void check(); 145 | 146 | protected void checkName() { 147 | this.check(); 148 | if(StringUtils.isEmpty(this.newFileName)||exists(this.newFileName)){ 149 | createNewFileName(); 150 | } 151 | } 152 | 153 | protected abstract String uploadInputStream(InputStream is, String fileName); 154 | 155 | @Override 156 | public void downloadFileToLocal(String key, String localFile) { 157 | this.check(); 158 | InputStream content = this.downloadFileStream(key); 159 | String saveFile = localFile + key; 160 | FileUtil.mkdirs(saveFile); 161 | FileUtil.down(content, saveFile); 162 | } 163 | 164 | void createNewFileName() { 165 | this.suffix = FileUtil.getSuffix(this.newFileName); 166 | this.newFileName = folder + RandomsUtil.alpha(16) + suffix; 167 | } 168 | 169 | void checkDomainUrl(String domainUrl) { 170 | if(StringUtils.isEmpty(domainUrl)){ 171 | throw new ServiceException("业务域名不能为空"); 172 | } 173 | this.domainUrl = domainUrl.endsWith("/") ? domainUrl : domainUrl + "/"; 174 | } 175 | 176 | @Override 177 | public VirtualFile multipartUpload(File file, MetaDataRequest metaDataRequest) { 178 | try { 179 | FileInputStream fileInputStream = new FileInputStream(file); 180 | return this.multipartUpload(fileInputStream, metaDataRequest); 181 | } catch (FileNotFoundException e) { 182 | throw new ServiceException("[" + this.storageType + "]文件上传失败:" + e.getMessage()); 183 | } 184 | } 185 | 186 | @Override 187 | public VirtualFile multipartUpload(MultipartFile file, MetaDataRequest metaDataRequest) { 188 | if (file == null) { 189 | throw new ServiceException("[" + this.storageType + "]文件上传失败:文件不可为空"); 190 | } 191 | try { 192 | return this.multipartUpload(file.getInputStream(), metaDataRequest); 193 | } catch (IOException e) { 194 | throw new ServiceException("[" + this.storageType + "]文件上传失败:" + e.getMessage()); 195 | } 196 | } 197 | 198 | @Override 199 | public CheckFileResult checkFile(MetaDataRequest metaDataRequest, HttpServletRequest request) { 200 | return null; 201 | } 202 | 203 | @Override 204 | public VirtualFile resumeUpload(InputStream inputStream, String fileName) { 205 | return null; 206 | } 207 | public ApiClient getAwsApiClient() { 208 | return new AwsS3ApiClient(this.accessKey 209 | ,secretKey 210 | ,region 211 | ,endpoint 212 | ,bucketName 213 | ,this.domainUrl); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/common/fastdfs/StructGroupStat.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.common.fastdfs; 2 | 3 | /** 4 | * C struct body decoder 5 | * 6 | * @author Happy Fish / YuQing 7 | * @version Version 1.18 8 | */ 9 | public class StructGroupStat extends StructBase { 10 | protected static final int FIELD_INDEX_GROUP_NAME = 0; 11 | protected static final int FIELD_INDEX_TOTAL_MB = 1; 12 | protected static final int FIELD_INDEX_FREE_MB = 2; 13 | protected static final int FIELD_INDEX_TRUNK_FREE_MB = 3; 14 | protected static final int FIELD_INDEX_STORAGE_COUNT = 4; 15 | protected static final int FIELD_INDEX_STORAGE_PORT = 5; 16 | protected static final int FIELD_INDEX_STORAGE_HTTP_PORT = 6; 17 | protected static final int FIELD_INDEX_ACTIVE_COUNT = 7; 18 | protected static final int FIELD_INDEX_CURRENT_WRITE_SERVER = 8; 19 | protected static final int FIELD_INDEX_STORE_PATH_COUNT = 9; 20 | protected static final int FIELD_INDEX_SUBDIR_COUNT_PER_PATH = 10; 21 | protected static final int FIELD_INDEX_CURRENT_TRUNK_FILE_ID = 11; 22 | 23 | protected static int fieldsTotalSize; 24 | protected static StructBase.FieldInfo[] fieldsArray = new StructBase.FieldInfo[12]; 25 | 26 | static { 27 | int offset = 0; 28 | fieldsArray[FIELD_INDEX_GROUP_NAME] = new StructBase.FieldInfo("groupName", offset, ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + 1); 29 | offset += ProtoCommon.FDFS_GROUP_NAME_MAX_LEN + 1; 30 | 31 | fieldsArray[FIELD_INDEX_TOTAL_MB] = new StructBase.FieldInfo("totalMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); 32 | offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; 33 | 34 | fieldsArray[FIELD_INDEX_FREE_MB] = new StructBase.FieldInfo("freeMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); 35 | offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; 36 | 37 | fieldsArray[FIELD_INDEX_TRUNK_FREE_MB] = new StructBase.FieldInfo("trunkFreeMB", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); 38 | offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; 39 | 40 | fieldsArray[FIELD_INDEX_STORAGE_COUNT] = new StructBase.FieldInfo("storageCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); 41 | offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; 42 | 43 | fieldsArray[FIELD_INDEX_STORAGE_PORT] = new StructBase.FieldInfo("storagePort", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); 44 | offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; 45 | 46 | fieldsArray[FIELD_INDEX_STORAGE_HTTP_PORT] = new StructBase.FieldInfo("storageHttpPort", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); 47 | offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; 48 | 49 | fieldsArray[FIELD_INDEX_ACTIVE_COUNT] = new StructBase.FieldInfo("activeCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); 50 | offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; 51 | 52 | fieldsArray[FIELD_INDEX_CURRENT_WRITE_SERVER] = new StructBase.FieldInfo("currentWriteServer", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); 53 | offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; 54 | 55 | fieldsArray[FIELD_INDEX_STORE_PATH_COUNT] = new StructBase.FieldInfo("storePathCount", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); 56 | offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; 57 | 58 | fieldsArray[FIELD_INDEX_SUBDIR_COUNT_PER_PATH] = new StructBase.FieldInfo("subdirCountPerPath", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); 59 | offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; 60 | 61 | fieldsArray[FIELD_INDEX_CURRENT_TRUNK_FILE_ID] = new StructBase.FieldInfo("currentTrunkFileId", offset, ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE); 62 | offset += ProtoCommon.FDFS_PROTO_PKG_LEN_SIZE; 63 | 64 | fieldsTotalSize = offset; 65 | } 66 | 67 | protected String groupName; //name of this group 68 | protected long totalMB; //total disk storage in MB 69 | protected long freeMB; //free disk space in MB 70 | protected long trunkFreeMB; //trunk free space in MB 71 | protected int storageCount; //storage server count 72 | protected int storagePort; //storage server port 73 | protected int storageHttpPort; //storage server HTTP port 74 | protected int activeCount; //active storage server count 75 | protected int currentWriteServer; //current storage server index to upload file 76 | protected int storePathCount; //store base path count of each storage server 77 | protected int subdirCountPerPath; //sub dir count per store path 78 | protected int currentTrunkFileId; //current trunk file id 79 | 80 | /** 81 | * get fields total size 82 | * 83 | * @return fields total size 84 | */ 85 | public static int getFieldsTotalSize() { 86 | return fieldsTotalSize; 87 | } 88 | 89 | /** 90 | * get group name 91 | * 92 | * @return group name 93 | */ 94 | public String getGroupName() { 95 | return this.groupName; 96 | } 97 | 98 | /** 99 | * get total disk space in MB 100 | * 101 | * @return total disk space in MB 102 | */ 103 | public long getTotalMB() { 104 | return this.totalMB; 105 | } 106 | 107 | /** 108 | * get free disk space in MB 109 | * 110 | * @return free disk space in MB 111 | */ 112 | public long getFreeMB() { 113 | return this.freeMB; 114 | } 115 | 116 | /** 117 | * get trunk free space in MB 118 | * 119 | * @return trunk free space in MB 120 | */ 121 | public long getTrunkFreeMB() { 122 | return this.trunkFreeMB; 123 | } 124 | 125 | /** 126 | * get storage server count in this group 127 | * 128 | * @return storage server count in this group 129 | */ 130 | public int getStorageCount() { 131 | return this.storageCount; 132 | } 133 | 134 | /** 135 | * get active storage server count in this group 136 | * 137 | * @return active storage server count in this group 138 | */ 139 | public int getActiveCount() { 140 | return this.activeCount; 141 | } 142 | 143 | /** 144 | * get storage server port 145 | * 146 | * @return storage server port 147 | */ 148 | public int getStoragePort() { 149 | return this.storagePort; 150 | } 151 | 152 | /** 153 | * get storage server HTTP port 154 | * 155 | * @return storage server HTTP port 156 | */ 157 | public int getStorageHttpPort() { 158 | return this.storageHttpPort; 159 | } 160 | 161 | /** 162 | * get current storage server index to upload file 163 | * 164 | * @return current storage server index to upload file 165 | */ 166 | public int getCurrentWriteServer() { 167 | return this.currentWriteServer; 168 | } 169 | 170 | /** 171 | * get store base path count of each storage server 172 | * 173 | * @return store base path count of each storage server 174 | */ 175 | public int getStorePathCount() { 176 | return this.storePathCount; 177 | } 178 | 179 | /** 180 | * get sub dir count per store path 181 | * 182 | * @return sub dir count per store path 183 | */ 184 | public int getSubdirCountPerPath() { 185 | return this.subdirCountPerPath; 186 | } 187 | 188 | /** 189 | * get current trunk file id 190 | * 191 | * @return current trunk file id 192 | */ 193 | public int getCurrentTrunkFileId() { 194 | return this.currentTrunkFileId; 195 | } 196 | 197 | /** 198 | * set fields 199 | * 200 | * @param bs byte array 201 | * @param offset start offset 202 | */ 203 | @Override 204 | public void setFields(byte[] bs, int offset) { 205 | this.groupName = stringValue(bs, offset, fieldsArray[FIELD_INDEX_GROUP_NAME]); 206 | this.totalMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_TOTAL_MB]); 207 | this.freeMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_FREE_MB]); 208 | this.trunkFreeMB = longValue(bs, offset, fieldsArray[FIELD_INDEX_TRUNK_FREE_MB]); 209 | this.storageCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_COUNT]); 210 | this.storagePort = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_PORT]); 211 | this.storageHttpPort = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORAGE_HTTP_PORT]); 212 | this.activeCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_ACTIVE_COUNT]); 213 | this.currentWriteServer = intValue(bs, offset, fieldsArray[FIELD_INDEX_CURRENT_WRITE_SERVER]); 214 | this.storePathCount = intValue(bs, offset, fieldsArray[FIELD_INDEX_STORE_PATH_COUNT]); 215 | this.subdirCountPerPath = intValue(bs, offset, fieldsArray[FIELD_INDEX_SUBDIR_COUNT_PER_PATH]); 216 | this.currentTrunkFileId = intValue(bs, offset, fieldsArray[FIELD_INDEX_CURRENT_TRUNK_FILE_ID]); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/fileClient/GithubApiClient.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.fileClient; 2 | 3 | import com.alibaba.fastjson.JSONArray; 4 | import com.alibaba.fastjson.JSONObject; 5 | import com.zhouzifei.tool.common.ServiceException; 6 | import com.zhouzifei.tool.config.FileProperties; 7 | import com.zhouzifei.tool.config.GithubFileProperties; 8 | import com.zhouzifei.tool.dto.GithubFileList; 9 | import com.zhouzifei.tool.dto.VirtualFile; 10 | import com.zhouzifei.tool.entity.FileListRequesr; 11 | import com.zhouzifei.tool.entity.MetaDataRequest; 12 | import com.zhouzifei.tool.media.file.util.StreamUtil; 13 | import com.zhouzifei.tool.service.ApiClient; 14 | import com.zhouzifei.tool.util.FileUtil; 15 | import com.zhouzifei.tool.util.HttpUtils; 16 | import com.zhouzifei.tool.util.StringUtils; 17 | import lombok.var; 18 | import sun.misc.BASE64Encoder; 19 | 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.security.spec.DSAParameterSpec; 23 | import java.util.*; 24 | 25 | /** 26 | * @author 周子斐 27 | * @date 2021/10/12 28 | * @Description 29 | */ 30 | public class GithubApiClient extends BaseApiClient { 31 | 32 | private String token; 33 | private String user; 34 | private String repository; 35 | private final String requestUrl = "https://api.github.com/"; 36 | private final String domianUrl = "https://cdn.jsdelivr.net/gh/"; 37 | 38 | public GithubApiClient() { 39 | super("github"); 40 | } 41 | 42 | public GithubApiClient(FileProperties fileProperties) { 43 | super("github"); 44 | init(fileProperties); 45 | } 46 | 47 | @Override 48 | public GithubApiClient init(FileProperties fileProperties) { 49 | final GithubFileProperties githubFileProperties = (GithubFileProperties) fileProperties; 50 | this.repository = githubFileProperties.getRepository(); 51 | this.token = githubFileProperties.getToken(); 52 | this.user = githubFileProperties.getUser(); 53 | checkDomainUrl(domianUrl); 54 | return this; 55 | } 56 | 57 | @Override 58 | public String uploadInputStream(InputStream inputStream, String fileName) { 59 | try (InputStream uploadIs = StreamUtil.clone(inputStream)) { 60 | Map hears = new HashMap<>(); 61 | hears.put("Authorization", "Bearer " + token); 62 | hears.put("Content-Type", "text/plain"); 63 | // 读取图片字节数组 64 | byte[] data = new byte[uploadIs.available()]; 65 | uploadIs.read(data); 66 | // 对字节数组Base64编码 67 | BASE64Encoder encoder = new BASE64Encoder(); 68 | // 返回Base64编码过的字节数组字符串 69 | final String baseContent = encoder.encode(data); 70 | final JSONObject jsonObject = new JSONObject(); 71 | jsonObject.put("message", fileName); 72 | jsonObject.put("content", baseContent); 73 | final String toString = jsonObject.toString(); 74 | String uploadUrl = requestUrl + "repos/" + user + "/" + repository + "/contents/" + fileName; 75 | //查询是否存在 76 | if (exists(fileName)) { 77 | jsonObject.put("sha", UUID.randomUUID().toString().replace("-", "")); 78 | } 79 | final String s = HttpUtils.JsonPut(uploadUrl, toString, hears); 80 | return user + "/" + repository + "/" + fileName; 81 | } catch (IOException e) { 82 | throw new ServiceException("[" + this.storageType + "]文件上传失败:" + e.getMessage()); 83 | } 84 | } 85 | 86 | @Override 87 | public boolean removeFile(String fileName) { 88 | Map hears = new HashMap<>(); 89 | hears.put("Authorization", "Bearer " + token); 90 | hears.put("Content-Type", "text/plain"); 91 | hears.put("Accept", "application/vnd.github.v3+json"); 92 | String deleteUrl = requestUrl + "repos/" + user + "/" + repository + "/contents/" + fileName; 93 | //查询是否存在 94 | final String get = HttpUtils.Get(deleteUrl, hears); 95 | if (null == get) { 96 | throw new ServiceException("[" + this.storageType + "]文件不存在:" + fileName); 97 | } 98 | final JSONArray objects = JSONObject.parseArray(get); 99 | final List githubFileLists = objects.toJavaList(GithubFileList.class); 100 | for (GithubFileList githubFileList : githubFileLists) { 101 | final JSONObject jsonObject = new JSONObject(); 102 | jsonObject.put("message", "删除文件:" + fileName); 103 | jsonObject.put("sha", githubFileList.getSha()); 104 | // jsonObject.put("committer", githubFileList.getSha()); 105 | // jsonObject.put("sha", githubFileList.getSha()); 106 | final String toString = jsonObject.toString(); 107 | final String s = HttpUtils.JsonDelete(deleteUrl, toString, hears); 108 | } 109 | return true; 110 | } 111 | 112 | @Override 113 | public void downloadFileToLocal(String fileName, String localFile) { 114 | 115 | } 116 | 117 | @Override 118 | public InputStream downloadFileStream(String key) { 119 | return FileUtil.getInputStreamByUrl(key, requestUrl); 120 | } 121 | 122 | @Override 123 | protected void check() { 124 | 125 | } 126 | 127 | @Override 128 | public VirtualFile multipartUpload(InputStream inputStream, MetaDataRequest metaDataRequest) { 129 | throw new ServiceException("[" + this.storageType + "]文件上传失败:该上传类型不支持分片上传"); 130 | } 131 | 132 | @Override 133 | public VirtualFile resumeUpload(InputStream inputStream, String fileName) { 134 | return null; 135 | } 136 | 137 | @Override 138 | public List fileList(FileListRequesr fileListRequesr) { 139 | Map hears = new HashMap<>(); 140 | hears.put("Authorization", "Bearer " + token); 141 | hears.put("Content-Type", "text/plain"); 142 | hears.put("Accept", "application/vnd.github.v3+json"); 143 | final String listRequesrFold = fileListRequesr.getFold(); 144 | String uploadUrl = requestUrl + "repos/" + user + "/" + repository + "/contents/" + listRequesrFold; 145 | //查询是否存在 146 | final String get = HttpUtils.Get(uploadUrl, hears); 147 | if (null == get) { 148 | return new ArrayList<>(); 149 | } 150 | if (!get.contains("[") && !get.contains("]")) { 151 | final JSONObject jsonObject = JSONObject.parseObject(get); 152 | final String message = jsonObject.getString("message"); 153 | throw new ServiceException("[" + this.storageType + "]" + message); 154 | } 155 | final JSONArray objects = JSONObject.parseArray(get); 156 | final List githubFileLists = objects.toJavaList(GithubFileList.class); 157 | List virtualFiles = new ArrayList<>(); 158 | for (GithubFileList githubFileList : githubFileLists) { 159 | final VirtualFile file = VirtualFile.builder().fileHash(githubFileList.getSha()).filePath(listRequesrFold + githubFileList.getPath()).fullFilePath(getFileUrl(repository, githubFileList)).originalFileName(githubFileList.getName()).suffix(FileUtil.getSuffixName(githubFileList.getName())).size(Long.parseLong(githubFileList.getSize())).isFold(!"file".equals(githubFileList.getType())).build(); 160 | virtualFiles.add(file); 161 | } 162 | return virtualFiles; 163 | } 164 | 165 | private String getFileUrl(String repository, GithubFileList githubFileList) { 166 | final String url = githubFileList.getUrl(); 167 | if (StringUtils.isNotEmpty(url)) { 168 | String replace = url.replace("https://api.github.com/repos/", domianUrl); 169 | final String[] split = replace.split("\\?"); 170 | final String branch = split[1]; 171 | replace = replace.replace("?" + branch, ""); 172 | final String[] split1 = branch.split("="); 173 | final String branchName = split1[1]; 174 | replace = replace.replace(repository + "/contents", repository + "@" + branchName); 175 | return replace; 176 | } else { 177 | return url; 178 | } 179 | } 180 | 181 | @Override 182 | public boolean exists(String fileName) { 183 | Map hears = new HashMap<>(); 184 | hears.put("Authorization", "Bearer " + token); 185 | hears.put("Content-Type", "text/plain"); 186 | String uploadUrl = requestUrl + "repos/" + user + "/" + repository + "/contents/" + fileName; 187 | //查询是否存在 188 | final String get = HttpUtils.Get(uploadUrl, hears); 189 | return null != get; 190 | } 191 | 192 | @Override 193 | public ApiClient getAwsApiClient() { 194 | throw new ServiceException("[" + this.storageType + "]暂不支持AWS3协议!"); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/fileClient/SmMsApiClient.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.fileClient; 2 | 3 | import com.alibaba.fastjson.JSONArray; 4 | import com.alibaba.fastjson.JSONObject; 5 | import com.zhouzifei.tool.common.ServiceException; 6 | import com.zhouzifei.tool.config.FileProperties; 7 | import com.zhouzifei.tool.config.SmmsFileProperties; 8 | import com.zhouzifei.tool.dto.SmmFileList; 9 | import com.zhouzifei.tool.dto.VirtualFile; 10 | import com.zhouzifei.tool.entity.FileListRequesr; 11 | import com.zhouzifei.tool.entity.MetaDataRequest; 12 | import com.zhouzifei.tool.media.file.util.StreamUtil; 13 | import com.zhouzifei.tool.service.ApiClient; 14 | import com.zhouzifei.tool.util.FileUtil; 15 | import com.zhouzifei.tool.util.HttpUtils; 16 | import com.zhouzifei.tool.util.RandomsUtil; 17 | import com.zhouzifei.tool.util.StringUtils; 18 | import lombok.extern.slf4j.Slf4j; 19 | 20 | import java.io.*; 21 | import java.net.HttpURLConnection; 22 | import java.net.URL; 23 | import java.util.*; 24 | 25 | /** 26 | * @author 周子斐 (17600004572@163.com) 27 | * @version 1.0 28 | * @remark 2019年7月23日 29 | * @since 1.0 30 | */ 31 | @Slf4j 32 | public class SmMsApiClient extends BaseApiClient { 33 | 34 | private Map hears = new HashMap<>(); 35 | private String requestUrl = "https://sm.ms/api/v2"; 36 | 37 | public SmMsApiClient() { 38 | super("阿里云OSS"); 39 | } 40 | 41 | public SmMsApiClient(FileProperties fileProperties) { 42 | super("SMMS"); 43 | init(fileProperties); 44 | } 45 | 46 | @Override 47 | public SmMsApiClient init(FileProperties fileProperties) { 48 | final SmmsFileProperties smmsFileProperties = (SmmsFileProperties) fileProperties; 49 | this.username = smmsFileProperties.getUserName(); 50 | this.password = smmsFileProperties.getPassWord(); 51 | this.token = smmsFileProperties.getToken(); 52 | if (StringUtils.isEmpty(token)) { 53 | if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) { 54 | throw new ServiceException("[" + this.storageType + "]初始化失败!"); 55 | } 56 | //获取token 57 | final String s1 = "username=" + username + "&password=" + password; 58 | final String s = HttpUtils.DataPost(this.domainUrl + "token", s1); 59 | final JSONObject jsonObject = JSONObject.parseObject(s); 60 | if (!(Boolean) jsonObject.get("success")) { 61 | throw new ServiceException("[" + this.storageType + "]初始化失败:" + jsonObject); 62 | } 63 | final JSONObject data = (JSONObject) jsonObject.get("data"); 64 | final Object token1 = data.get("token"); 65 | this.token = (String) token1; 66 | } 67 | hears.put("token", token); 68 | hears.put("Content-Type", "multipart/form-data"); 69 | checkDomainUrl(requestUrl); 70 | return this; 71 | } 72 | 73 | @Override 74 | public String uploadInputStream(InputStream is, String imageUrl) { 75 | this.check(); 76 | Date startTime = new Date(); 77 | try (InputStream uploadIs = StreamUtil.clone(is); InputStream fileHashIs = StreamUtil.clone(is)) { 78 | final String fileName = RandomsUtil.alpha(20) + ".jpg"; 79 | // 换行符 80 | final String newLine = "\r\n"; 81 | final String boundaryPrefix = "--"; 82 | // 定义数据分隔线 83 | String BOUNDARY = "========7d4a6d158c9"; 84 | // 服务器的域名 85 | URL url = new URL(this.domainUrl + "/upload"); 86 | HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 87 | // 设置为POST情 88 | conn.setRequestMethod("POST"); 89 | // 发送POST请求必须设置如下两行 90 | conn.setDoOutput(true); 91 | conn.setDoInput(true); 92 | conn.setUseCaches(false); 93 | // 设置请求头参数 94 | conn.setRequestProperty("connection", "Keep-Alive"); 95 | conn.setRequestProperty("Charsert", "UTF-8"); 96 | conn.setRequestProperty("Authorization", token); 97 | conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY); 98 | OutputStream out = new DataOutputStream(conn.getOutputStream()); 99 | // 上传文件 100 | File file = new File(fileName); 101 | StringBuilder sb = new StringBuilder(); 102 | sb.append(boundaryPrefix); 103 | sb.append(BOUNDARY); 104 | sb.append(newLine); 105 | // 文件参数,photo参数名可以随意修改 106 | sb.append("Content-Disposition: form-data;name=\"smfile\";filename=\"" + fileName + "\"" + newLine); 107 | sb.append("Content-Type:multipart/form-data"); 108 | // 参数头设置完以后需要两个换行,然后才是参数内容 109 | sb.append(newLine); 110 | sb.append(newLine); 111 | // 将参数头的数据写入到输出流中 112 | out.write(sb.toString().getBytes()); 113 | byte[] bufferOut = new byte[1024]; 114 | int bytes = 0; 115 | // 每次读1KB数据,并且将文件数据写入到输出流中 116 | while ((bytes = is.read(bufferOut)) != -1) { 117 | out.write(bufferOut, 0, bytes); 118 | } 119 | // 最后添加换行 120 | out.write(newLine.getBytes()); 121 | is.close(); 122 | // 定义最后数据分隔线,即--加上BOUNDARY再加上--。 123 | byte[] end_data = (newLine + boundaryPrefix + BOUNDARY + boundaryPrefix + newLine).getBytes(); 124 | // 写上结尾标识 125 | out.write(end_data); 126 | out.flush(); 127 | out.close(); 128 | 129 | // 定义BufferedReader输入流来读取URL的响应 130 | BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); 131 | String line = null; 132 | StringBuilder result = new StringBuilder(); 133 | while ((line = reader.readLine()) != null) { 134 | result.append(line); 135 | } 136 | final JSONObject parse = JSONObject.parseObject(result.toString()); 137 | final Object data = parse.get("data"); 138 | final JSONObject dataJosn = JSONObject.parseObject((String) data); 139 | final Object newFileUrl = dataJosn.get("url"); 140 | return imageUrl; 141 | } catch (IOException e) { 142 | throw new ServiceException("[" + this.storageType + "]文件上传失败:" + e.getMessage()); 143 | } 144 | } 145 | 146 | @Override 147 | public boolean removeFile(String fileName) { 148 | throw new ServiceException("[" + this.storageType + "]文件上传失败:该上传类型不支持删除"); 149 | } 150 | 151 | @Override 152 | public VirtualFile multipartUpload(InputStream inputStream, MetaDataRequest metaDataRequest) { 153 | throw new ServiceException("[" + this.storageType + "]文件上传失败:该上传类型不支持分片上传"); 154 | } 155 | 156 | @Override 157 | public List fileList(FileListRequesr fileListRequesr) { 158 | final HashMap map = new HashMap<>(); 159 | map.put("Content-Type", "multipart/form-data"); 160 | map.put("Authorization", token); 161 | final String resultString = HttpUtils.Get(this.domainUrl + "upload_history", map); 162 | if (StringUtils.isEmpty(resultString)) { 163 | return new ArrayList<>(); 164 | } 165 | final JSONObject jsonObject = JSONObject.parseObject(resultString); 166 | final Boolean success = jsonObject.getBoolean("success"); 167 | if (!success) { 168 | return new ArrayList<>(); 169 | } 170 | final JSONArray jsonArray = jsonObject.getJSONArray("data"); 171 | final List smmFileLists = jsonArray.toJavaList(SmmFileList.class); 172 | List virtualFiles = new ArrayList<>(); 173 | for (SmmFileList smmFileList : smmFileLists) { 174 | final VirtualFile file = VirtualFile.builder() 175 | .fileHash(smmFileList.getHash()) 176 | .filePath(smmFileList.getPath()) 177 | .fullFilePath(smmFileList.getUrl()) 178 | .originalFileName(smmFileList.getFilename()) 179 | .suffix(FileUtil.getSuffixName(smmFileList.getFilename())) 180 | .size(Long.parseLong(smmFileList.getSize())) 181 | .isFold(false) 182 | .uploadStartTime(new Date(smmFileList.getCreated_at())) 183 | .uploadEndTime(new Date(smmFileList.getCreated_at())) 184 | .build(); 185 | virtualFiles.add(file); 186 | } 187 | return virtualFiles; 188 | } 189 | 190 | @Override 191 | public boolean exists(String fileName) { 192 | return false; 193 | } 194 | 195 | @Override 196 | protected void check() { 197 | 198 | } 199 | 200 | @Override 201 | public InputStream downloadFileStream(String key) { 202 | return null; 203 | } 204 | 205 | @Override 206 | public ApiClient getAwsApiClient() { 207 | throw new com.zhouzifei.tool.common.ServiceException("[" + this.storageType + "]暂不支持AWS3协议!"); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # simpleFS 2 | 它是一个小型整合型的工具类,带有整合(阿里云,七牛云,又拍云,腾讯云,华为云,~~百度云~~,本地上传)OSS上传,短信发送(阿里云,腾讯云,七牛云),文件加工类,它可以让我们脱离繁琐的开发流程,让开发变得**So easy!**。 3 | 4 | # 支持平台 5 | 6 | | 站点 | 文件上传 | 分片上传 | 断点续传 | 文件下载 | 文件删除 | 7 | | :--: | :--: | :--: | :--: | :--: | :--: | 8 | | **阿里云OSS** |✔|✔|✔|✔|✔| 9 | | **FastDfs** |✔|✖|✖|✔|✔| 10 | | **华为云OBS** |✔|✖|✖|✔|✔| 11 | | **本地上传** |✔|✖|✖|✔|✔| 12 | | **腾讯云COS** |✔|✖|✖|✔|✔| 13 | | **七牛云Kodo** |✔|✖|✖|✔|✔| 14 | | **又拍云USS** |✔|✖|✖|✔|✔| 15 | | **百度云BOS** |✔|✖|✖|✔|✔| 16 | | **MinIO** |✔|✖|✖|✔|✔| 17 | | **AWS S3** |✔|✖|✖|✔|✔| 18 | | **金山云 KS3** |✔|✖|✖|✔|✔| 19 | | **美团云 MSS** |✔|✖|✖|✔|✔| 20 | | **京东云 OSS** |✔|✖|✖|✔|✔| 21 | | **天翼云 OOS** |✔|✖|✖|✔|✔| 22 | | **移动云 EOS** |✔|✖|✖|✔|✔| 23 | | **沃云 OSS** |✔|✖|✖|✔|✔| 24 | | **网易数帆 NOS** |✔|✖|✖|✔|✔| 25 | | **Ucloud US3** |✔|✖|✖|✔|✔| 26 | | **青云 QingStor** |✔|✖|✖|✔|✔| 27 | | **平安云 OBS** |✔|✖|✖|✔|✔| 28 | | **首云 OSS** |✔|✖|✖|✔|✔| 29 | | **IBM COS** |✔|✖|✖|✔|✔| 30 | | **其它兼容 S3 协议的平台** |✔|✖|✖|✔|✔| 31 | 32 | # 快速开始 33 | 34 | ## 1、安装 35 | 36 | ### 依赖包方式引入 37 | 38 | ```xml 39 | 40 | com.zhouzifei 41 | simpleFS 42 | {latest-version} 43 | 44 | ``` 45 | > **latest-version** 版本为: 46 | > - 稳定版本:![](https://img.shields.io/github/v/release/shengdingbox/simpleFS?style=flat-square) 47 | > - 快照版本:![](https://img.shields.io/maven-metadata/v.svg?label=snapshots&metadataUrl=https://repo1.maven.org/maven2/com/zhouzifei/simpleFS/maven-metadata.xml&style=flat-square) 48 | > > 注意:无法引入可添加中央仓库地址。 49 | > 50 | > 51 | > ```xml 52 | > 53 | > 54 | > ossrh-snapshot 55 | > https://oss.sonatype.org/content/repositories/snapshots 56 | > 57 | > true 58 | > 59 | > 60 | > 61 | > ``` 62 | > 63 | > 如果你想第一时间获取 simpleFS 的最新快照,可以添加下列代码,每次构建时都检查是否有最新的快照(默认每天检查)。 64 | > 65 | > ```diff 66 | > https://oss.sonatype.org/content/repositories/snapshots 67 | > 68 | > + always 69 | > true 70 | > 71 | > ``` 72 | 73 | ### 源码安装方式引入 74 | 拉取代码 75 | 76 | ```shell 77 | git clone https://gitee.com/zifeiZhou/simpleFS.git(国内,版本较为落后) 78 | git clone https://github.com/shengdingbox/simpleFS.git(默认) 79 | ``` 80 | 编译构建 81 | ```shell 82 | mvn clean install 83 | ``` 84 | 85 | ## 2、配置 86 | 87 | 88 | - `application.yml`方式 89 | ```yaml 90 | simple-fs: 91 | local: 92 | local-file-path: 本地路径 93 | local-url: 本机访问地址 94 | oss: 95 | access-key: 阿里云OSS授权AK 96 | secret-key: 阿里云OSS授权SK 97 | endpoint: 阿里云OSS地域 98 | domain-url: 阿里云OSS访问地址 99 | bucket-name: 阿里云OSS空间名称 100 | fast: 101 | user-name: FASTDFS用户名 102 | pass-word: FASTDFS密码 103 | server-url: FASTDFS上传地址 104 | domain-url: FASTDFS访问地址 105 | huawei: 106 | access-key: 华为对象存储授权AK 107 | secret-key: 华为对象存储授权SK 108 | endpoint: 华为对象存储地域 109 | domain-url: 华为对象存储访问地址 110 | bucket-name: 华为对象存储空间名称 111 | bos: 112 | access-key: 百度bos授权AK 113 | secret-key: 百度bos授权SK 114 | endpoint: 百度bos地域 115 | domain-url: 百度bos访问地址 116 | bucket-name: 百度bos空间名称 117 | qcloud: 118 | access-key: 腾讯云cos授权AK 119 | secret-key: 腾讯云cos授权SK 120 | endpoint: 腾讯云cos地域 121 | domain-url: 腾讯云cos访问地址 122 | bucket-name: 腾讯云cos空间名称 123 | region: 腾讯云cos地域 124 | qiniu: 125 | access-key: 七牛云授权AK 126 | secret-key: 七牛云授权SK 127 | domain-url: 七牛云访问地址 128 | bucket-name: 七牛云空间名称 129 | region: 七牛云地域 130 | upai: 131 | user-name: 又拍云用户名 132 | pass-word: 又拍云密码 133 | domain-url: 又拍云访问地址 134 | bucket-name: 又拍云空间名称 135 | smms: 136 | user-name: smms用户名 137 | token: smms-Token 138 | pass-word: smms密码 139 | github: 140 | repository: github仓库 141 | user: github用户名 142 | token: githubToken 143 | aws: 144 | access-key: AWS授权AK 145 | secret-key: AWS授权SK 146 | endpoint: AWS地域 147 | bucket-name: AWS空间名称 148 | region: AWS地域 149 | domain-url: AWS访问地址 150 | ``` 151 | - `application.properties`方式 152 | ```properties 153 | simple-fs.oss.accessKey=授权AK 154 | simple-fs.oss.secretKey=授权SK 155 | simple-fs.oss.endpoint=地域 156 | simple-fs.oss.domain-url=图片外网地址 157 | simple-fs.oss.bucket-name=空间名称 158 | ``` 159 | - 启动配置类赋值 160 | 161 | ```java 162 | import com.zhouzifei.tool.config.OssFileProperties; 163 | import com.zhouzifei.tool.config.SimpleFsProperties; 164 | import lombok.extern.slf4j.Slf4j; 165 | import org.springframework.beans.factory.annotation.Autowired; 166 | import org.springframework.beans.factory.annotation.Configurable; 167 | 168 | @Slf4j 169 | @Configurable 170 | public class OssConfig { 171 | @Autowired 172 | SimpleFsProperties simpleFsProperties; 173 | 174 | @PostConstruct 175 | public void init() { 176 | log.info("项目启动中,赋值存储配置类"); 177 | final OssFileProperties aliYunOss = simpleFsProperties.getAliYunOss(); 178 | aliYunOss.setAccessKey("授权AK"); 179 | aliYunOss.setSecretKey("授权SK"); 180 | aliYunOss.setEndpoint("地域"); 181 | aliYunOss.setDomainUrl("图片外网地址"); 182 | aliYunOss.setBucketName("空间名称"); 183 | log.info("项目启动中,加载配置数据数据完成-->" + simpleFsProperties); 184 | } 185 | } 186 | ``` 187 | 188 | 189 | ## 开发 190 | 191 | - 初始化 192 | 193 | ```java 194 | @Autowired 195 | private SimpleFsProperties simpleFsProperties; 196 | 197 | FileUploader uploader = FileUploader.builder() 198 | .simpleFsProperties(simpleFsProperties) 199 | .progressListener(progressListener) 200 | .domainUrl(domainUrl) 201 | .accessKey(accessKey) 202 | .secretKey(secretKey) 203 | .region(region) 204 | .bucketName(bucketName) 205 | .storageType(storageType) 206 | .build(); 207 | 208 | ApiClient apiClient = uploader.execute(); 209 | ``` 210 | - 文件上传 211 | ```java 212 | VirtualFile virtualFile = apiClient.uploadFile(file, TEST_OBJECT_NAME); 213 | 214 | ``` 215 | - 文件下载 216 | ```java 217 | void downloadFile(String file, String localFile); 218 | ``` 219 | - 切片上传 220 | ```java 221 | VirtualFile multipartUpload(File file); 222 | ``` 223 | - 文件删除 224 | ```java 225 | boolean removeFile(String key); 226 | ``` 227 | 228 | - 文件上传最佳实践 229 | 230 | ```java 231 | package com.zhouzifei.oss; 232 | 233 | import com.zhouzifei.tool.config.QiniuFileProperties; 234 | import com.zhouzifei.tool.config.SimpleFsProperties; 235 | import com.zhouzifei.tool.consts.StorageTypeConst; 236 | import com.zhouzifei.tool.dto.VirtualFile; 237 | import com.zhouzifei.tool.listener.ProgressListener; 238 | import com.zhouzifei.tool.service.ApiClient; 239 | import com.zhouzifei.tool.service.FileUploader; 240 | import lombok.SneakyThrows; 241 | import org.junit.jupiter.api.Test; 242 | import org.springframework.beans.factory.annotation.Autowired; 243 | import org.springframework.boot.autoconfigure.SpringBootApplication; 244 | import org.springframework.boot.test.context.SpringBootTest; 245 | import org.springframework.test.context.ActiveProfiles; 246 | import org.springframework.util.ResourceUtils; 247 | 248 | import java.io.FileInputStream; 249 | 250 | @SpringBootApplication(scanBasePackages = "com.zhouzifei.*") 251 | @SpringBootTest 252 | @ActiveProfiles("oss") 253 | public class OssTemplateTest { 254 | 255 | @Autowired 256 | private SimpleFsProperties simpleFsProperties; 257 | 258 | /** 259 | * 测试用文件名,该文件在测试资源文件夹下 260 | */ 261 | private static final String TEST_OBJECT_NAME = "test.txt"; 262 | 263 | @Test 264 | @SneakyThrows 265 | public void test() { 266 | System.out.println(simpleFsProperties); 267 | ProgressListener progressListener = new ProgressListener() { 268 | @Override 269 | public void start(String s) { 270 | System.out.println("开始上传"); 271 | } 272 | 273 | @Override 274 | public void process(int i, int i1) { 275 | System.out.println("i=" + i); 276 | System.out.println("i1=" + i1); 277 | } 278 | 279 | @Override 280 | public void end(VirtualFile virtualFile) { 281 | System.out.println("上传完成"); 282 | System.out.println(virtualFile); 283 | 284 | } 285 | }; 286 | QiniuFileProperties qiniuFileProperties = simpleFsProperties.getQiniu(); 287 | System.out.println(qiniuFileProperties); 288 | 289 | String domainUrl = qiniuFileProperties.getDomainUrl(); 290 | String accessKey = qiniuFileProperties.getAccessKey(); 291 | String secretKey = qiniuFileProperties.getSecretKey(); 292 | String region = qiniuFileProperties.getRegion(); 293 | String bucketName = qiniuFileProperties.getBucketName(); 294 | String storageType = StorageTypeConst.QINIUYUN.getStorageType(); 295 | 296 | FileUploader uploader = FileUploader.builder() 297 | .simpleFsProperties(simpleFsProperties) 298 | .progressListener(progressListener) 299 | .domainUrl(domainUrl) 300 | .accessKey(accessKey) 301 | .secretKey(secretKey) 302 | .region(region) 303 | .bucketName(bucketName) 304 | .storageType(storageType) 305 | .build(); 306 | 307 | ApiClient apiClient = uploader.execute(); 308 | 309 | 310 | FileInputStream file = new FileInputStream(ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX + TEST_OBJECT_NAME)); 311 | 312 | VirtualFile virtualFile = apiClient.uploadFile(file, TEST_OBJECT_NAME); 313 | System.out.println(virtualFile.getFullFilePath()); 314 | System.out.println(virtualFile.getFileHash()); 315 | 316 | 317 | } 318 | 319 | } 320 | 321 | ``` 322 | 323 | 324 | 325 | -------------------------------------------------------------------------------- /src/main/java/com/zhouzifei/tool/util/Base64Coder.java: -------------------------------------------------------------------------------- 1 | package com.zhouzifei.tool.util; 2 | 3 | 4 | public class Base64Coder { 5 | // The line separator string of the operating system. 6 | private static final String systemLineSeparator = System.getProperty("line.separator"); 7 | 8 | // Mapping table from 6-bit nibbles to Base64 characters. 9 | private static final char[] map1 = new char[64]; 10 | 11 | static { 12 | int i = 0; 13 | for (char c = 'A'; c <= 'Z'; c++) map1[i++] = c; 14 | for (char c = 'a'; c <= 'z'; c++) map1[i++] = c; 15 | for (char c = '0'; c <= '9'; c++) map1[i++] = c; 16 | map1[i++] = '+'; 17 | map1[i++] = '/'; 18 | } 19 | 20 | // Mapping table from Base64 characters to 6-bit nibbles. 21 | private static final byte[] map2 = new byte[128]; 22 | 23 | static { 24 | for (int i = 0; i < map2.length; i++) map2[i] = -1; 25 | for (int i = 0; i < 64; i++) map2[map1[i]] = (byte) i; 26 | } 27 | 28 | /** 29 | * Encodes a string into Base64 format. 30 | * No blanks or line breaks are inserted. 31 | * 32 | * @param s A String to be encoded. 33 | * @return A String containing the Base64 encoded data. 34 | */ 35 | public static String encodeString(String s) { 36 | return new String(encode(s.getBytes())); 37 | } 38 | 39 | /** 40 | * Encodes a byte array into Base 64 format and breaks the output into lines of 76 characters. 41 | * This method is compatible with sun.misc.BASE64Encoder.encodeBuffer(byte[]). 42 | * 43 | * @param in An array containing the data bytes to be encoded. 44 | * @return A String containing the Base64 encoded data, broken into lines. 45 | */ 46 | public static String encodeLines(byte[] in) { 47 | return encodeLines(in, 0, in.length, 76, systemLineSeparator); 48 | } 49 | 50 | /** 51 | * Encodes a byte array into Base 64 format and breaks the output into lines. 52 | * 53 | * @param in An array containing the data bytes to be encoded. 54 | * @param iOff Offset of the first byte in in to be processed. 55 | * @param iLen Number of bytes to be processed in in, starting at iOff. 56 | * @param lineLen Line length for the output data. Should be a multiple of 4. 57 | * @param lineSeparator The line separator to be used to separate the output lines. 58 | * @return A String containing the Base64 encoded data, broken into lines. 59 | */ 60 | public static String encodeLines(byte[] in, int iOff, int iLen, int lineLen, String lineSeparator) { 61 | int blockLen = (lineLen * 3) / 4; 62 | if (blockLen <= 0) throw new IllegalArgumentException(); 63 | int lines = (iLen + blockLen - 1) / blockLen; 64 | int bufLen = ((iLen + 2) / 3) * 4 + lines * lineSeparator.length(); 65 | StringBuilder buf = new StringBuilder(bufLen); 66 | int ip = 0; 67 | while (ip < iLen) { 68 | int l = Math.min(iLen - ip, blockLen); 69 | buf.append(encode(in, iOff + ip, l)); 70 | buf.append(lineSeparator); 71 | ip += l; 72 | } 73 | return buf.toString(); 74 | } 75 | 76 | /** 77 | * Encodes a byte array into Base64 format. 78 | * No blanks or line breaks are inserted in the output. 79 | * 80 | * @param in An array containing the data bytes to be encoded. 81 | * @return A character array containing the Base64 encoded data. 82 | */ 83 | public static char[] encode(byte[] in) { 84 | return encode(in, 0, in.length); 85 | } 86 | 87 | /** 88 | * Encodes a byte array into Base64 format. 89 | * No blanks or line breaks are inserted in the output. 90 | * 91 | * @param in An array containing the data bytes to be encoded. 92 | * @param iLen Number of bytes to process in in. 93 | * @return A character array containing the Base64 encoded data. 94 | */ 95 | public static char[] encode(byte[] in, int iLen) { 96 | return encode(in, 0, iLen); 97 | } 98 | 99 | /** 100 | * Encodes a byte array into Base64 format. 101 | * No blanks or line breaks are inserted in the output. 102 | * 103 | * @param in An array containing the data bytes to be encoded. 104 | * @param iOff Offset of the first byte in in to be processed. 105 | * @param iLen Number of bytes to process in in, starting at iOff. 106 | * @return A character array containing the Base64 encoded data. 107 | */ 108 | public static char[] encode(byte[] in, int iOff, int iLen) { 109 | int oDataLen = (iLen * 4 + 2) / 3; // output length without padding 110 | int oLen = ((iLen + 2) / 3) * 4; // output length including padding 111 | char[] out = new char[oLen]; 112 | int ip = iOff; 113 | int iEnd = iOff + iLen; 114 | int op = 0; 115 | while (ip < iEnd) { 116 | int i0 = in[ip++] & 0xff; 117 | int i1 = ip < iEnd ? in[ip++] & 0xff : 0; 118 | int i2 = ip < iEnd ? in[ip++] & 0xff : 0; 119 | int o0 = i0 >>> 2; 120 | int o1 = ((i0 & 3) << 4) | (i1 >>> 4); 121 | int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); 122 | int o3 = i2 & 0x3F; 123 | out[op++] = map1[o0]; 124 | out[op++] = map1[o1]; 125 | out[op] = op < oDataLen ? map1[o2] : '='; 126 | op++; 127 | out[op] = op < oDataLen ? map1[o3] : '='; 128 | op++; 129 | } 130 | return out; 131 | } 132 | 133 | /** 134 | * Decodes a string from Base64 format. 135 | * No blanks or line breaks are allowed within the Base64 encoded input data. 136 | * 137 | * @param s A Base64 String to be decoded. 138 | * @return A String containing the decoded data. 139 | * @throws IllegalArgumentException If the input is not valid Base64 encoded data. 140 | */ 141 | public static String decodeString(String s) { 142 | return new String(decode(s)); 143 | } 144 | 145 | /** 146 | * Decodes a byte array from Base64 format and ignores line separators, tabs and blanks. 147 | * CR, LF, Tab and Space characters are ignored in the input data. 148 | * This method is compatible with sun.misc.BASE64Decoder.decodeBuffer(String). 149 | * 150 | * @param s A Base64 String to be decoded. 151 | * @return An array containing the decoded data bytes. 152 | * @throws IllegalArgumentException If the input is not valid Base64 encoded data. 153 | */ 154 | public static byte[] decodeLines(String s) { 155 | char[] buf = new char[s.length()]; 156 | int p = 0; 157 | for (int ip = 0; ip < s.length(); ip++) { 158 | char c = s.charAt(ip); 159 | if (c != ' ' && c != '\r' && c != '\n' && c != '\t') 160 | buf[p++] = c; 161 | } 162 | return decode(buf, 0, p); 163 | } 164 | 165 | /** 166 | * Decodes a byte array from Base64 format. 167 | * No blanks or line breaks are allowed within the Base64 encoded input data. 168 | * 169 | * @param s A Base64 String to be decoded. 170 | * @return An array containing the decoded data bytes. 171 | * @throws IllegalArgumentException If the input is not valid Base64 encoded data. 172 | */ 173 | public static byte[] decode(String s) { 174 | return decode(s.toCharArray()); 175 | } 176 | 177 | /** 178 | * Decodes a byte array from Base64 format. 179 | * No blanks or line breaks are allowed within the Base64 encoded input data. 180 | * 181 | * @param in A character array containing the Base64 encoded data. 182 | * @return An array containing the decoded data bytes. 183 | * @throws IllegalArgumentException If the input is not valid Base64 encoded data. 184 | */ 185 | public static byte[] decode(char[] in) { 186 | return decode(in, 0, in.length); 187 | } 188 | 189 | /** 190 | * Decodes a byte array from Base64 format. 191 | * No blanks or line breaks are allowed within the Base64 encoded input data. 192 | * 193 | * @param in A character array containing the Base64 encoded data. 194 | * @param iOff Offset of the first character in in to be processed. 195 | * @param iLen Number of characters to process in in, starting at iOff. 196 | * @return An array containing the decoded data bytes. 197 | * @throws IllegalArgumentException If the input is not valid Base64 encoded data. 198 | */ 199 | public static byte[] decode(char[] in, int iOff, int iLen) { 200 | if (iLen % 4 != 0) 201 | throw new IllegalArgumentException("Length of Base64 encoded input string is not a multiple of 4."); 202 | while (iLen > 0 && in[iOff + iLen - 1] == '=') iLen--; 203 | int oLen = (iLen * 3) / 4; 204 | byte[] out = new byte[oLen]; 205 | int ip = iOff; 206 | int iEnd = iOff + iLen; 207 | int op = 0; 208 | while (ip < iEnd) { 209 | int i0 = in[ip++]; 210 | int i1 = in[ip++]; 211 | int i2 = ip < iEnd ? in[ip++] : 'A'; 212 | int i3 = ip < iEnd ? in[ip++] : 'A'; 213 | if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) 214 | throw new IllegalArgumentException("Illegal character in Base64 encoded data."); 215 | int b0 = map2[i0]; 216 | int b1 = map2[i1]; 217 | int b2 = map2[i2]; 218 | int b3 = map2[i3]; 219 | if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) 220 | throw new IllegalArgumentException("Illegal character in Base64 encoded data."); 221 | int o0 = (b0 << 2) | (b1 >>> 4); 222 | int o1 = ((b1 & 0xf) << 4) | (b2 >>> 2); 223 | int o2 = ((b2 & 3) << 6) | b3; 224 | out[op++] = (byte) o0; 225 | if (op < oLen) out[op++] = (byte) o1; 226 | if (op < oLen) out[op++] = (byte) o2; 227 | } 228 | return out; 229 | } 230 | 231 | // Dummy constructor. 232 | private Base64Coder() { 233 | } 234 | 235 | 236 | }// end class Base64Coder 237 | --------------------------------------------------------------------------------