如何使用Java检查给定的S3存储桶中是否存在指定的密钥


86

我想使用Java检查给定存储桶中是否存在密钥。我看了看API,但没有任何有用的方法。我尝试使用,getObject但是引发了异常。


2
在未来,请提供更喜欢什么,你得到了异常的信息。我已经提供了基于一个假设的答案..
sethu

4
仅供参考:对于这个问题,公认的答案不是最佳答案。
马拉纳

Answers:


3

使用jets3t库。它比AWS sdk更轻松,更强大。使用此库,您可以调用s3service.getObjectDetails()。这将仅检查和检索对象的详细信息(而不是对象的内容)。如果缺少该对象,它将抛出404。因此,您可以捕获该异常并在您的应用程序中对其进行处理。

但是,要使其正常工作,您将需要对该存储桶上的用户具有ListBucket访问权限。仅GetObject访问将不起作用。原因是,如果您没有ListBucket访问权限,Amazon将阻止您检查密钥是否存在。在某些情况下,仅知道密钥是否存在也足以满足恶意用户的需要。因此,除非他们具有ListBucket访问权限,否则他们将无法访问。


4
全部-请在下面查看此问题的更新答案:stackoverflow.com/a/36653034/49678
alexandroid

3
jets3t是一个已弃用的旧库。而是使用aws-java-sdk。
–the_storyteller

“更简单,更可靠”是非常主观的
狮子座Romanovsky

290

官方Java API中现在有一个dosObjectExist方法。

请享用!


13
它是在1.10.51版中添加的-steamer25
2016年

4
我们必须对此表示赞同,并将其推向最高!
SureshS

2
正确的做法是使它成为可接受的答案,但只有OP才能做到这一点。meta.stackexchange.com/questions/120568/...
malana

4
这必须进行网络调用,如果您有很多对象,这将是昂贵的...太可惜了,它不能仅在元数据请求上返回null。
乔尔(Joel)

9
看起来亚马逊已从doesObjectExist2.x SDK(当前为v2.3.9)中删除。
Bampfer

59

更新:

似乎有一个新的API可以对此进行检查。在此页面中查看其他答案:https : //stackoverflow.com/a/36653034/435605

原始帖子:

使用 errorCode.equals("NoSuchKey")

try {
    AmazonS3 s3 = new AmazonS3Client(new ClasspathPropertiesFileCredentialsProvider());
    String bucketName = getBucketName();
    s3.createBucket(bucketName);
    S3Object object = s3.getObject(bucketName, getKey());
} catch (AmazonServiceException e) {
    String errorCode = e.getErrorCode();
    if (!errorCode.equals("NoSuchKey")) {
        throw e;
    }
    Logger.getLogger(getClass()).debug("No such key!!!", e);
}

关于异常的注意事项:我知道异常不应用于流控制。问题在于,亚马逊没有提供任何API来检查此流程-只是有关异常的文档。


14
不要将异常处理用于程序控制。
西蒙·派克

34
@SimonPeck:你是对的。问题是亚马逊没有提供任何API来检查此流程-只是有关异常的文档。如果您不赞成,请删除您的反对票。
AlikElzin-kilaka

1
Java SDK似乎不再是这样。我看到我errorMessage的设置为“未找到”,但errorCode为空。
bstempi 2014年

3
我会去寻找状态码404。似乎比看字符串更坚固
Oskar Kjellin 2014年

2
@rboarman的评论不正确-是NoSuchKey。有关S3错误代码的权威
艾伦·乔治

22

使用AWS开发工具包可使用getObjectMetadata方法。如果密钥不存在,则该方法将引发AmazonServiceException。

private AmazonS3 s3;
...
public boolean exists(String path, String name) {
    try {
        s3.getObjectMetadata(bucket, getS3Path(path) + name); 
    } catch(AmazonServiceException e) {
        return false;
    }
    return true;
}

