From 1cc4375cbc728722c7a4f75d20d7c74229c90c95 Mon Sep 17 00:00:00 2001 From: chrislu Date: Mon, 17 Nov 2025 12:55:22 -0800 Subject: [PATCH] Update s3api_object_handlers_list.go --- weed/s3api/s3api_object_handlers_list.go | 114 +++++++++++------------ 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/weed/s3api/s3api_object_handlers_list.go b/weed/s3api/s3api_object_handlers_list.go index c2c4455d6..ece1194c0 100644 --- a/weed/s3api/s3api_object_handlers_list.go +++ b/weed/s3api/s3api_object_handlers_list.go @@ -205,47 +205,47 @@ func (s3a *S3ApiServer) listFilerEntries(bucket string, originalPrefix string, m for { empty := true - nextMarker, doErr = s3a.doListFilerEntries(client, reqDir, prefix, cursor, marker, delimiter, false, func(dir string, entry *filer_pb.Entry) { - empty = false - dirName, entryName, _ := entryUrlEncode(dir, entry.Name, encodingTypeUrl) - if entry.IsDirectory { + nextMarker, doErr = s3a.doListFilerEntries(client, reqDir, prefix, cursor, marker, delimiter, false, func(dir string, entry *filer_pb.Entry) { + empty = false + dirName, entryName, _ := entryUrlEncode(dir, entry.Name, encodingTypeUrl) + if entry.IsDirectory { // When delimiter is specified, apply delimiter logic to directory key objects too - if delimiter != "" && entry.IsDirectoryKeyObject() { - // Apply the same delimiter logic as for regular files - var delimiterFound bool - // Use raw dir and entry.Name (not encoded) to ensure consistent handling - // Encoding will be applied after sorting if encodingTypeUrl is set - undelimitedPath := fmt.Sprintf("%s/%s/", dir, entry.Name)[len(bucketPrefix):] + if delimiter != "" && entry.IsDirectoryKeyObject() { + // Apply the same delimiter logic as for regular files + var delimiterFound bool + // Use raw dir and entry.Name (not encoded) to ensure consistent handling + // Encoding will be applied after sorting if encodingTypeUrl is set + undelimitedPath := fmt.Sprintf("%s/%s/", dir, entry.Name)[len(bucketPrefix):] - // take into account a prefix if supplied while delimiting. - undelimitedPath = strings.TrimPrefix(undelimitedPath, originalPrefix) + // take into account a prefix if supplied while delimiting. + undelimitedPath = strings.TrimPrefix(undelimitedPath, originalPrefix) - delimitedPath := strings.SplitN(undelimitedPath, delimiter, 2) + delimitedPath := strings.SplitN(undelimitedPath, delimiter, 2) - if len(delimitedPath) == 2 { - // S3 clients expect the delimited prefix to contain the delimiter and prefix. - delimitedPrefix := originalPrefix + delimitedPath[0] + delimiter + if len(delimitedPath) == 2 { + // S3 clients expect the delimited prefix to contain the delimiter and prefix. + delimitedPrefix := originalPrefix + delimitedPath[0] + delimiter - for i := range commonPrefixes { - if commonPrefixes[i].Prefix == delimitedPrefix { - delimiterFound = true - break + for i := range commonPrefixes { + if commonPrefixes[i].Prefix == delimitedPrefix { + delimiterFound = true + break + } } - } - if !delimiterFound { - commonPrefixes = append(commonPrefixes, PrefixEntry{ - Prefix: delimitedPrefix, - }) - cursor.maxKeys-- - delimiterFound = true - lastEntryWasCommonPrefix = true - lastCommonPrefixName = delimitedPath[0] - } else { - // This directory object belongs to an existing CommonPrefix, skip it - delimiterFound = true + if !delimiterFound { + commonPrefixes = append(commonPrefixes, PrefixEntry{ + Prefix: delimitedPrefix, + }) + cursor.maxKeys-- + delimiterFound = true + lastEntryWasCommonPrefix = true + lastCommonPrefixName = delimitedPath[0] + } else { + // This directory object belongs to an existing CommonPrefix, skip it + delimiterFound = true + } } - } // If no delimiter found in the directory object name, treat it as a regular key if !delimiterFound { @@ -258,18 +258,18 @@ func (s3a *S3ApiServer) listFilerEntries(bucket string, originalPrefix string, m contents = append(contents, newListEntry(entry, "", dirName, entryName, bucketPrefix, fetchOwner, true, false, s3a.iam)) cursor.maxKeys-- lastEntryWasCommonPrefix = false - // https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html - } else if delimiter == "/" { // A response can contain CommonPrefixes only if you specify a delimiter. - // Use raw dir and entry.Name (not encoded) to ensure consistent handling - // Encoding will be applied after sorting if encodingTypeUrl is set - commonPrefixes = append(commonPrefixes, PrefixEntry{ - Prefix: fmt.Sprintf("%s/%s/", dir, entry.Name)[len(bucketPrefix):], - }) - //All of the keys (up to 1,000) rolled up into a common prefix count as a single return when calculating the number of returns. - cursor.maxKeys-- - lastEntryWasCommonPrefix = true - lastCommonPrefixName = entry.Name - } + // https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html + } else if delimiter == "/" { // A response can contain CommonPrefixes only if you specify a delimiter. + // Use raw dir and entry.Name (not encoded) to ensure consistent handling + // Encoding will be applied after sorting if encodingTypeUrl is set + commonPrefixes = append(commonPrefixes, PrefixEntry{ + Prefix: fmt.Sprintf("%s/%s/", dir, entry.Name)[len(bucketPrefix):], + }) + //All of the keys (up to 1,000) rolled up into a common prefix count as a single return when calculating the number of returns. + cursor.maxKeys-- + lastEntryWasCommonPrefix = true + lastCommonPrefixName = entry.Name + } } else { var delimiterFound bool if delimiter != "" { @@ -355,19 +355,19 @@ func (s3a *S3ApiServer) listFilerEntries(bucket string, originalPrefix string, m Contents: contents, CommonPrefixes: commonPrefixes, } - // Sort CommonPrefixes lexicographically to match AWS S3 behavior - // Sorting happens on decoded values for correct lexicographic order - sort.Slice(response.CommonPrefixes, func(i, j int) bool { return response.CommonPrefixes[i].Prefix < response.CommonPrefixes[j].Prefix }) - - // URL-encode CommonPrefixes AFTER sorting (if EncodingType=url) - // This ensures proper sort order (on decoded values) and correct encoding in response - if encodingTypeUrl { - response.EncodingType = s3.EncodingTypeUrl - for i := range response.CommonPrefixes { - response.CommonPrefixes[i].Prefix = urlPathEscape(response.CommonPrefixes[i].Prefix) + // Sort CommonPrefixes lexicographically to match AWS S3 behavior + // Sorting happens on decoded values for correct lexicographic order + sort.Slice(response.CommonPrefixes, func(i, j int) bool { return response.CommonPrefixes[i].Prefix < response.CommonPrefixes[j].Prefix }) + + // URL-encode CommonPrefixes AFTER sorting (if EncodingType=url) + // This ensures proper sort order (on decoded values) and correct encoding in response + if encodingTypeUrl { + response.EncodingType = s3.EncodingTypeUrl + for i := range response.CommonPrefixes { + response.CommonPrefixes[i].Prefix = urlPathEscape(response.CommonPrefixes[i].Prefix) + } } - } - return nil + return nil }) return