目前只实现了 S3 的存储,提供同步和异步两种操作接口。

AWS 凭证提供者

S3 存储模块依赖 AWS 凭证提供者来认证访问。框架提供了两个版本的凭证提供者,分别对应 AWS SDK v1 和 v2。

AWS SDK v1 凭证提供者

提供 AWSCredentialsProvider Bean,供 S3 v1 及其他 AWS SDK v1 模块使用。

仓库坐标

<dependency>
    <groupId>com.luamas.thinkbridge</groupId>
    <artifactId>aws-spring-boot-starter</artifactId>
</dependency>
implementation("com.luamas.thinkbridge:aws-spring-boot-starter")

配置

think:
  aws:
    # 是否启用,默认启用
    enable: true
    # AWS Access Key ID
    access-key-id: 'TODO key'
    # AWS Secret Access Key
    secret-access-key-id: 'TODO'

配置属性一览

属性

默认值

说明

think.aws.enable

true

是否启用 AWS 凭证提供者

think.aws.access-key-id

空

AWS Access Key ID

think.aws.secret-access-key-id

空

AWS Secret Access Key

注册的Bean

Bean名称 类型 说明

awsCredentialsProvider

AWSCredentialsProvider

AWS SDK v1 静态凭证提供者

AWS SDK v2 凭证提供者

提供 AwsCredentialsProvider Bean,供 S3 v2 及其他 AWS SDK v2 模块使用。

仓库坐标

<dependency>
    <groupId>com.luamas.thinkbridge</groupId>
    <artifactId>aws-v2-spring-boot-starter</artifactId>
</dependency>
implementation("com.luamas.thinkbridge:aws-v2-spring-boot-starter")

配置

think:
  aws:
    # 是否启用,默认启用
    enable: true
    # AWS Access Key ID
    access-key-id: 'TODO key'
    # AWS Secret Access Key
    secret-access-key-id: 'TODO'
AWS v1 和 v2 凭证提供者使用相同的配置属性前缀 think.aws,但 Bean 类型不同。根据引入的 starter 自动选择对应版本的凭证提供者。

注册的Bean

Bean名称 类型 说明

awsCredentialsProvider

AwsCredentialsProvider

AWS SDK v2 静态凭证提供者

同步接口

package com.luamas.thinkbridge.common.upload.service;

import org.apache.commons.lang3.time.DateFormatUtils;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.time.Duration;
import java.util.Date;
import java.util.Map;

/
 * 文件上传下载接口
 /
public interface UploadService {
    String DATE_PATH = "yyyy/MM/dd";
    String CONTENT_TYPE = "Content-Type";
    String CONTENT_LENGTH = "Content-Length";
    String CONTENT_MD5 = "Content-MD5";
    String ETAG = "ETag";


    /
     * 获取文件md5值
     * @return
     */
    default String getContentMd5(String bucket, String fullFilePath) {
        if (getFileHeaders(bucket, fullFilePath).containsKey(CONTENT_MD5)) {
            return (String) getFileHeaders(bucket, fullFilePath).get(CONTENT_MD5);
        }
        return null;
    }

    default String getContentMd5(String fullFilePath) {
        return getContentMd5(null, fullFilePath);
    }

    /
     * 获取文件类型
     * @return
     */
    default String getContentType(String bucket, String fullFilePath) {
        if (getFileHeaders(bucket, fullFilePath).containsKey(CONTENT_TYPE)) {
            return (String) getFileHeaders(bucket, fullFilePath).get(CONTENT_TYPE);
        }
        return null;
    }

    default String getContentType(String fullFilePath) {
        return getContentType(null, fullFilePath);
    }

    /
     * 获取文件长度
     * @return
     */
    default String getContentLength(String bucket, String fullFilePath) {
        if (getFileHeaders(bucket, fullFilePath).containsKey(CONTENT_LENGTH)) {
            return (String) getFileHeaders(bucket, fullFilePath).get(CONTENT_LENGTH);
        }
        return null;
    }
    default String getContentLength(String fullFilePath) {
        return getContentLength(null, fullFilePath);
    }

    /
     * 获取文件etag,hash值
     * @return
     */
    default String getEtag(String bucket, String fullFilePath) {
        if (getFileHeaders(bucket, fullFilePath).containsKey(ETAG)) {
            return (String) getFileHeaders(bucket, fullFilePath).get(ETAG);
        }
        return null;
    }
    default String getEtag(String fullFilePath) {
        return getEtag(null, fullFilePath);
    }

    default Map<String, Object> getFileHeaders(String fullFilePath) {
        return getFileHeaders(null, fullFilePath);
    }

    Map<String, Object> getFileHeaders(String bucket, String fullFilePath);



    /
     * 缓存路径
     * @return
     */
    default String tempPath() {
        String filePath = "temp" +
                "/" +
                DateFormatUtils.format(new Date(), DATE_PATH);
        return filePath;
    }

    /
     * 文件上传
     * @param bucket 桶
     * @param inputStream 文件流
     * @param filePath 文件路径
     * @param fileName 文件名
     * @param isOpenUrl url是否为开放状态
     * @return
     */
    String upload(final String bucket, final InputStream inputStream, final String filePath, final String fileName, final boolean isOpenUrl);

