You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Java实现带文件夹校验的S3文件夹条件式对象写入?

刚好做过类似的S3批量处理需求,我来给你一个完整的Java实现方案,完全符合你的要求👇

Java实现S3按记录批量创建文件夹并写入对象

需求梳理

先明确下你的核心需求:

逐条遍历记录,以记录首字段作为S3文件夹名称,先检查该文件夹是否存在,不存在则创建;仅当目标文件夹内现有对象数量小于10时,将记录第二字段作为对象Key、第三字段作为对象Value写入对应S3文件夹。
示例记录:1,abc,def;2,xyz,mno;1,ghi,lks;2,dec,frt;3,uhy,erw,处理后生成S3文件夹1/、2/、3/,对应文件夹下存储abc.txt、ghi.txt等对象。

实现思路

首先得明确S3的一个核心特性:没有真正的物理文件夹,所谓的“文件夹”其实是对象Key的前缀(比如1/)。基于这个特性,我们的实现步骤是:

  • 遍历每条记录,拆分出文件夹前缀、对象名称、对象内容
  • 检查对应前缀的“文件夹”是否存在(通过判断是否有以/结尾的空对象),不存在则创建
  • 统计该前缀下的对象数量(排除文件夹本身的空对象),如果小于10则写入对象
  • 使用AWS SDK for Java 2.x(当前官方推荐版本)来完成所有S3操作

依赖准备

首先需要在你的项目中引入AWS S3的SDK依赖,以Maven为例:

<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>s3</artifactId>
    <version>2.20.100</version> <!-- 可以使用最新稳定版 -->
</dependency>

完整代码实现

import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;
import java.util.List;

public class S3RecordProcessor {
    // 替换成你的目标S3桶名
    private static final String BUCKET_NAME = "your-target-bucket";
    private static final S3Client s3Client;

    static {
        // 初始化S3客户端,使用默认凭证链(支持环境变量、凭证文件等)
        s3Client = S3Client.builder()
                .region(Region.US_EAST_1) // 替换成你的桶所在区域
                .credentialsProvider(DefaultCredentialsProvider.create())
                .build();
    }

    public static void main(String[] args) {
        // 示例记录列表,实际中可以从CSV文件、数据库或消息队列读取
        List<String> records = List.of(
                "1,abc,def",
                "2,xyz,mno",
                "1,ghi,lks",
                "2,dec,frt",
                "3,uhy,erw"
        );

        // 逐条处理记录
        for (String record : records) {
            processSingleRecord(record);
        }

        // 关闭客户端释放资源
        s3Client.close();
    }

    /**
     * 处理单条记录的核心逻辑
     */
    private static void processSingleRecord(String record) {
        // 拆分记录,确保格式正确
        String[] recordParts = record.split(",");
        if (recordParts.length != 3) {
            System.err.println("跳过无效记录: " + record);
            return;
        }

        String folderName = recordParts[0];
        String objectBaseKey = recordParts[1];
        String objectContent = recordParts[2];

        // 构造S3中的文件夹前缀(必须以/结尾,模拟文件夹)
        String folderPrefix = folderName + "/";
        // 最终对象Key:文件夹前缀 + 基础Key + .txt后缀
        String targetObjectKey = folderPrefix + objectBaseKey + ".txt";

        // 步骤1:确保文件夹(前缀)存在
        ensureFolderExists(folderPrefix);

        // 步骤2:统计该文件夹下的对象数量
        int currentObjectCount = getObjectCountInFolder(folderPrefix);
        if (currentObjectCount < 10) {
            // 步骤3:写入对象到S3
            putObjectToS3(targetObjectKey, objectContent);
            System.out.println("成功写入对象: " + targetObjectKey);
        } else {
            System.out.println("文件夹" + folderName + "下对象已达10个,跳过写入: " + targetObjectKey);
        }
    }

    /**
     * 确保S3中的文件夹(前缀)存在,不存在则创建空对象模拟文件夹
     */
    private static void ensureFolderExists(String folderPrefix) {
        try {
            // 检查是否存在文件夹对象(以/结尾的空对象)
            HeadObjectRequest headRequest = HeadObjectRequest.builder()
                    .bucket(BUCKET_NAME)
                    .key(folderPrefix)
                    .build();
            s3Client.headObject(headRequest);
        } catch (NoSuchKeyException e) {
            // 不存在则创建空对象
            PutObjectRequest putFolderRequest = PutObjectRequest.builder()
                    .bucket(BUCKET_NAME)
                    .key(folderPrefix)
                    .build();
            s3Client.putObject(putFolderRequest, software.amazon.awssdk.core.sync.RequestBody.empty());
            System.out.println("创建文件夹: " + folderPrefix);
        }
    }

    /**
     * 获取指定文件夹前缀下的对象数量(排除文件夹本身的空对象)
     * 处理S3分页返回的情况(单次最多返回1000个对象)
     */
    private static int getObjectCountInFolder(String folderPrefix) {
        ListObjectsV2Request listRequest = ListObjectsV2Request.builder()
                .bucket(BUCKET_NAME)
                .prefix(folderPrefix)
                .delimiter("/") // 只统计当前文件夹下的直接对象,不递归子文件夹
                .build();

        ListObjectsV2Response response = s3Client.listObjectsV2(listRequest);
        int count = 0;

        // 统计第一页的对象
        for (S3Object s3Object : response.contents()) {
            // 排除文件夹本身的空对象
            if (!s3Object.key().equals(folderPrefix)) {
                count++;
            }
        }

        // 处理分页,直到获取所有对象
        while (response.isTruncated()) {
            listRequest = ListObjectsV2Request.builder()
                    .bucket(BUCKET_NAME)
                    .prefix(folderPrefix)
                    .delimiter("/")
                    .continuationToken(response.nextContinuationToken())
                    .build();
            response = s3Client.listObjectsV2(listRequest);
            for (S3Object s3Object : response.contents()) {
                if (!s3Object.key().equals(folderPrefix)) {
                    count++;
                }
            }
        }

        return count;
    }

    /**
     * 将文本内容写入指定的S3对象
     */
    private static void putObjectToS3(String objectKey, String content) {
        PutObjectRequest putRequest = PutObjectRequest.builder()
                .bucket(BUCKET_NAME)
                .key(objectKey)
                .contentType("text/plain") // 设置内容类型为纯文本
                .build();
        s3Client.putObject(putRequest, software.amazon.awssdk.core.sync.RequestBody.fromString(content));
    }
}

关键细节说明

  • S3文件夹模拟:通过创建以/结尾的空对象来模拟文件夹,这样在S3控制台中会显示为文件夹结构,方便管理。
  • 对象数量统计:使用ListObjectsV2 API并设置delimiter="/",确保只统计当前文件夹下的直接对象,不会递归子文件夹。同时处理了分页逻辑,避免因为对象数量超过1000导致统计不全。
  • 凭证管理:使用默认凭证提供器,会自动从环境变量、AWS凭证文件(~/.aws/credentials)、IAM角色(如果在EC2/EKS等AWS服务中运行)获取凭证,无需硬编码。
  • 扩展性:你可以根据实际需求扩展错误处理(比如重试写入、记录详细日志)、记录来源(比如从CSV文件读取)等功能。

内容的提问来源于stack exchange,提问作者Akshat Choudhary

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 13:17:50