aj-s3client:基于 Java 的轻量级 S3 存储客户端项目

轻量级 S3 存储 Java 客户端

分支1Tags0
文件最后提交记录最后更新时间
1 年前
1 年前
1 年前
1 年前
1 年前

Maven Central Javadoc Ask DeepWiki License Email

A Lightweight S3 Client / S3 存储 Java 客户端

如今,直接将二进制文件存储在应用服务器上的做法已不多见。人们更倾向于使用“对象存储”解决方案,例如亚马逊 AWS S3、阿里云 OSS、Cloudflare R2 等。 AWS 是该领域的先驱,其 S3 协议几乎已成为事实上的行业标准。 大多数主流厂商的对象存储产品都已实现了这一协议。 几乎所有厂商都提供了便捷的 SDK,以便快速集成 S3 服务。 就复杂度而言,构建一个 S3 客户端并非难事。 出于一时的“造轮子”冲动,我决定着手开发自己的 S3 客户端。 该组件追求轻量级设计,包含不超过十个类,同时仍能覆盖日常对象存储操作所需的基本功能。

直接支持以下 S3 存储服务:

  • Cloudflare R2
  • Scaleway
  • Backblaze
  • 阿里云 OSS
  • 网易 OSS

教程(中文):https://blog.csdn.net/zhangxin09/article/details/137671230。

源代码

Github | Gitcode

安装

需要 Java 8 或更高版本。


<dependency>
    <groupId>com.ajaxjs</groupId>
    <artifactId>aj-s3client</artifactId>
    <version>1.2</version>
</dependency>

单元测试的配置文件application.yml未随版本控制系统(VCS)提交。其内容如下:

S3Storage:
  Nso:
    accessKey: xx
    accessSecret: xx
    api: nos-eastchina1.126.net
    bucket: xx
  Oss:
    accessKeyId: xx
    secretAccessKey: xx
    endpoint: oss-cn-beijing.aliyuncs.com
    bucket: xx
  LocalStorage: # 本地保存
    absoluteSavePath: c:\temp\ # 若有此值,保存这个绝对路径上

使用方法

步骤 1:设置 OSS 参数

第一步是配置所有必要的 OSS 参数。这些参数在配置对象 Config 中有详细说明。以下是设置这些参数的示例:

/**
 * 配置
 */
@Data
public class Config {
    /**
     * 访问 API
     */
    private String endPoint;

    /**
     * 访问 Key
     */
    private String accessKey;

    /**
     * 访问密钥
     */
    private String secretKey;

    /**
     * 存储桶名称
     */
    private String bucketName;

    /**
     * 签名中的标识,每个厂商不同
     */
    private String remark;
}

此 POJO 可通过 Spring 注入到各厂商的实现中。 如果您未使用 Spring,最简单的方式是像这样手动实例化并配置它:

https://i-blog.csdnimg.cn/blog_migrate/4fd200e1c60016fe1acc5afd70a54ada.png

关于各种 OSS API 操作,请参考 S3Client 接口。 由于 Java 开发者通常对此较为熟悉,这里不再进行详细说明。

package com.ajaxjs.s3client;

import java.util.Map;

/**
 * S3 客户端
 */
public interface S3Client {
    /**
     * 列出存储桶中的所有对象
     *
     * @return XML List
     */
    String listBucket();

    /**
     * 列出存储桶中的所有对象
     *
     * @return XML List Map
     */
    Map<String, String> listBucketXml();

    /**
     * 创建一个存储桶(Bucket)
     *
     * @param bucketName 存储桶的名称,必须全局唯一
     * @return true=操作成功
     */
    boolean createBucket(String bucketName);

    /**
     * 删除一个存储桶(Bucket)
     *
     * @param bucketName 存储桶的名称
     * @return true=操作成功
     */
    boolean deleteBucket(String bucketName);

    /**
     * 将字节数据上传到指定的存储桶中
     *
     * @param bucketName 存储桶的名称
     * @param objectName 对象(文件)在存储桶中的名称
     * @param fileBytes  要上传的文件的字节数据
     * @return true=操作成功
     */
    boolean putObject(String bucketName, String objectName, byte[] fileBytes);

    /**
     * 将字节数据上传到指定的存储桶中
     *
     * @param objectName 对象(文件)在存储桶中的名称
     * @param fileBytes  要上传的文件的字节数据
     * @return true=操作成功
     */
    boolean putObject(String objectName, byte[] fileBytes);

    /**
     * 将字节数据上传到指定的存储桶中
     *
     * @param bucketName 存储桶的名称
     * @param objectName 对象(文件)在存储桶中的名称
     * @return true=操作成功
     */
    boolean getObject(String bucketName, String objectName);

    /**
     * 将字节数据上传到指定的存储桶中
     *
     * @param objectName 对象(文件)在存储桶中的名称
     * @return true=操作成功
     */
    boolean getObject(String objectName);

    /**
     * 删除指定的文件
     *
     * @param bucketName 存储桶的名称
     * @param objectName 要删除的文件名称
     * @return true=操作成功
     */
    boolean deleteObject(String bucketName, String objectName);

    /**
     * 删除指定的文件
     *
     * @param objectName 要删除的文件名称
     * @return true=操作成功
     */
    boolean deleteObject(String objectName);
}

开发心得

在实现这款客户端的过程中,我收获了以下几点心得:

  1. 依赖 HTTP 通信 尽管是对象存储服务,但 OSS 操作本质上围绕文件管理展开——上传、下载、删除文件,以及处理“Bucket”。所有这些交互都通过 HTTP API 完成。因此,客户端必须封装一个 HttpClient 组件来妥善处理 API 调用。Cloudflare R2 为其 API 提供了 Postman 示例,这对我重构 Java HTTP 请求帮助很大。
  2. 身份验证与请求签名 与任何基于协议的通信一样,身份验证是一大挑战。S3 协议要求每个 HTTP 请求都包含 Authorization 头。生成有效的签名是一个相对复杂的过程,涉及多个参数和加密步骤,这部分代码占了相当大的比重。主要有两个签名版本:SigV2 和 SigV4。本组件同时支持这两种协议。
  3. 厂商间的差异 尽管大多数厂商都实现了 S3 协议,但他们各自的 API 往往存在细微差异。为了适应这些差异,代码设计采用了继承结构:BaseS3Client–>BaseS3ClientSigV2/BaseS3ClientSigV4–>厂商特定实现。这种结构允许在基础层共享逻辑,同时支持按厂商进行定制。
  4. 签名生成是最棘手的部分 开发过程中最具挑战性的部分是生成正确的签名。即使是一个小错误也可能导致签名无效,使服务器因验证失败而拒绝请求。在两个版本中,SigV2 相对简单,而 SigV4 涉及更多步骤和更严格的规则。因此,我找到了一个很棒的开源实现,名为 aws-v4-signer-java——它零依赖,非常符合我的需求。我对其代码进行了大量简化和重构,以更好地适应本项目。此外,阿里云关于 S3 兼容签名的文档也非常清晰且有帮助。更多详情,请参考他们的文档源代码
  5. 单元测试仍然极其重要——尤其是在开发或重构组件时。很多情况下,没有单元测试,重现问题会非常困难。扎实的测试覆盖率也能显著提高开发效率。

本组件并非完整的 S3 客户端实现。它仅涵盖了我们软件所需的一小部分 S3 功能。设计目标是简洁、最少的外部依赖以及低内存占用。

项目介绍

轻量级 S3 存储 Java 客户端

定制我的领域