    /
     * 文件上传
     * @param inputStream 文件流
     * @param filePath 文件路径
     * @param fileName 文件名
     * @param isOpenUrl url是否为开放状态
     * @return
     */
    default String upload(final InputStream inputStream, final String filePath, final String fileName, final boolean isOpenUrl) {
        return upload(null, inputStream, filePath, fileName, isOpenUrl);
    }

    /
     * 上传私有文件
     * @param inputStream 文件流
     * @param filePath 文件路径
     * @param fileName 文件名
     * @return
     */
    default String upload(final InputStream inputStream, final String filePath, final String fileName) {
        return upload(inputStream, filePath, fileName, false);
    }

    /
     * 上传私有文件
     * @param inputStream 文件流
     * @param fileName 文件名
     * @return
     */
    default String upload(final InputStream inputStream, final String fileName) {
        return upload(inputStream, null, fileName);
    }

    /
     * 文件上传
     * @param bucket 桶
     * @param file 文件
     * @param filePath 文件路径
     * @param fileName 文件名
     * @param isOpenUrl url是否为开放状态
     * @return
     */
    String upload(final String bucket, final File file, final String filePath, final String fileName, final boolean isOpenUrl);


    /
     * 文件上传
     * @param file 文件
     * @param filePath 文件路径
     * @param fileName 文件名
     * @param isOpenUrl url是否为开放状态
     * @return
     */
    default String upload(final File file, final String filePath, final String fileName, final boolean isOpenUrl) {
        return upload(null, file, filePath, fileName, isOpenUrl);
    }

    /
     * 上传私有文件
     * @param file 文件
     * @param filePath 文件路径
     * @param fileName 文件名
     * @return
     */
    default String upload(final File file, final String filePath, final String fileName) {
        return upload(file, filePath, fileName, false);
    }

    /
     * 上传私有文件
     * @param file 文件
     * @param fileName 文件名
     * @return
     */
    default String upload(final File file, final String fileName) {
        return upload(file, null, fileName);
    }

    /
     * 根据路径获取url
     * @param bucket 桶
     * @param fullFilePath
     * @param timeToLive 文件上传后获取url的有效期
     * @param isOpenUrl 开放
     * @return timeToLive有效期的url
     */
    String getUrl(final String bucket, final String fullFilePath, final Duration timeToLive, final boolean isOpenUrl);

    /
     * 根据路径获取url
     * @param bucket 桶
     * @param fullFilePath
     * @return timeToLive有效期的url
     */
    default String getOpenUrl(final String bucket, final String fullFilePath) {
        return getUrl(bucket, fullFilePath, null, true);
    }

    /
     * 根据路径获取url
     * @param bucket 桶
     * @param fullFilePath
     * @param timeToLive 文件上传后获取url的有效期
     * @return timeToLive有效期的url
     */
    default String getUrl(final String bucket, final String fullFilePath, final Duration timeToLive) {
        return getUrl(bucket, fullFilePath, timeToLive, false);
    }

    /
     * 根据路径获取url
     * @param fullFilePath
     * @param timeToLive 文件上传后获取url的有效期
     * @return timeToLive有效期的url
     */
    default String getUrl(final String fullFilePath, final Duration timeToLive) {
        return getUrl(null, fullFilePath, timeToLive);
    }


    /
     * 根据路径获取url
     * @param fullFilePath 全路径
     * @return 默认为一小时的有效期的url
     */
    default String getUrl(final String fullFilePath) {
        return getUrl(fullFilePath, null);
    }

    /
     * 删除文件
     * @param bucket 桶
     * @param fullFilePath
     */
    void delete(final String bucket, final String fullFilePath);

    /
     * 删除文件
     * @param fullFilePath
     */
    default void delete(final String fullFilePath) {
        delete(null, fullFilePath);
    }

    /
     * 下载文件到指定路径
     * @param bucket 桶
     * @param fullFilePath 远程全路径
     * @param localFullFilePath 本地全路径
     */
    void download(final String bucket, final String fullFilePath, final String localFullFilePath);

    /
     * 下载文件到指定路径
     * @param fullFilePath 远程全路径
     * @param localFullFilePath 本地全路径
     */
    default void download(final String fullFilePath, final String localFullFilePath) {
        download(null, fullFilePath, localFullFilePath);
    }

    /
     * 下载文件到输出流
     * @param bucket 桶
     * @param fullFilePath 远程全路径
     * @param outputStream 输出流
     */
    void download(final String bucket, final String fullFilePath, final OutputStream outputStream);

    /
     * 下载文件到输出流
     * @param fullFilePath 远程全路径
     * @param outputStream 输出流
     */
    default void download(final String fullFilePath, final OutputStream outputStream) {
        download(null, fullFilePath, outputStream);
    }

    /
     * 文件上传
     * @param file 文件
     * @param filePath 文件路径
     * @param fileName 文件名
     * @return 默认为一小时的有效期的url
     */
    default String uploadFile(final File file, final String filePath, final String fileName) {
        return uploadFile(file, filePath, fileName, null);
    }