2
getObject也将引发AmazonServiceException,那么为什么要进行两次调用?另外,我怎么知道该对象不存在?可能是由于另一个S3错误而确实找到了对象。
AlikElzin-kilaka

5
不要将异常处理用于程序控制。
西蒙·派克

4
@ AlikElzin-kilaka,因为getObject()意味着您必须下载对象的内容,这可能会很大。
杰森·尼科尔斯

18
@SimonPeck,这不是理想的方法,但是当Amazon提供适当的exist()方法时,您的观点就成立了。
杰森·尼科尔斯

4
@SimonPeck在这种情况下,您还有其他选择吗?这并不是公然滥用程序控制流……这是简单,准确,安全的。如果您将自己的想法发挥到极致(显然,如果您认为此代码段正在滥用异常),那么为什么在某种语言中根本没有异常?我认为运行时应该终止运行,而不是抛出异常来警告程序并更改程序流
Don Cheadle 2015年

16

在Amazon Java SDK 1.10+中,您可以getStatusCode()用来获取HTTP响应的状态代码,如果对象不存在,则为404。

import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.AmazonS3Exception;
import org.apache.http.HttpStatus;

try {
    AmazonS3 s3 = new AmazonS3Client();
    ObjectMetadata object = s3.getObjectMetadata("my-bucket", "my-client");
} catch (AmazonS3Exception e) {
    if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
        // bucket/key does not exist 
    } else {
        throw e;
    }
}

getObjectMetadata()消耗更少的资源,并且不需要像那样关闭响应getObject()


在以前的版本中,您可以使用getErrorCode()并检查适当的字符串(取决于版本)。


如果s3对象没有附加任何元数据,则即使s3对象存在,getObjectMetadata也会引发404错误。如果目标是检查s3对象的存在,我将不建议这样做。
Ashish Goel

@AshishGoel,如果对象存在,将始终有元数据。实际上,底层的HTTP请求只是对象URL的HEAD。
Paul Draper

5

使用ListObjectsRequest设置Prefix作为密钥。

.NET代码:

 public bool Exists(string key)
    {

        using (Amazon.S3.AmazonS3Client client = (Amazon.S3.AmazonS3Client)Amazon.AWSClientFactory.CreateAmazonS3Client(m_accessKey, m_accessSecret))
        {
            ListObjectsRequest request = new ListObjectsRequest();
            request.BucketName = m_bucketName;
            request.Prefix = key;
            using (ListObjectsResponse response = client.ListObjects(request))
            {

                foreach (S3Object o in response.S3Objects)
                {
                    if( o.Key == key )
                        return true;
                }
                return false;
            }
        }
    }.

7
警告!亚马逊对每个LIST通话收取额外费用!此方法可以,但是在下载文件之前,请勿使用它来检查文件是否存在。
2013年

这不是获取文件是否存在的好方法,因为它会获取所有与前缀匹配的对象。如果您有多个以密钥开头的文件,它将下载所有对象,包括您指定的对象。
Crypth

关于LIST与GET的费用:请注意,您还需要为传输出的任何数据付费。所以,如果这是非常不可能的文件存在(例如,您生成一个随机的UUID作为密钥,并且要确保它不是已在使用中),然后得到便宜得多。但是,如果文件大小为0.5 MB,并且已经存在11%的机会,那么LIST看起来会便宜一些。如果文件大小为0.1 MB,并且有52%的可能性存在,则相同...文件越大,列表越便宜。但是,再次常见的情况是测试新生成的UUID密钥,而GET这样做更便宜。
Bampfer

5

对于PHP(我知道问题是Java,但Google将我带到了这里),您可以使用流包装器和file_exists

$bucket = "MyBucket";
$key = "MyKey";
$s3 = Aws\S3\S3Client->factory([...]);
$s3->registerStreamWrapper();
$keyExists = file_exists("s3://$bucket/$key");

4

此Java代码检查s3存储桶中是否存在密钥(文件)。

