励志语录
- 能学、不断学习新知识、提升自己的能力、跟上时代的步伐。
- 能稳、行事稳重、不骄不躁、每一步都走的扎实、方能走得长远。
- 能静、 内心平静、不被外界的喧腾干扰、保持清醒头脑。
- 能熬、 熬是一种资本、也是一种财富、熬得住者必有所得。
- 能容、 容下不同的意见、三人行必有我师、有容乃大、心宽福自来
文件客户端
文件存储
目前市面上常见的文件存储方案
- 本地磁盘存储 直接在服务器的本地磁盘上存储文件、适用于小型项目或对性能要求较高的场景
- MinIo 一个高性能的分布式对象存储系统、兼容Amazon S3接口、适用于需要高可用和扩展性的场景。
- FastDFS 一个开源的轻量级分布式文件系统、专为海量小文件存储设计、适用于需要高性能和高扩展性的场景
- Hadoop HDFS 一个分布式文件系统、适用于大数据存储和处理、适用于需要处理海量数据的场景
- 云存储服务 如阿里云 OSS、腾讯云、七牛云等、提供高可用、可扩展的存储服务、适用于需要快速部署和高可靠性的场景
Amazon S3 接口通常指的是 Amazon S3(Simple Storage Service)提供的一套基于 REST 的 API 接口。这套接口允许开发者通过 HTTP 请求来管理对象存储服务中的资源。
具体实现
- 定义一个文件客户端
public interface FileClient {
/**
* 获得客户端编号
*
* @return 客户端编号
*/
Long getId();
/**
* 上传文件
*
* @param content 文件流
* @param path 相对路径
* @return 完整路径,即 HTTP 访问地址
*/
String upload(byte[] content, String path);
/**
* 删除文件
*
* @param path 相对路径
*/
void delete(String path);
/**
* 获得文件的内容
*
* @param path 相对路径
* @return 文件的内容
*/
byte[] getContent(String path);
}
- 因为不同存储器的配置也会有所不同 这里定义一个
AbstractFileClient<Connfig>抽象类用于存储不同的配置数据
import lombok.extern.slf4j.Slf4j;
/**
* 文件客户端的抽象类,提供模板方法,减少子类的冗余代码
*/
@Slf4j
public abstract class AbstractFileClient <Config extends FileClientConfig> implements FileClient{
/**
* 配置编号
*/
private final Long id;
/**
* 文件配置
*/
protected Config config;
public AbstractFileClient(Long id, Config config) {
this.id = id;
this.config = config;
}
/**
* 初始化
*/
public final void init() {
doInit();
log.debug("[init][配置({}) 初始化完成]", config);
}
/**
* 自定义初始化
*/
protected abstract void doInit();
public final void refresh(Config config) {
// 判断是否更新
if (config.equals(this.config)) {
return;
}
log.info("[refresh][配置({})发生变化,重新初始化]", config);
this.config = config;
// 初始化
this.init();
}
@Override
public Long getId() {
return id;
}
}
这里的FileClientConfig接口是一个标识接口、方便用户实现该接口进行修改配置
- 实现客户端
- 配置类 : 这里定义的是minIo需要配置信息
@Data public class S3FileClientConfig implements FileClientConfig { @NotNull(message = "endpoint 不能为空") private String endpoint; /** * 自定义域名 */ @URL(message = "domain 必须是 URL 格式") private String domain; /** * 存储 Bucket */ @NotNull(message = "bucket 不能为空") private String bucket; /** * 访问 Key */ @NotNull(message = "accessKey 不能为空") private String accessKey; /** * 访问 Secret */ @NotNull(message = "accessSecret 不能为空") private String accessSecret; } - 实现类
/** * 基于 S3 协议的文件客户端,实现 MinIO、阿里云、腾讯云、七牛云、华为云等云服务 * <p> * S3 协议的客户端,采用亚马逊提供的 software.amazon.awssdk.s3 库 * * @author 芋道源码 */ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { private MinioClient client; public S3FileClient(Long id, S3FileClientConfig config) { super(id, config); } @Override protected void doInit() { // 补全 domain if (StrUtil.isEmpty(config.getDomain())) { config.setDomain(buildDomain()); } // 初始化客户端 client = MinioClient.builder() .endpoint(buildEndpointURL()) // Endpoint URL .region(buildRegion()) // Region .credentials(config.getAccessKey(), config.getAccessSecret()) // 认证密钥 .build(); } /** * 基于 bucket + endpoint 构建访问的 Domain 地址 * * @return Domain 地址 */ private String buildDomain() { // 如果已经是 http 或者 https , 则不进行拼接,主要适配 MinIo if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) { return StrUtil.format("{}/{}", config.getEndpoint(), config.getBucket()); } return StrUtil.format("https://{}.{}", config.getBucket(), config.getEndpoint()); } /** * 基于 endpoint 构建调用云服务的 URL 地址 * * @return URI 地址 */ private String buildEndpointURL() { // 如果已经是 http 或者 https,则不进行拼接.主要适配 MinIO if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) { return config.getEndpoint(); } return StrUtil.format("https://{}", config.getEndpoint()); } /** * 基于 bucket 构建 region 地区 * * @return region 地区 */ private String buildRegion() { // 阿里云必须有 region,否则会报错 if (config.getEndpoint().contains(S3FileClientConfig.ENDPOINT_ALIYUN)) { return StrUtil.subBefore(config.getEndpoint(), '.', false) .replaceAll("-internal", "")// 去除内网 Endpoint 的后缀 .replaceAll("https://", ""); } // 腾讯云必须有 region,否则会报错 if (config.getEndpoint().contains(S3FileClientConfig.ENDPOINT_TENCENT)) { return StrUtil.subAfter(config.getEndpoint(), "cos.", false) .replaceAll("." + S3FileClientConfig.ENDPOINT_TENCENT, ""); // 去除 Endpoint } return null; } @Override public String upload(byte[] content, String path, String type) throws Exception { // 执行上传 client.putObject(PutObjectArgs.builder() .bucket(config.getBucket()) // bucket 必须传递 .contentType(type) .object(path) // 相对路径作为 key .stream(new ByteArrayInputStream(content), content.length, -1) // 文件内容 .build()); // 拼接返回路径 return config.getDomain() + "/" + path; } @Override public byte[] getContent(String path) throws Exception { GetObjectResponse response = client.getObject(GetObjectArgs.builder() .bucket(config.getBucket()) // bucket 必须传递 .object(path) // 相对路径作为 key .build()); return IoUtil.readBytes(response); } @Override public void delete(String path) throws Exception { } }
- 配置类 : 这里定义的是minIo需要配置信息
测试
public class S3FileClientTest {
@Test
@Disabled // MinIO,如果要集成测试,可以注释本行
public void testMinIO() throws Exception {
S3FileClientConfig config = new S3FileClientConfig();
// 配置成你自己的
config.setAccessKey("admin");
config.setAccessSecret("12345678111");
config.setBucket("zhou.wenfeng");
config.setDomain(null);
// 默认 9000 endpoint
config.setEndpoint("http://127.0.0.1:9000");
// 执行上传
testExecuteUpload(config);
}
private void testExecuteUpload(S3FileClientConfig config) throws Exception {
// 创建 Client
S3FileClient client = new S3FileClient(0L, config);
client.init();
// 上传文件
String path = IdUtil.fastSimpleUUID() + ".jpg";
byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
String fullPath = client.upload(content, path, "image/jpeg");
System.out.println("访问地址:" + fullPath);
// 读取文件
if (true) {
byte[] bytes = client.getContent(path);
System.out.println("文件内容:" + bytes.length);
}
}
}