    /
     * 文件上传
     * @param file 文件
     * @param filePath 文件路径
     * @param fileName 文件名
     * @param timeToLive 文件上传后获取url的有效期
     * @return timeToLive有效期的url
     */
    default String uploadFile(final File file, final String filePath, final String fileName, final Duration timeToLive) {
        String key = upload(file, filePath, fileName);
        return getUrl(key, timeToLive);
    }

    /
     * 文件上传
     * @param inputStream 文件流
     * @param filePath 文件路径
     * @param fileName 文件名
     * @return 默认为一小时的有效期的url
     */
    default String uploadFile(final InputStream inputStream, final String filePath, final String fileName) {
        return uploadFile(inputStream, filePath, fileName, null);
    }

    /
     * 文件上传
     * @param inputStream 文件流
     * @param filePath 文件路径
     * @param fileName 文件名
     * @param timeToLive 文件上传后获取url的有效期
     * @return timeToLive有效期的url
     */
    default String uploadFile(final InputStream inputStream, final String filePath, String fileName, final Duration timeToLive) {
        String key = upload(inputStream, filePath, fileName);
        return getUrl(key, timeToLive);
    }

    /
     * 文件上传至temp文件夹,temp默认为临时目录,为了将来更好清理
     * @param fileName 文件名
     * @return 默认为一小时的有效期的url
     */
    default String uploadTempFile(final File file, final String fileName) {
        return uploadTempFile(file, fileName, null);
    }

    /
     * 文件上传至temp文件夹,temp默认为临时目录,为了将来更好清理
     * @param file 文件
     * @param fileName 文件名
     * @param timeToLive 文件上传后获取url的有效期
     * @return timeToLive有效期的url
     */
    default String uploadTempFile(final File file, final String fileName, final Duration timeToLive) {
        String key = upload(file, tempPath(), fileName);
        return getUrl(key, timeToLive);
    }

    /
     * 文件上传至temp文件夹,temp默认为临时目录,为了将来更好清理
     * @param inputStream 文件流
     * @param fileName 文件名
     * @return 默认为一小时的有效期的url
     */
    default String uploadTempFile(final InputStream inputStream, final String fileName) {
        return uploadTempFile(inputStream, fileName, null);
    }

    /
     * 文件上传至temp文件夹,temp默认为临时目录,为了将来更好清理
     * @param inputStream 文件流
     * @param fileName 文件名
     * @param timeToLive 文件上传后获取url的有效期
     * @return timeToLive有效期的url
     */
    default String uploadTempFile(final InputStream inputStream, final String fileName, final Duration timeToLive) {
        String key = upload(inputStream, tempPath(), fileName);
        return getUrl(key, timeToLive);
    }

    /
     * 文件上上传且url地址为开放状态
     * @param file 文件
     * @param filePath 文件路径
     * @param fileName 文件名
     * @return
     */
    default String uploadCanReadFile(final File file, final String filePath, final String fileName) {
        return uploadCanReadFile(file, filePath, fileName, null);
    }
    /
     * 文件上上传且url地址为开放状态
     * @param file 文件
     * @param filePath 文件路径
     * @param fileName 文件名
     * @param timeToLive 文件上传后获取url的有效期 ,暂时无效
     * @return timeToLive有效期的url
     */
    default String uploadCanReadFile(final File file, final String filePath, final String fileName, final Duration timeToLive) {
        String key = upload(file, filePath, fileName, true);
        return getUrl(key, timeToLive);
    }

    /
     * 文件上上传且url地址为开放状态
     * @param inputStream 文件流
     * @param filePath 文件路径
     * @param fileName 文件名
     * @return timeToLive有效期的url
     */
    default String uploadCanReadFile(final InputStream inputStream, final String filePath, final String fileName) {
        return uploadCanReadFile(inputStream, filePath, fileName, null);
    }

    /
     * 文件上上传且url地址为开放状态
     * @param inputStream 文件流
     * @param filePath 文件路径
     * @param fileName 文件名
     * @param timeToLive 文件上传后获取url的有效期 ,暂时无效
     * @return timeToLive有效期的url
     */
    default String uploadCanReadFile(final InputStream inputStream, final String filePath, final String fileName, final Duration timeToLive) {
        String key = upload(inputStream, filePath, fileName, true);
        return getUrl(key, timeToLive);
    }

    /
     * 文件上上传且url地址为开放状态
     * @param file 文件
     * @param fileName 文件名
     * @return
     */
    default String uploadCanReadTempFile(final File file, final String fileName) {
        return uploadCanReadTempFile(file, fileName, null);
    }

    /
     * 文件上上传且url地址为开放状态
     * @param file 文件
     * @param fileName 文件名
     * @param timeToLive 文件上传后获取url的有效期 ,暂时无效
     * @return
     */
    default String uploadCanReadTempFile(final File file, final String fileName, final Duration timeToLive) {
        String key = upload(file, tempPath(), fileName);
        return getUrl(key, timeToLive);
    }

    /
     * 文件上上传且url地址为开放状态
     * @param inputStream 文件流
     * @param fileName 文件名
     */
    default String uploadCanReadTempFile(final InputStream inputStream, final String fileName) {
        return uploadCanReadTempFile(inputStream, fileName, null);
    }