public static boolean isExistS3(String accessKey, String secretKey, String bucketName, String file) {

    // Amazon-s3 credentials
    AWSCredentials myCredentials = new BasicAWSCredentials(accessKey, secretKey); 
    AmazonS3Client s3Client = new AmazonS3Client(myCredentials); 

    ObjectListing objects = s3Client.listObjects(new ListObjectsRequest().withBucketName(bucketName).withPrefix(file));

    for (S3ObjectSummary objectSummary: objects.getObjectSummaries()) {
        if (objectSummary.getKey().equals(file)) {
            return true;
        }
    }
    return false;
}

2
这应该可以工作,但是在有数千个文件的情况下也应该很慢,并且每个文件都需要循环。
Danijel 2014年

正如@Danijel所说,这确实将确定给定密钥的对象是否存在,但是这样做必须在确定它是否存在之前循环遍历S3中潜在的成千上万个对象
Don Cheadle

1
我不同意@Danijel和mmcrae的说法,它运行缓慢。listObjects请求指定.withPrefix(file),因此它最多应返回单个匹配文件,除非有其他文件的名称以目标文件的名称开头。
davidwebster48 2015年

3

闯入桶和物体。使用方法测试存储桶,使用doesBucketExist列表大小测试对象(如果不存在,则测试0)。因此,这段代码可以做到:

String bucket = ...;
String objectInBucket = ...;
AmazonS3 s3 = new AmazonS3Client(...);
return s3.doesBucketExist(bucket) 
       && !s3.listObjects(bucket, objectInBucket).getObjectSummaries().isEmpty();

简单容易。感谢
Thermech '16

3

使用对象浪费。检查AWS S3中是否存在指定密钥的Java函数。

boolean isExist(String key)
    {
        ObjectListing objects = amazonS3.listObjects(new ListObjectsRequest().withBucketName(bucketName).withPrefix(key));

        for (S3ObjectSummary objectSummary : objects.getObjectSummaries())
        {
            if (objectSummary.getKey().equals(key))
            {
                return true;
            }

        }
        return false;
    }

1

使用jetS3t API的isObjectInBucket()方法有一种简单的方法。

样例代码:

ProviderCredentials awsCredentials = new AWSCredentials(
                awsaccessKey,
                awsSecretAcessKey);

        // REST implementation of S3Service
        RestS3Service restService = new RestS3Service(awsCredentials);

        // check whether file exists in bucket
        if (restService.isObjectInBucket(bucket, objectKey)) {

            //your logic

        }

它在引擎盖+异常捕获下执行相同的get-metadata调用:grepcode.com/file/repo1.maven.org/maven2/net.java.dev.jets3t/…–
alexandroid

1

其他答案适用于AWS开发工具包v1。这是适用于AWS开发工具包v2(当前为2.3.9)的方法。

请注意,v2 SDK当前没有getObjectMetadatadoesObjectExist方法!因此,这些不再是选择。我们被迫使用getObjectlistObjects

listObjects目前,拨打电话的费用是的12.5倍getObject。但是AWS还会对下载的所有数据收取费用,这会提高getObject 文件是否存在的价格。只要文件不太可能存在(例如,您随机生成了一个新的UUID密钥,并且只需要仔细检查该文件是否被占用),getObject按我的计算,调用就便宜得多。

为了安全起见,我添加了一个range()规范,要求AWS仅发送文件的几个字节。据我所知,SDK始终会尊重这一点,不会向您收取下载整个文件的费用。但是我还没有证实,因此依靠这种行为需要您自担风险!(此外,我不确定rangeS3对象的长度为0字节时的行为。)

    private boolean sanityCheckNewS3Key(String bucket, String key) {

        ResponseInputStream<GetObjectResponse> resp = null;
        try {
            resp = s3client.getObject(GetObjectRequest.builder()
                .bucket(bucket)
                .key(key)
                .range("bytes=0-3")
                .build());
        }
        catch (NoSuchKeyException e) {
            return false;
        }
        catch (AwsServiceException se) {
            throw se;
        }
        finally {
            if (resp != null) {
                try {
                    resp.close();
                } catch (IOException e) {
                    log.warn("Exception while attempting to close S3 input stream", e);
                }
            }
        }
        return true;
    }
}

