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

C# ASP.NET中获取AWS S3存储桶指定文件夹子文件夹列表问题

问题描述

初始问题

我有一个用来获取AWS S3存储桶指定文件夹下所有子文件夹列表的方法:

public static async Task<List<string>> GetAvailableDatasetsList(AmazonS3Client s3Client, string bucketName)
{
    try
    {
        ListObjectsV2Response response = await s3Client.ListObjectsV2Async(new ListObjectsV2Request
        {
            BucketName = bucketName,
            Prefix = "folderName1",
            StartAfter = "folderName1"
        });
        Console.WriteLine($"Size: {response.S3Objects.Count}");
        return response.S3Objects
            .Select(o => o.Key.Split('/')[1])
            .Distinct()
            .ToList();  
    }
    catch (AmazonS3Exception e)
    {
        Console.WriteLine("Error retrieving list from S3: " + e.Message);
        return null;
    }
    catch (Exception e)
    {
        Console.WriteLine("Error: " + e.Message);
        return null;
    }
}

我的S3存储桶结构如下:

bucketName
     - folderName1
          - subfolder1
               - *该文件夹下有超过1000个文件[图片]
          - subfolder2
               - *该文件夹下有超过1000个文件[图片]
          - subfolder3
               - *该文件夹下有超过1000个文件[图片]

我期望返回的列表是:

subFolder1
subFolder2
subFolder3

但当前方法只返回subFolder2,response.S3Objects.Count显示为1000,最终返回的列表长度仅为1,求修复方法。

更新后的问题

我通过分页实现了预期输出,但现在出现子文件夹被重复添加到列表的问题,更新后的方法如下:

public static async Task<List<string>> GetAvailableDatasetsList(AmazonS3Client s3Client, string bucketName)
{
    try
    {
        List<string> customUploads = new List<string>();
        string continuationToken = null;

        do
        {
            ListObjectsV2Response response = await s3Client.ListObjectsV2Async(new ListObjectsV2Request
            {
                BucketName = bucketName,
                Prefix = "custom-uploads",
                StartAfter = "custom-uploads",
                ContinuationToken = continuationToken
            });

            customUploads.AddRange(response.S3Objects
                .Select(o => o.Key.Split('/')[1])
                .Distinct());

            continuationToken = response.NextContinuationToken;
        }
        while (continuationToken != null);

        return customUploads;
    }
    catch (AmazonS3Exception e)
    {
        Console.WriteLine("Error retrieving list from S3: " + e.Message);
        return null;
    }
    catch (Exception e)
    {
        Console.WriteLine("Error: " + e.Message);
        return null;
    }
}

解决方案

初始问题根因

S3的ListObjectsV2接口默认单次最多返回1000条结果,你的每个子文件夹下都有超过1000个文件,单次调用只能拿到某一个子文件夹的全部文件,因此只能返回一个子文件夹名称。要获取所有子文件夹,必须分页遍历所有对象,或者用更高效的方式直接获取子目录。

优化方案一:用分隔符直接获取子文件夹(推荐)

利用S3的Delimiter参数,不需要遍历所有文件就能直接拿到指定目录下的直接子文件夹,性能大幅提升:

public static async Task<List<string>> GetAvailableDatasetsList(AmazonS3Client s3Client, string bucketName)
{
    try
    {
        List<string> subFolders = new List<string>();
        string continuationToken = null;

        do
        {
            var request = new ListObjectsV2Request
            {
                BucketName = bucketName,
                Prefix = "folderName1/", // 注意加末尾斜杠,确保只匹配目标目录下的直接子项
                Delimiter = "/",
                ContinuationToken = continuationToken
            };

            var response = await s3Client.ListObjectsV2Async(request);

            // 从CommonPrefixes提取子文件夹名称
            subFolders.AddRange(response.CommonPrefixes
                .Select(p => p.TrimEnd('/').Split('/').Last()));

            continuationToken = response.NextContinuationToken;
        }
        while (continuationToken != null);

        return subFolders.Distinct().ToList();
    }
    catch (AmazonS3Exception e)
    {
        Console.WriteLine("从S3获取列表出错: " + e.Message);
        return null;
    }
    catch (Exception e)
    {
        Console.WriteLine("出错: " + e.Message);
        return null;
    }
}

优化方案二:修复分页重复问题

如果坚持遍历所有文件,改用HashSet自动去重,避免分页时重复添加:

public static async Task<List<string>> GetAvailableDatasetsList(AmazonS3Client s3Client, string bucketName)
{
    try
    {
        HashSet<string> customUploads = new HashSet<string>(); // HashSet自动去重
        string continuationToken = null;

        do
        {
            ListObjectsV2Response response = await s3Client.ListObjectsV2Async(new ListObjectsV2Request
            {
                BucketName = bucketName,
                Prefix = "custom-uploads/", // 建议加末尾斜杠
                StartAfter = "custom-uploads/",
                ContinuationToken = continuationToken
            });

            foreach (var obj in response.S3Objects)
            {
                var parts = obj.Key.Split('/');
                if (parts.Length >= 2) // 防止Key格式异常
                {
                    customUploads.Add(parts[1]);
                }
            }

            continuationToken = response.NextContinuationToken;
        }
        while (continuationToken != null);

        return customUploads.ToList();
    }
    catch (AmazonS3Exception e)
    {
        Console.WriteLine("从S3获取列表出错: " + e.Message);
        return null;
    }
    catch (Exception e)
    {
        Console.WriteLine("出错: " + e.Message);
        return null;
    }
}

关键注意点

  1. Prefix末尾的斜杠:必须加上/(比如folderName1/),否则会匹配到类似folderName1abc这样的目录或文件,导致结果错误。
  2. Delimiter的作用:设置为/时,S3会返回所有以Prefix开头、下一个/之前的路径,直接拿到子文件夹,无需遍历海量文件。
  3. HashSet去重:相比每次分页局部去重再添加到列表,用HashSet可以在添加时自动去重,避免重复项。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 08:35:05