    /*
     * 文件上上传且url地址为开放状态
     * @param inputStream 文件流
     * @param fileName 文件名
     * @param timeToLive 文件上传后获取url的有效期 ,暂时无效
     * @return
     */
    default String uploadCanReadTempFile(final InputStream inputStream, final String fileName, final Duration timeToLive) {
        String key = upload(inputStream, tempPath(), fileName);
        return getUrl(key, timeToLive);
    }

}

异步接口

异步接口只有 S3 v2 包支持。
package com.luamas.thinkbridge.common.upload.service;

import org.apache.commons.lang3.time.DateFormatUtils;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.time.Duration;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

/
 * 文件上传下载接口
 /
public interface AsyncUploadService {
    String DATE_PATH = "yyyy/MM/dd";
    String CONTENT_TYPE = "Content-Type";
    String CONTENT_LENGTH = "Content-Length";
    String CONTENT_MD5 = "Content-MD5";
    String ETAG = "ETag";


    /
     * 获取文件md5值
     * @return
     */
    default CompletableFuture<String> getContentMd5(String bucket, String fullFilePath) {
        return getFileHeaders(bucket, fullFilePath).thenApply(headers -> {
            if (headers.containsKey(CONTENT_MD5)) {
                return (String) headers.get(CONTENT_MD5);
            }
            return null;
        });
    }

    default CompletableFuture<String> getContentMd5(String fullFilePath) {
        return getContentMd5(null, fullFilePath);
    }

    /
     * 获取文件类型
     * @return
     */
    default CompletableFuture<String> getContentType(String bucket, String fullFilePath) {
        return getFileHeaders(bucket, fullFilePath).thenApply(headers -> {
            if (headers.containsKey(CONTENT_TYPE)) {
                return (String) headers.get(CONTENT_TYPE);
            }
            return null;
        });
    }

    default CompletableFuture<String> getContentType(String fullFilePath) {
        return getContentType(null, fullFilePath);
    }

    /
     * 获取文件长度
     * @return
     */
    default CompletableFuture<String> getContentLength(String bucket, String fullFilePath) {
        return getFileHeaders(bucket, fullFilePath).thenApply(headers -> {
            if (headers.containsKey(CONTENT_LENGTH)) {
                return (String) headers.get(CONTENT_LENGTH);
            }
            return null;
        });
    }
    default CompletableFuture<String> getContentLength(String fullFilePath) {
        return getContentLength(null, fullFilePath);
    }

    /
     * 获取文件etag,hash值
     * @return
     */
    default CompletableFuture<String> getEtag(String bucket, String fullFilePath) {
        return getFileHeaders(bucket, fullFilePath).thenApply(headers -> {
            if (headers.containsKey(ETAG)) {
                return (String) headers.get(ETAG);
            }
            return null;
        });
    }
    default CompletableFuture<String> getEtag(String fullFilePath) {
        return getEtag(null, fullFilePath);
    }

    default CompletableFuture<Map<String, Object>> getFileHeaders(String fullFilePath) {
        return getFileHeaders(null, fullFilePath);
    }

    CompletableFuture<Map<String, Object>> getFileHeaders(String bucket, String fullFilePath);



    /
     * 缓存路径
     * @return
     */
    default String tempPath() {
        String filePath = "temp" +
                "/" +
                DateFormatUtils.format(new Date(), DATE_PATH);
        return filePath;
    }

    /
     * 文件上传
     * @param bucket 桶
     * @param inputStream 文件流
     * @param filePath 文件路径
     * @param fileName 文件名
     * @param isOpenUrl url是否为开放状态
     * @return
     */
    CompletableFuture<String> upload(final String bucket, final InputStream inputStream, final String filePath, final String fileName, final boolean isOpenUrl);

    /
     * 文件上传
     * @param inputStream 文件流
     * @param filePath 文件路径
     * @param fileName 文件名
     * @param isOpenUrl url是否为开放状态
     * @return
     */
    default CompletableFuture<String> upload(final InputStream inputStream, final String filePath, final String fileName, final boolean isOpenUrl) {
        return upload(null, inputStream, filePath, fileName, isOpenUrl);
    }

    /
     * 上传私有文件
     * @param inputStream 文件流
     * @param filePath 文件路径
     * @param fileName 文件名
     * @return
     */
    default CompletableFuture<String> upload(final InputStream inputStream, final String filePath, final String fileName) {
        return upload(inputStream, filePath, fileName, false);
    }

    /
     * 上传私有文件
     * @param inputStream 文件流
     * @param fileName 文件名
     * @return
     */
    default CompletableFuture<String> upload(final InputStream inputStream, final String fileName) {
        return upload(inputStream, null, fileName);
    }

    /
     * 文件上传
     * @param bucket 桶
     * @param file 文件
     * @param filePath 文件路径
     * @param fileName 文件名
     * @param isOpenUrl url是否为开放状态
     * @return
     */
    CompletableFuture<String> upload(final String bucket, final File file, final String filePath, final String fileName, final boolean isOpenUrl);


    /
     * 文件上传
     * @param file 文件
     * @param filePath 文件路径
     * @param fileName 文件名
     * @param isOpenUrl url是否为开放状态
     * @return
     */
    default CompletableFuture<String> upload(final File file, final String filePath, final String fileName, final boolean isOpenUrl) {
        return upload(null, file, filePath, fileName, isOpenUrl);
    }