注意:此代码假定s3Clientlog在其他地方声明和初始化。方法返回一个布尔值,但可以引发异常。


好像现在s3Client.headObject()在V2中有一个要执行此操作:stackoverflow.com/a/56949742/9814131,您将S3Exception根据github问题github.com/aws/aws-sdk-检查状态代码404以检查对象是否存在。java-v2 / issues / 297。但是我想您会更进步,因为它的开销只有0-3个字节。
熊城


1

我使用时也遇到了这个问题

String BaseFolder = "3patti_Logs"; 
S3Object object = s3client.getObject(bucketName, BaseFolder);
 

我找不到错误码

当我尝试

String BaseFolder = "3patti_Logs"; 
S3Object object = s3client.getObject(bucketName, BaseFolder+"/");

它可以正常工作,此代码适用于1.9 jar,否则请更新为1.11并按上述方式使用dosObjectExist


1

就像其他人提到的那样,对于AWS S3 Java SDK 2.10+,您可以使用HeadObjectRequest对象检查S3存储桶中是否有文件。这将像一个GET请求一样,实际上没有获取文件。

示例代码,因为其他人实际上并未在上面添加任何代码:

public boolean existsOnS3 () throws Exception {
    try {
       S3Client s3Client = S3Client.builder ().credentialsProvider (...).build ();
       HeadObjectRequest headObjectRequest = HeadObjectRequest.builder ().bucket ("my-bucket").key ("key/to/file/house.pdf").build ();
       HeadObjectResponse headObjectResponse = s3Client.headObject (headObjectRequest);
       return headObjectResponse.sdkHttpResponse ().isSuccessful ();    
   }
   catch (NoSuchKeyException e) {
      //Log exception for debugging
      return false;
   }
}

引发NoSuchKeyException
Andrii Karaivanskyi

这是因为密钥不存在。这正是您想要的。因此,处理该异常并为其返回false。我已经更新了上面的代码以包含try / catch。
Navigatron

然后,您根本不需要headObjectResponsethrows Exception也不需要。
Andrii Karaivanskyi

@AndriiKaraivanskyi只是一个例子,我没有测试它。
Navigatron

headObjectResponse.sdkHttpResponse().isSuccessful(); 文件是否存在总是成功?

0

或者,您可以使用Minio-Java客户端库,该客户端库是开源的并且与AWS S3 API兼容。

您可以使用Minio -Java StatObject.java示例。

导入io.minio.MinioClient;
导入io.minio.errors.MinioException;

导入java.io.InputStream;
导入java.io.IOException;
导入java.security.NoSuchAlgorithmException;
导入java.security.InvalidKeyException;

导入org.xmlpull.v1.XmlPullParserException;


公共类GetObject {
  公共静态void main(String [] args)
    引发NoSuchAlgorithmException,IOException,InvalidKeyException,XmlPullParserException,MinioException {
    //注意:YOUR-ACCESSKEYID,YOUR-SECRETACCESSKEY和my-bucketname是
    //虚拟值,请用原始值替换它们。
    //设置s3端点,区域会自动计算
    MinioClient s3Client =新的MinioClient(“ https://s3.amazonaws.com”,“ YOUR-ACCESSKEYID”,“ YOUR-SECRETACCESSKEY”);
    InputStream流= s3Client.getObject(“ my-bucketname”,“ my-objectname”);

    byte [] buf =新的byte [16384];
    int bytesRead;
    而((bytesRead = stream.read(buf,0,buf.length))> = 0){
      System.out.println(new String(buf,0,bytesRead));
    }

    stream.close();
  }
}

希望对您有所帮助。

免责声明:我为Minio工作

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.