Java与AWS S3的文件操作

从零开始:Java与AWS S3的文件操作

  • [一、什么是 AWS S3?](#一、什么是 AWS S3?)
    • [AWS S3 的特点](#AWS S3 的特点)
    • [AWS S3 的应用场景](#AWS S3 的应用场景)
  • 二、Java整合S3方法
    • [使用 MinIO 客户端操作 S3](#使用 MinIO 客户端操作 S3)
    • [使用 AWS SDK 操作 S3 (推荐使用)](#使用 AWS SDK 操作 S3 (推荐使用))
  • 三、总结

一、什么是 AWS S3?

Amazon Simple Storage Service(简称 Amazon S3)是由亚马逊网络服务(AWS)提供的一种对象存储服务。它提供了一个高度可扩展、持久、安全且低成本的存储解决方案,用于存储和检索任意数量的数据。

AWS S3 的特点

  1. 高可用性和持久性:
  • AWS S3 的设计目标是为存储的数据提供 99.999999999%(11个9)的持久性,并确保 99.99% 的可用性。

  • 数据会自动在多个地理位置冗余存储,以保证数据的高持久性和可用性。

  1. 安全性:
  • AWS S3 提供了多种安全措施,包括传输中和存储时的数据加密、细粒度的访问控制策略、以及与 AWS Identity and Access Management (IAM) 集成的用户认证机制。
  1. 可扩展性:
  • AWS S3 可以自动扩展以处理从几字节到数十亿字节的数据。

  • 用户无需预先配置存储容量,且可以根据需要动态增加或减少存储容量。

  1. 成本效益:
  • AWS S3 提供按需付费的计费模式,根据实际使用量收费,没有预付费用或最低消费。

  • 用户可以选择不同的存储类别(如标准存储、智能分层存储、归档存储等),以优化存储成本。

  1. 简单易用:
  • AWS S3 提供了简单的 REST 和 SOAP API,开发人员可以方便地进行集成和操作。

  • AWS S3 管理控制台提供了直观的用户界面,用于管理存储桶和对象。

  1. 灵活的数据管理:
  • 支持版本控制、生命周期管理、事件通知和跨区域复制等高级功能,帮助用户更好地管理数据。
  1. 高效的上传和下载速度:
  • AWS S3 提供了高效的数据传输速度,支持大规模数据的快速上传和下载。

  • 上传速度 :在良好的网络条件下,单个文件的上传速度可以达到数百兆比特每秒 (Mbps)

  • 下载速度 :下载速度也可以达到数百兆比特每秒 (Mbps),尤其是在使用 Amazon CloudFront 进行内容分发时。

  • S3 Transfer Acceleration:通过启用 S3 Transfer Acceleration,可以进一步提升上传速度,全球范围内的传输速度可以提高 50-500% 以上,具体提升取决于用户的地理位置和网络条件。

AWS S3 的应用场景

  1. 静态网站托管:
  • AWS S3 可以用于托管静态网站,包括 HTML、CSS、JavaScript 文件等。
  1. 备份和恢复:
  • 作为企业级备份解决方案,AWS S3 可以用于存储备份数据,支持高持久性和快速恢复。
  1. 大数据分析:
  • 数据湖:AWS S3 可以作为数据湖,用于存储和分析大规模的结构化和非结构化数据。

  • 与 AWS 分析服务(如 Amazon Redshift、Amazon Athena)集成,进行大数据分析。

  1. 媒体存储和分发:
  • AWS S3 可以存储大量的媒体文件(如图片、视频、音频),并通过 Content Delivery Network (CDN) 进行全球分发。
  1. 日志存储:
  • 服务器和应用程序的日志可以存储在 AWS S3 中,以便于后续的分析和监控。
  1. 软件分发:
  • 企业可以使用 AWS S3 进行软件包和应用程序更新的分发。
  1. 容灾和跨区域复制:
  • 利用跨区域复制功能,企业可以在多个地理位置间复制数据,提高容灾能力。

二、Java整合S3方法

在 Java 中,可以通过两种主要方法与 S3 服务器进行交互:使用 MinIO 客户端 和使用 AWS SDK。两者都能与 S3 服务兼容,但它们在使用方式和配置上有所不同。

使用 MinIO 客户端操作 S3

MinIO 是一个兼容 S3 API 的开源对象存储解决方案。它提供了与 AWS S3 相同的接口,因此可以使用 MinIO 的 SDK 来操作 S3 存储服务。MinIO 客户端非常适合在本地部署的 S3 兼容存储(如 MinIO 自身)和 AWS S3 上进行文件操作。

优点:

  • 轻量级、开源,适合本地部署或私有云环境。

  • 与 AWS S3 完全兼容,支持所有 S3 操作(如上传、下载、删除文件等)。

  1. 添加依赖
xml 复制代码
        <!--   S3存储服务依赖 minio    -->
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>8.5.5</version>
        </dependency>
  1. 配置AWS S3相关的信息
java 复制代码
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;

/**
 * @CreateTime: 2024/05/31  18:17
 * @Description: S3配置文件
 * @Version: 1.0
 */
@Component
public class S3Config {

    @Value("${spring.s3_config.bucket_name}")
    private String bucketName;

    @Value("${spring.s3_config.endpoint}")
    private String s3Endpoint;

    @Value("${spring.s3_config.aws_access_key_id}")
    private String awsAccessKeyId;

    @Value("${spring.s3_config.aws_secret_access_key}")
    private String awsSecretAccessKey;

    private MinioClient minioClient;

    @PostConstruct
    public void init() {
        // 初始化 MinioClient,
        minioClient = MinioClient.builder()
                .endpoint(s3Endpoint)// s3服务器端点地址
                .credentials(awsAccessKeyId, awsSecretAccessKey)// 设置 访问凭证
                .build();
    }

    /**
     *  获取桶名
     */
    public String getBucketName() {
        return bucketName;
    }
    /**
     *  获取minio客户端
     */
    public MinioClient getMinioClient() {
        return minioClient;
    }
}
  1. 生成 S3 预览链接
java 复制代码
	/**
     * @description: 生成 S3 预览链接
     * @param realPath 生成 S3 预览链接的真实地址
     * @return 如果成功生成预览链接则返回链接字符串,否则返回 null
     */
    public static String generatePreviewUrl(String realPath) {
        try {
            // 创建额外的请求参数,这里设置响应内容类型
            Map<String, String> reqParams = new HashMap<>();
            String contentType = FileEnum.getByExtension(realPath.substring(realPath.lastIndexOf(".") + 1)).getContentType();
            reqParams.put("response-content-type", contentType);
            // 使用 MinIO 客户端生成预签名的对象 URL
            return minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(Method.GET) // 指定请求方法为 GET
                            .bucket(bucketName) // 指定存储桶名称
                            .object(realPath) // 指定对象名称
                            .extraQueryParams(reqParams) // 添加额外的查询参数(这里是响应内容类型)
                            .expiry(7, TimeUnit.DAYS) // 指定预签名 URL 的有效期
                            .build());
        } catch (Exception e) {
            // 捕获并打印任何异常
            log.error("生成预览链接失败: " + e);
            throw new RuntimeException("生成预览链接失败");
        }
    }
  1. 上传文件到S3
java 复制代码
 	/**
     * @description: 上传文件到S3
     * @param file 上传的文件
     * @return 如果成功上传返回 true,否则返回 false
     */
    public static String uploadFile(MultipartFile file) {
        try {
            // 获取上传的文件
            String fileName = file.getOriginalFilename();

            // 参数验证:确保文件名不为空
            if (fileName == null || fileName.isEmpty()) {
                throw new ServiceException("文件名无效");
            }
            String path = projectPath + fileName;
            // 使用 MinIO 客户端将文件上传到 S3 存储桶
            try (InputStream fileStream = file.getInputStream()) {
                minioClient.putObject(
                        PutObjectArgs.builder()
                                .bucket(bucketName) // 指定存储桶名称
                                .object(path) // 指定对象名称
                                .stream(fileStream, file.getSize(), -1) // 输入流和大小
                                .build()
                );
            }
            return path;
        } catch (Exception e) {
            // 捕获并打印 MinIO 异常信息
            log.error("上传文件失败: " + e);
            throw new RuntimeException("上传文件失败");
        }
    }
  1. 删除 S3 文件
java 复制代码
  	/**
     * @description: 删除 S3 文件
     * @param fileUrl 要删除的文件的 URL 或对象名称
     */
    public static void removeFile(String realPath) {
        try {
            // 使用 MinIO 客户端删除对象
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(bucketName) // 指定存储桶名称
                            .object(realPath) // 指定要删除的对象名称或 URL
                            .build());
        } catch (Exception e) {
        	log.error("文件删除失败: " + e);
            throw new RuntimeException("文件删除失败");
        }
    }
  1. 从s3获取文件流

当在大批量并发获取大文件流时,minio的方式可能会出现获取缓慢、卡死或中断的情况,推荐采用第二种方法AWS SDK的方式去获取文件流

java 复制代码
	/**
     * @description: 通过realPath获取文件流
     * @param: realPath
     * @return: InputStream 
     **/
    public static InputStream downloadByS3Url(String realPath) throws IOException {
        try {
            // 获取文件流
            InputStream fis= minioClient.getObject(GetObjectArgs.builder()
                                            .bucket(bucketName)
                                            .object(realPath)
                                            .build());
            return fis;
        } catch (Exception e) {
            log.error("获取文件流失败: " + e);
            throw new RuntimeException("获取文件流失败");
        }
    }
  1. 从s3下载文件
java 复制代码
	/**
     * 下载文件
     * @param filePath MinIO 中的文件路径
     * @param downloadPath 本地下载路径
     */
    public static void downloadFile(String filePath, String downloadPath) {
        try {
            // 使用 getObject 获取文件
            InputStream fileStream = minioClient.getObject(
                    GetObjectArgs.builder()
	                            .bucket(bucketName) // 指定存储桶名称
	                            .object(filePath)    // 指定文件路径
	                            .build());

            // 将文件内容写入到本地文件
            Path path = Paths.get(downloadPath);
            Files.copy(fileStream, path);

            System.out.println("文件已成功下载到: " + downloadPath);

        } catch (MinioException e) {
            log.error("下载文件失败: " + e);
            throw new RuntimeException("MinIO 下载文件失败");
        } catch (IOException e) {
            log.error("保存文件到本地时失败: " + e);
            throw new RuntimeException("保存文件到本地时失败");
        }
    }

使用 AWS SDK 操作 S3 (推荐使用)

AWS SDK 是 Amazon 提供的官方库,用于与 AWS 服务(包括 S3)进行交互。它不仅支持 AWS S3,还能支持其他 AWS 服务(如 EC2、DynamoDB 等)。如果你使用的是 AWS S3 或希望利用 AWS 的其它功能,AWS SDK 是推荐的选择。

优点:

  • 官方支持,功能全面,适用于使用 AWS 云服务的场景。

  • 可以访问 AWS 特有的功能和工具。

  1. 添加依赖
xml 复制代码
        <!--   S3存储服务依赖 AWS SDK    -->
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>s3</artifactId>
            <version>2.17.85</version>
        </dependency>
  1. 配置AWS S3相关的信息
java 复制代码
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;

import javax.annotation.PostConstruct;
import java.net.URI;
import java.time.Duration;

/**
 * @CreateTime: 2024/05/31  18:17
 * @Description: S3配置文件
 * @Version: 1.0
 */
@Component
public class S3Config {

    @Value("${spring.s3_config.bucket_name}")
    private String bucketName;

    @Value("${spring.s3_config.endpoint}")
    private String s3Endpoint;

    @Value("${spring.s3_config.aws_access_key_id}")
    private String awsAccessKeyId;

    @Value("${spring.s3_config.aws_secret_access_key}")
    private String awsSecretAccessKey;

    private S3Client s3Client;


    @PostConstruct
    public void init() {
        // 将凭证作为类级别的静态变量,避免每次调用都创建
        AwsBasicCredentials awsCredentials = AwsBasicCredentials.create(awsAccessKeyId, awsSecretAccessKey);

        s3Client = S3Client.builder()
                .region(Region.US_EAST_1)  // 选择正确的区域
                .endpointOverride(URI.create(s3Endpoint))// s3 服务器端点地址
                .credentialsProvider(StaticCredentialsProvider.create(awsCredentials))
                .overrideConfiguration(ClientOverrideConfiguration.builder()
                        .apiCallTimeout(Duration.ofMinutes(10))  // 设置整个请求的最大超时为 10 分钟
                        .apiCallAttemptTimeout(Duration.ofMinutes(9))  // 每次尝试的最大超时为 9 分钟
                        .build())
                .build();
    }



    /**
     *  获取桶名
     */
    public String getBucketName() {
        return bucketName;
    }
    /**
     *  获取亚马逊s3客户端
     */
    public S3Client getS3Client() {
        return s3Client;
    }
}
  1. 生成 S3 预览链接
java 复制代码
	/**
     * @description: 生成 S3 预览链接
     * @param realPath 生成 S3 预览链接的真实地址
     * @return 如果成功生成预览链接则返回链接字符串,否则返回 null
     */
    public static String generatePreviewUrl(String realPath) {
        try {
         // 创建 S3Presigner 实例
    		private static final S3Presigner presigner = S3Presigner.builder()
														            .region(Region.US_EAST_1) // 选择正确的区域
														            .credentialsProvider(StaticCredentialsProvider.create(awsCredentials))
														            .build();
             // 获取文件扩展名并根据扩展名获取内容类型
            String extension = realPath.substring(realPath.lastIndexOf(".") + 1);
            String contentType = FileEnum.getByExtension(extension).getContentType();

            // 构建 GetObjectRequest 对象
            GetObjectRequest getObjectRequest = GetObjectRequest.builder()
											                    .bucket(bucketName)
											                    .key(realPath)
											                    .responseContentType(contentType) // 设置响应内容类型
											                    .build();

            // 构建 GetObjectPresignRequest 对象
            GetObjectPresignRequest getObjectPresignRequest = GetObjectPresignRequest.builder()
																                    .signatureDuration(Duration.ofDays(7)) // 设置预签名 URL 的有效期
																                    .getObjectRequest(getObjectRequest)
																                    .build();

            // 生成预签名的 URL
            return presigner.presignGetObject(getObjectPresignRequest).url().toString();
        } catch (Exception e) {
            // 捕获并打印任何异常
            log.error("生成预览链接失败: " + e);
            throw new RuntimeException("生成预览链接失败");
        }
    }
  1. 上传文件到S3
java 复制代码
 	/**
     * @description: 上传文件到S3
     * @param file 上传的文件
     * @return 如果成功上传返回 true,否则返回 false
     */
    public static String uploadFile(MultipartFile file) {
        try {
            // 获取上传的文件
            String fileName = file.getOriginalFilename();

            // 参数验证:确保文件名不为空
            if (fileName == null || fileName.isEmpty()) {
                throw new ServiceException("文件名无效");
            }

            String path = projectPath + fileName;  // 在S3存储桶中存储的路径

            // 获取文件输入流
            try (InputStream fileStream = file.getInputStream()) {
                // 创建 PutObjectRequest
                PutObjectRequest putObjectRequest = PutObjectRequest.builder()
							                        .bucket(bucketName)
							                        .key(path)  // 设置 S3 存储对象的路径
							                        .build();

                // 上传文件到 S3
                PutObjectResponse putObjectResponse = s3Client.putObject(
                        putObjectRequest,
                        RequestBody.fromInputStream(fileStream, file.getSize())
                );

                // 返回上传文件的路径
                return path;
        } catch (Exception e) {
            log.error("上传文件失败: " + e);
            throw new RuntimeException("上传文件失败");
        }
    }
  1. 删除 S3 文件
java 复制代码
  	/**
     * @description: 删除 S3 文件
     * @param fileUrl 要删除的文件的 URL 或对象名称
     */
    public static void removeFile(String realPath) {
        try {
            // 创建删除文件的请求对象
            DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder()
													                    .bucket(bucketName)  // 存储桶名称
													                    .key(realPath)        // 文件路径
													                    .build();

            // 执行文件删除操作
            s3Client.deleteObject(deleteObjectRequest);
        } catch (Exception e) {
        	log.error("文件删除失败: " + e);
            throw new RuntimeException("文件删除失败");
        }
    }
  1. 从s3获取文件流
java 复制代码
/**
     * @description: 通过realPath获取文件流
     * @param: realPath
     * @return: InputStream 
     **/
    public static InputStream downloadByS3Url(String realPath) throws IOException {
         try {
            // 创建 GetObjectRequest 请求,指定存储桶名称和对象键
            GetObjectRequest getObjectRequest = GetObjectRequest.builder()
											                    .bucket(bucketName)        // S3 存储桶名称
											                    .key(realPath)            // S3 对象键
											                    .build();

            // 获取对象并返回文件的输入流
            InputStream fis = s3Client.getObject(getObjectRequest); // 获取文件输入流
            return fis;
        } catch (Exception e) {
            log.error("获取文件流失败: " + e);
            throw new RuntimeException("获取文件流失败");
        }
    }
  1. 从s3下载文件
java 复制代码
/**
     * @description: 从 S3 下载文件
     * @param filePath 要下载的文件路径
     * @param downloadPath 本地存储路径
     */
    public static void downloadFile(String filePath, String downloadPath) {
        try {
            // 创建 GetObjectRequest 请求
            GetObjectRequest getObjectRequest = GetObjectRequest.builder()
											                    .bucket(bucketName)  // 存储桶名称
											                    .key(filePath)        // S3 中的文件路径
											                    .build();

            // 执行下载操作并获取文件响应
            GetObjectResponse getObjectResponse = s3Client.getObject(getObjectRequest,
                    Paths.get(downloadPath));  // 下载到指定路径

            // 处理下载后的响应,如果需要可以做进一步处理
            System.out.println("文件已下载: " + getObjectResponse);

        } catch (S3Exception e) {
            // 捕获 AWS S3 异常并记录错误
            log.error("文件下载失败: " + e.awsErrorDetails().errorMessage());
            throw new RuntimeException("文件下载失败");
        } catch (Exception e) {
            // 捕获其他异常
            log.error("文件下载失败: " + e);
            throw new RuntimeException("文件下载失败");
        }
    }

三、总结

MinIO 客户端:

  • 适用于本地部署的 S3 兼容存储,或者想要避免依赖 AWS 提供的 SDK。通过 MinIO,你可以在 S3 和 MinIO 之间无缝迁移,或用于私有云环境。

AWS SDK:

  • 适用于直接操作 AWS S3 服务,提供更多 AWS 的特性和功能支持。如果你的应用已经在 AWS 云上运行,使用 AWS SDK 是更理想的选择。
相关推荐
人才程序员2 分钟前
详解QtPDF之 QPdfLink
开发语言·c++·qt·pdf·软件工程·界面·c语音
梦.清..7 分钟前
C语言——指针基础
c语言·开发语言
paopaokaka_luck13 分钟前
[384]基于springboot的药品管理系统
java·spring boot·后端
DreamByter19 分钟前
Day4:生信新手笔记 — R语言简单命令与Rstudio配置
开发语言·笔记·r语言
老马啸西风31 分钟前
Neo4j APOC-01-图数据库 apoc 插件介绍
java
名字不要太长 像我这样就好34 分钟前
【iOS】《Effective Objective-C 2.0》阅读笔记(一)
开发语言·笔记·学习·macos·ios·objective-c
次次皮44 分钟前
【方案三】JAVA中使用ocr(Umi-OCR)
java·ocr
疯一样的码农1 小时前
如何使用Apache HttpClient发送带有HTML表单数据的POST请求
java·apache·httpclient
Allen Bright1 小时前
使用 Apache Commons IO 实现文件读写
java·开发语言·apache
武子康1 小时前
Java-16 深入浅出 MyBatis - SqlSession Executor StatementHandler 源码分析
java·开发语言·mysql·mybatis·springboot