    /
     * 上传私有文件
     * @param file 文件
     * @param filePath 文件路径
     * @param fileName 文件名
     * @return
     */
    default CompletableFuture<String> upload(final File file, final String filePath, final String fileName) {
        return upload(file, filePath, fileName, false);
    }

    /
     * 上传私有文件
     * @param file 文件
     * @param fileName 文件名
     * @return
     */
    default CompletableFuture<String> upload(final File file, final String fileName) {
        return upload(file, null, fileName);
    }

    /
     * 根据路径获取url
     * @param bucket 桶
     * @param fullFilePath
     * @param timeToLive 文件上传后获取url的有效期
     * @param isOpenUrl 开放
     * @return timeToLive有效期的url
     */
    String getUrl(final String bucket, final String fullFilePath, final Duration timeToLive, final boolean isOpenUrl);

    /
     * 根据路径获取url
     * @param bucket 桶
     * @param fullFilePath
     * @return timeToLive有效期的url
     */
    default String getOpenUrl(final String bucket, final String fullFilePath) {
        return getUrl(bucket, fullFilePath, null, true);
    }

    /
     * 根据路径获取url
     * @param bucket 桶
     * @param fullFilePath
     * @param timeToLive 文件上传后获取url的有效期
     * @return timeToLive有效期的url
     */
    default String getUrl(final String bucket, final String fullFilePath, final Duration timeToLive) {
        return getUrl(bucket, fullFilePath, timeToLive, false);
    }

    /
     * 根据路径获取url
     * @param fullFilePath
     * @param timeToLive 文件上传后获取url的有效期
     * @return timeToLive有效期的url
     */
    default String getUrl(final String fullFilePath, final Duration timeToLive) {
        return getUrl(null, fullFilePath, timeToLive);
    }


    /
     * 根据路径获取url
     * @param fullFilePath 全路径
     * @return 默认为一小时的有效期的url
     */
    default String getUrl(final String fullFilePath) {
        return getUrl(fullFilePath, null);
    }

    /
     * 删除文件
     * @param bucket 桶
     * @param fullFilePath
     */
    CompletableFuture<Void> delete(final String bucket, final String fullFilePath);

    /
     * 删除文件
     * @param fullFilePath
     */
    default CompletableFuture<Void> delete(final String fullFilePath) {
        return delete(null, fullFilePath);
    }

    /
     * 下载文件到指定路径
     * @param bucket 桶
     * @param fullFilePath 远程全路径
     * @param localFullFilePath 本地全路径
     */
    CompletableFuture<Void> download(final String bucket, final String fullFilePath, final String localFullFilePath);

    /
     * 下载文件到指定路径
     * @param fullFilePath 远程全路径
     * @param localFullFilePath 本地全路径
     */
    default CompletableFuture<Void> download(final String fullFilePath, final String localFullFilePath) {
        return download(null, fullFilePath, localFullFilePath);
    }

    /
     * 下载文件到输出流
     * @param bucket 桶
     * @param fullFilePath 远程全路径
     * @param outputStream 输出流
     */
    CompletableFuture<Void> download(final String bucket, final String fullFilePath, final OutputStream outputStream);

    /
     * 下载文件到输出流
     * @param fullFilePath 远程全路径
     * @param outputStream 输出流
     */
    default CompletableFuture<Void> download(final String fullFilePath, final OutputStream outputStream) {
        return download(null, fullFilePath, outputStream);
    }

    /
     * 文件上传
     * @param file 文件
     * @param filePath 文件路径
     * @param fileName 文件名
     * @return 默认为一小时的有效期的url
     */
    default CompletableFuture<String> uploadFile(final File file, final String filePath, final String fileName) {
        return uploadFile(file, filePath, fileName, null);
    }

    /
     * 文件上传
     * @param file 文件
     * @param filePath 文件路径
     * @param fileName 文件名
     * @param timeToLive 文件上传后获取url的有效期
     * @return timeToLive有效期的url
     */
    default CompletableFuture<String> uploadFile(final File file, final String filePath, final String fileName, final Duration timeToLive) {
        return upload(file, filePath, fileName).thenApply(key -> getUrl(key, timeToLive));
    }

    /
     * 文件上传
     * @param inputStream 文件流
     * @param filePath 文件路径
     * @param fileName 文件名
     * @return 默认为一小时的有效期的url
     */
    default CompletableFuture<String> uploadFile(final InputStream inputStream, final String filePath, final String fileName) {
        return uploadFile(inputStream, filePath, fileName, null);
    }

    /
     * 文件上传
     * @param inputStream 文件流
     * @param filePath 文件路径
     * @param fileName 文件名
     * @param timeToLive 文件上传后获取url的有效期
     * @return timeToLive有效期的url
     */
    default CompletableFuture<String> uploadFile(final InputStream inputStream, final String filePath, String fileName, final Duration timeToLive) {
        return upload(inputStream, filePath, fileName).thenApply(key -> getUrl(key, timeToLive));
    }

    /
     * 文件上传至temp文件夹,temp默认为临时目录,为了将来更好清理
     * @param fileName 文件名
     * @return 默认为一小时的有效期的url
     */
    default CompletableFuture<String> uploadTempFile(final File file, final String fileName) {
        return uploadTempFile(file, fileName, null);
    }

    /
     * 文件上传至temp文件夹,temp默认为临时目录,为了将来更好清理
     * @param file 文件
     * @param fileName 文件名
     * @param timeToLive 文件上传后获取url的有效期
     * @return timeToLive有效期的url
     */
    default CompletableFuture<String> uploadTempFile(final File file, final String fileName, final Duration timeToLive) {
        return upload(file, tempPath(), fileName).thenApply(key -> getUrl(key, timeToLive));
    }

    /
     * 文件上传至temp文件夹,temp默认为临时目录,为了将来更好清理
     * @param inputStream 文件流
     * @param fileName 文件名
     * @return 默认为一小时的有效期的url
     */
    default CompletableFuture<String> uploadTempFile(final InputStream inputStream, final String fileName) {
        return uploadTempFile(inputStream, fileName, null);
    }

    /
     * 文件上传至temp文件夹,temp默认为临时目录,为了将来更好清理
     * @param inputStream 文件流
     * @param fileName 文件名
     * @param timeToLive 文件上传后获取url的有效期
     * @return timeToLive有效期的url
     */
    default CompletableFuture<String> uploadTempFile(final InputStream inputStream, final String fileName, final Duration timeToLive) {
        return upload(inputStream, tempPath(), fileName).thenApply(key -> getUrl(key, timeToLive));
    }

    /
     * 文件上上传且url地址为开放状态
     * @param file 文件
     * @param filePath 文件路径
     * @param fileName 文件名
     * @return
     */
    default CompletableFuture<String> uploadCanReadFile(final File file, final String filePath, final String fileName) {
        return uploadCanReadFile(file, filePath, fileName, null);
    }
    /
     * 文件上上传且url地址为开放状态
     * @param file 文件
     * @param filePath 文件路径
     * @param fileName 文件名
     * @param timeToLive 文件上传后获取url的有效期 ,暂时无效
     * @return timeToLive有效期的url
     */
    default CompletableFuture<String> uploadCanReadFile(final File file, final String filePath, final String fileName, final Duration timeToLive) {
        return upload(file, filePath, fileName, true).thenApply(key -> getUrl(key, timeToLive));
    }

    /
     * 文件上上传且url地址为开放状态
     * @param inputStream 文件流
     * @param filePath 文件路径
     * @param fileName 文件名
     * @return timeToLive有效期的url
     */
    default CompletableFuture<String> uploadCanReadFile(final InputStream inputStream, final String filePath, final String fileName) {
        return uploadCanReadFile(inputStream, filePath, fileName, null);
    }

    /
     * 文件上上传且url地址为开放状态
     * @param inputStream 文件流
     * @param filePath 文件路径
     * @param fileName 文件名
     * @param timeToLive 文件上传后获取url的有效期 ,暂时无效
     * @return timeToLive有效期的url
     */
    default CompletableFuture<String> uploadCanReadFile(final InputStream inputStream, final String filePath, final String fileName, final Duration timeToLive) {
        return upload(inputStream, filePath, fileName, true).thenApply(key -> getUrl(key, timeToLive));
    }

    /
     * 文件上上传且url地址为开放状态
     * @param file 文件
     * @param fileName 文件名
     * @return
     */
    default CompletableFuture<String> uploadCanReadTempFile(final File file, final String fileName) {
        return uploadCanReadTempFile(file, fileName, null);
    }

    /
     * 文件上上传且url地址为开放状态
     * @param file 文件
     * @param fileName 文件名
     * @param timeToLive 文件上传后获取url的有效期 ,暂时无效
     * @return
     */
    default CompletableFuture<String> uploadCanReadTempFile(final File file, final String fileName, final Duration timeToLive) {
        return upload(file, tempPath(), fileName, true).thenApply(key -> getUrl(key, timeToLive));
    }

    /
     * 文件上上传且url地址为开放状态
     * @param inputStream 文件流
     * @param fileName 文件名
     */
    default CompletableFuture<String> uploadCanReadTempFile(final InputStream inputStream, final String fileName) {
        return uploadCanReadTempFile(inputStream, fileName, null);
    }

    /*
     * 文件上上传且url地址为开放状态
     * @param inputStream 文件流
     * @param fileName 文件名
     * @param timeToLive 文件上传后获取url的有效期 ,暂时无效
     * @return
     */
    default CompletableFuture<String> uploadCanReadTempFile(final InputStream inputStream, final String fileName, final Duration timeToLive) {
        return upload(inputStream, tempPath(), fileName, true).thenApply(key -> getUrl(key, timeToLive));
    }

}

使用示例

@Resource
private UploadService uploadService;

// 上传文件
String url = uploadService.upload(inputStream, "bucket", "key", "content-type");

// 下载文件
InputStream is = uploadService.download("bucket", "key");

// 删除文件
uploadService.delete("bucket", "key");

S3

仓库坐标

Maven

<dependency>
    <groupId>com.luamas.thinkbridge</groupId>
    <artifactId>aws-s3-spring-boot-starter</artifactId>
</dependency>

Gradle

implementation("com.luamas.thinkbridge:aws-s3-spring-boot-starter")

配置

think:
  aws:
    # aws 的授权配置
    enable: true
    access-key-id: 'TODO key'
    secret-access-key-id: 'TODO'
    s3:
      # 是否启用
      enable: true
      # aws 地址
      service-endpoint: http://10.96.112.200:8080
      # 如果非外网的私有云直接使用以下地址即可,如果私有云使用反向代理,配置代理地址即可
      endpoint-url: http://10.96.112.200:8080
      # 区域,如果使用私有云的 s3 随意填写
      region: us-east-1
      bucket-name: '桶名称'
      # 是否为 aws,私有云设为 false
      is-aws: false
      # 启动时是否检查桶是否存在
      check-bucket-name-exist: true
      # 是否为路径样式(true=路径风格,false=虚拟主机风格),私有云通常设为 true
      path-style-access-enabled: true
      # 是否禁用分块编码,默认 false
      chunked-encoding-disabled: false
      # 是否启用加速模式,默认 false
      accelerate-mode-enabled: false
      # 是否启用有效负载签名,默认 false
      payload-signing-enabled: false
      # 是否启用双栈模式,默认 false
      dualstack-enabled: false
      # 是否强制全局桶访问,默认 false
      force-global-bucket-access-enabled: false
      # 是否使用 ARN 解析区域,默认 false
      use-arn-region-enabled: false
      # 是否启用 us-east-1 区域的全局端点,默认 false
      regional-us-east-1-endpoint-enabled: false

自动配置

自动配置类为 AwsS3AutoConfiguration,当 think.aws.s3.enable=true(默认为true)时自动生效。

注册的Bean

Bean名称 类型 说明

amazonS3

AmazonS3

AWS S3 v1 客户端,依赖 AWSCredentialsProvider

awsS3UploadService

UploadService

同步上传服务,依赖 AmazonS3

使用

注入上传服务

@Autowired
private UploadService awsS3UploadService;

上传文件

// 上传私有文件(InputStream)
String key = awsS3UploadService.upload(inputStream, "path/to", "file.txt");

// 上传私有文件(File)
String key = awsS3UploadService.upload(file, "path/to", "file.txt");

// 上传公开可读文件
String key = awsS3UploadService.upload(inputStream, "path/to", "file.txt", true);

// 上传到指定桶
String key = awsS3UploadService.upload("other-bucket", inputStream, "path/to", "file.txt");

获取文件URL

// 获取默认有效期(1小时)的URL
String url = awsS3UploadService.getUrl(key);

// 获取指定有效期的URL
String url = awsS3UploadService.getUrl(key, Duration.ofDays(7));

// 获取指定桶和有效期的URL
String url = awsS3UploadService.getUrl("other-bucket", key, Duration.ofHours(2));

下载文件

// 下载到本地路径
awsS3UploadService.download(key, "/local/path/file.txt");

// 下载到输出流
awsS3UploadService.download(key, outputStream);

删除文件

awsS3UploadService.delete(key);

获取文件元数据

Map<String, Object> headers = awsS3UploadService.getFileHeaders(key);
String contentType = awsS3UploadService.getContentType(key);
String contentLength = awsS3UploadService.getContentLength(key);
String etag = awsS3UploadService.getEtag(key);

便捷方法

// 上传文件并获取URL
String url = awsS3UploadService.uploadFile(file, "path/to", "file.txt");
String url = awsS3UploadService.uploadFile(file, "path/to", "file.txt", Duration.ofDays(7));

// 上传到临时目录并获取URL
String url = awsS3UploadService.uploadTempFile(file, "file.txt");

// 上传公开可读文件并获取URL
String url = awsS3UploadService.uploadCanReadFile(file, "path/to", "file.txt");

S3-V2

仓库坐标

Maven

<dependency>
    <groupId>com.luamas.thinkbridge</groupId>
    <artifactId>aws-s3-v2-spring-boot-starter</artifactId>
</dependency>

Gradle

implementation("com.luamas.thinkbridge:aws-s3-v2-spring-boot-starter")

配置

think:
  aws:
    # aws 的授权配置
    enable: true
    access-key-id: 'TODO key'
    secret-access-key-id: 'TODO'
    s3:
      # 是否启用
      enable: true
      # aws 地址
      service-endpoint: http://10.96.112.200:8080
      # 如果非外网的私有云直接使用以下地址即可,如果私有云使用反向代理,配置代理地址即可
      endpoint-url: http://10.96.112.200:8080
      # 区域,如果使用私有云的 s3 随意填写
      region: us-east-1
      bucket-name: '桶名称'
      # 是否为 aws,私有云设为 false
      is-aws: false
      # 启动时是否检查桶是否存在
      check-bucket-name-exist: true
      # 是否为路径样式(true=路径风格,false=虚拟主机风格),私有云通常设为 true
      path-style-access-enabled: true
      # 是否启用分块编码,默认 false
      chunked-encoding-enabled: false
      # 是否启用加速模式,默认 false
      accelerate-mode-enabled: false
      # 是否启用双栈模式,默认 false
      dualstack-enabled: false
      # 是否使用 ARN 解析区域,默认 false
      use-arn-region-enabled: false
      # 是否启用多区域,默认 false
      multi-region-enabled: false

自动配置

自动配置类为 AwsS3V2AutoConfiguration,当 think.aws.s3.enable=true(默认为true)时自动生效。

注册的Bean

Bean名称 类型 说明

s3Presigner

S3Presigner

S3预签名客户端,用于生成预签名URL,依赖 AwsCredentialsProvider

s3Client

S3Client

AWS S3 v2 同步客户端,依赖 AwsCredentialsProvider

s3AsyncClient

S3AsyncClient

AWS S3 v2 异步客户端,依赖 AwsCredentialsProvider

awsS3V2UploadService

UploadService

同步上传服务,依赖 S3Client 和 S3Presigner

awsS3V2AsyncUploadService

AsyncUploadService

异步上传服务,依赖 S3AsyncClient 和 S3Presigner

使用

同步上传服务

注入同步上传服务
@Autowired
private UploadService awsS3V2UploadService;
上传文件
// 上传私有文件(InputStream)
String key = awsS3V2UploadService.upload(inputStream, "path/to", "file.txt");

// 上传私有文件(File)
String key = awsS3V2UploadService.upload(file, "path/to", "file.txt");

// 上传公开可读文件
String key = awsS3V2UploadService.upload(inputStream, "path/to", "file.txt", true);

// 上传到指定桶
String key = awsS3V2UploadService.upload("other-bucket", inputStream, "path/to", "file.txt");
获取文件URL
// 获取默认有效期(1小时)的URL
String url = awsS3V2UploadService.getUrl(key);

// 获取指定有效期的URL
String url = awsS3V2UploadService.getUrl(key, Duration.ofDays(7));

// 获取指定桶和有效期的URL
String url = awsS3V2UploadService.getUrl("other-bucket", key, Duration.ofHours(2));
下载文件
// 下载到本地路径
awsS3V2UploadService.download(key, "/local/path/file.txt");

// 下载到输出流
awsS3V2UploadService.download(key, outputStream);
删除文件
awsS3V2UploadService.delete(key);
获取文件元数据
Map<String, Object> headers = awsS3V2UploadService.getFileHeaders(key);
String contentType = awsS3V2UploadService.getContentType(key);
String contentLength = awsS3V2UploadService.getContentLength(key);
String etag = awsS3V2UploadService.getEtag(key);

异步上传服务

注入异步上传服务
@Autowired
private AsyncUploadService awsS3V2AsyncUploadService;
异步上传文件
// 异步上传私有文件(InputStream)
CompletableFuture<String> future = awsS3V2AsyncUploadService.upload(inputStream, "path/to", "file.txt");

// 异步上传私有文件(File)
CompletableFuture<String> future = awsS3V2AsyncUploadService.upload(file, "path/to", "file.txt");

// 异步上传公开可读文件
CompletableFuture<String> future = awsS3V2AsyncUploadService.upload(inputStream, "path/to", "file.txt", true);

// 异步上传到指定桶
CompletableFuture<String> future = awsS3V2AsyncUploadService.upload("other-bucket", inputStream, "path/to", "file.txt");
异步删除文件
CompletableFuture<Void> future = awsS3V2AsyncUploadService.delete(key);
异步下载文件
// 异步下载到本地路径
CompletableFuture<Void> future = awsS3V2AsyncUploadService.download(key, "/local/path/file.txt");

// 异步下载到输出流
CompletableFuture<Void> future = awsS3V2AsyncUploadService.download(key, outputStream);
异步获取文件元数据
CompletableFuture<Map<String, Object>> headersFuture = awsS3V2AsyncUploadService.getFileHeaders(key);
CompletableFuture<String> contentTypeFuture = awsS3V2AsyncUploadService.getContentType(key);
CompletableFuture<String> contentLengthFuture = awsS3V2AsyncUploadService.getContentLength(key);
CompletableFuture<String> etagFuture = awsS3V2AsyncUploadService.getEtag(key);

直接使用底层客户端

如果 UploadService / AsyncUploadService 提供的API不满足需求,可以直接注入底层客户端进行自定义操作:

// 注入同步客户端
@Autowired
private S3Client s3Client;

// 注入异步客户端
@Autowired
private S3AsyncClient s3AsyncClient;

// 注入预签名客户端
@Autowired
private S3Presigner s3Presigner;

// 也可以从UploadService获取
@Autowired
private UploadService awsS3V2UploadService;

S3Client s3Client = ((AwsS3V2UploadService) awsS3V2UploadService).getS3Client();

// 从AsyncUploadService获取
@Autowired
private AsyncUploadService awsS3V2AsyncUploadService;

S3AsyncClient s3AsyncClient = ((AwsS3V2AsyncUploadService) awsS3V2AsyncUploadService).getS3AsyncClient();

与S3 v1的区别

特性 S3 (v1) S3-V2

SDK版本

AWS SDK v1 (com.amazonaws)

AWS SDK v2 (software.amazon.awssdk)

同步客户端

AmazonS3

S3Client

异步客户端

不支持

S3AsyncClient

预签名

内置于 AmazonS3

独立的 S3Presigner

分块编码配置

chunked-encoding-disabled

chunked-encoding-enabled

有效负载签名

支持 (payload-signing-enabled)

不支持

全局桶访问

支持 (force-global-bucket-access-enabled)

不支持

us-east-1端点

支持 (regional-us-east-1-endpoint-enabled)

不支持

多区域

不支持

支持 (multi-region-enabled)