Comment vérifier si une clé spécifiée existe dans un compartiment S3 donné à l'aide de Java


87

Je voudrais vérifier si une clé existe dans un seau donné en utilisant Java. J'ai regardé l'API mais aucune méthode n'est utile. J'ai essayé d'utiliser getObjectmais cela a jeté une exception.


2
À l'avenir, veuillez fournir plus d'informations, par exemple quelle était l'exception que vous avez reçue .. J'ai fourni une réponse basée sur une hypothèse ..
sethu

4
FYI: Pour cette question, la réponse acceptée n'est pas la meilleure réponse.
malana le

Réponses:


3

Utilisez la bibliothèque jets3t. C'est beaucoup plus facile et robuste que le sdk AWS. En utilisant cette bibliothèque, vous pouvez appeler s3service.getObjectDetails (). Cela vérifiera et ne récupérera que les détails de l'objet (pas le contenu) de l'objet. Il lancera un 404 si l'objet est manquant. Vous pouvez donc intercepter cette exception et la gérer dans votre application.

Mais pour que cela fonctionne, vous devez disposer d'un accès ListBucket pour l'utilisateur sur ce compartiment. L'accès à GetObject ne fonctionnera pas. La raison en est qu'Amazon vous empêchera de vérifier la présence de la clé si vous n'avez pas accès à ListBucket. Le simple fait de savoir si une clé est présente ou non suffira également aux utilisateurs malveillants dans certains cas. Par conséquent, à moins d'avoir accès à ListBucket, ils ne pourront pas le faire.


4
Tous - voir une réponse mise à jour à cette question ci-dessous: stackoverflow.com/a/36653034/49678
alexandroid

3
jets3t est une ancienne bibliothèque obsolète. Utilisez plutôt le fichier aws-java-sdk.
the_storyteller

"plus facile et plus robuste" est très subjectif
Leo Romanovsky

291

Il existe maintenant une méthode doesObjectExist dans l'API Java officielle.

Prendre plaisir!


13
Il a été ajouté en 1.10.51
steamer25

4
Nous devons voter pour cela et porter cela au sommet!
SureshS

2
La bonne chose à faire serait d'en faire la réponse acceptée, mais seul le PO peut le faire. meta.stackexchange.com/questions/120568/…
malana

4
Cela doit faire un appel réseau, ce qui est cher si vous avez beaucoup d'objets ... Dommage qu'il ne puisse pas simplement retourner null sur la demande de métadonnées.
Joel le

9
On dirait qu'Amazon a été supprimé doesObjectExistdu SDK 2.x (actuellement v2.3.9).
Bampfer

59

Mise à jour:

Il semble qu'il existe une nouvelle API pour vérifier cela. Voir une autre réponse dans cette page: https://stackoverflow.com/a/36653034/435605

Message d'origine:

Utilisation 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);
}

Remarque sur l'exception: je sais que les exceptions ne doivent pas être utilisées pour le contrôle de flux. Le problème est qu'Amazon n'a fourni aucune API pour vérifier ce flux - juste de la documentation sur l'exception.


14
N'utilisez pas la gestion des exceptions pour le contrôle du programme.
Simon Peck

34
@SimonPeck: vous avez raison. Le problème est qu'Amazon n'a fourni aucune API pour vérifier ce flux - juste de la documentation sur l'exception. Veuillez supprimer votre vote négatif si ce n'est pas le cas.
AlikElzin-kilaka

1
Cela ne semble plus être le cas pour le SDK Java. Je vois que my errorMessageest défini sur "Not Found", mais le errorCodeest nul.
bstempi

3
J'irais chercher le code de statut 404. Semble plus robuste que de regarder une chaîne
Oskar Kjellin

2
Le commentaire de @rboarman est incorrect - il l'est NoSuchKey. Pour une liste définitive des codes d'erreur S3, consultez la documentation: docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
Allen George

22

À l'aide du kit SDK AWS, utilisez la méthode getObjectMetadata. La méthode lèvera une AmazonServiceException si la clé n'existe pas.

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 lance également AmazonServiceException, alors pourquoi faire deux appels? Aussi, comment savoir que l'objet n'existe pas à partir de cette excpetion? Peut-être était-ce à cause d'une autre erreur S3 et l'objet est bien trouvé.
AlikElzin-kilaka

5
N'utilisez pas la gestion des exceptions pour le contrôle du programme.
Simon Peck

4
@ AlikElzin-kilaka, car getObject () signifie que vous devez télécharger le contenu de l'objet, qui peut potentiellement être énorme.
Jason Nichols

18
@SimonPeck, ce n'est pas idéal, mais lorsqu'Amazon propose une méthode exist () appropriée, votre argument est valide.
Jason Nichols

4
@SimonPeck avez-vous une alternative dans ce cas? Ce n'est pas un abus flagrant des exceptions en tant que flux de contrôle du programme ... c'est simple, précis dans ce qu'il fait et sûr. Si vous prenez votre idée à l'extrême (comme apparemment vous l'êtes si vous pensez que cet extrait de code abuse des exceptions), alors pourquoi avoir des exceptions dans un langage? Plutôt que de lancer une exception pour alerter le programme et modifier le déroulement du programme , le runtime devrait simplement se terminer, je suppose.
Don Cheadle

17

Dans Amazon Java SDK 1.10+, vous pouvez utiliser getStatusCode()pour obtenir le code d'état de la réponse HTTP, qui sera 404 si l'objet n'existe pas.

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()consomme moins de ressources et la réponse n'a pas besoin d'être fermée comme getObject().


Dans les versions précédentes, vous pouvez utiliser getErrorCode()et vérifier la chaîne appropriée (dépend de la version).


Si votre objet s3 n'a pas de métadonnées attachées, getObjectMetadata lèvera une erreur 404 même si l'objet s3 existe. Je ne recommanderai pas cela si l'objectif est de vérifier l'existence de l'objet s3.
Ashish Goel

@AshishGoel, il y aura toujours des métadonnées, si l'objet existe. En fait, la requête HTTP sous-jacente est simplement un HEAD vers l'URL de l'objet.
Paul Draper

5

Utilisez ListObjectsRequest setting Prefix comme clé.

Code .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
ATTENTION! Amazon facture un supplément pour chaque appel LIST! Cette méthode est correcte, mais ne l'utilisez pas pour vérifier si le fichier existe avant de le télécharger.
user34402

Ce n'est pas un bon moyen de savoir si un fichier existe car il obtient tous les objets correspondant au préfixe. Si vous avez plusieurs fichiers commençant par la clé, il téléchargera tous les objets, y compris celui que vous avez spécifié.
Crypth

Concernant le coût de LIST vs GET: notez que vous êtes également facturé pour toutes les données transférées. Donc, s'il est extrêmement improbable que le fichier existe (par exemple, vous avez généré un UUID aléatoire en tant que clé et voulez vous assurer qu'il n'est pas déjà utilisé) alors GET est beaucoup moins cher. Mais si les fichiers font 0,5 Mo et ont 11% de chances d'exister déjà, alors LIST semble un peu moins cher. Idem si les fichiers font 0,1 Mo et ont 52% de chances d'exister ... Plus les fichiers sont volumineux, plus tôt la LISTE devient moins chère. Mais encore une fois, un scénario courant teste une clé UUID nouvellement générée, et GET est moins cher pour cela.
Bampfer

5

Pour PHP (je sais que la question est Java, mais Google m'a amené ici), vous pouvez utiliser des wrappers de flux et file_exists

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

4

Ce code java vérifie si la clé (fichier) existe dans le compartiment 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
Cela devrait fonctionner, mais devrait également être lent dans les cas où il y a des milliers ou des fichiers, et pour chaque boucle de fichier serait nécessaire.
Danijel

comme l'a dit @Danijel, cela déterminera en effet si un objet d'une clé donnée existe ou non, mais pour ce faire, il doit boucler potentiellement des dizaines de milliers d'objets dans S3 avant de déterminer s'il existe ou non
Don Cheadle

1
Je ne suis pas d'accord avec @Danijel et mmcrae sur le fait que cela soit lent. La requête listObjects spécifie .withPrefix (fichier) donc elle doit renvoyer au maximum le seul fichier correspondant, sauf s'il existe d'autres fichiers dont le nom commence par le nom du fichier cible.
davidwebster48

3

Divisez votre chemin en seau et objet. Test du bucket à l'aide de la méthode doesBucketExist, Test de l'objet à l'aide de la taille de la liste (0 en cas d'existence) Donc, ce code fera:

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

Facile et simple. Merci
Thermech

3

Utiliser Object isting. Fonction Java pour vérifier si la clé spécifiée existe dans AWS S3.

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

Il existe un moyen simple de le faire en utilisant la méthode isObjectInBucket () de l'API jetS3t.

Exemple de code:

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

        }

Il fait le même appel get-metadata sous le capot + capture d'exception: grepcode.com/file/repo1.maven.org/maven2/net.java.dev.jets3t/…
alexandroid

1

Les autres réponses concernent AWS SDK v1. Voici une méthode pour AWS SDK v2 (actuellement 2.3.9).

Notez que les méthodes getObjectMetadataet doesObjectExistne sont pas actuellement dans le SDK v2! Ce ne sont donc plus des options. Nous sommes obligés d'utiliser soit getObjectou listObjects.

listObjectsles appels sont actuellement 12,5 fois plus chers à faire que getObject. Mais AWS facture également toutes les données téléchargées, ce qui augmente le prix de getObject l'existence du fichier . Tant qu'il est très peu probable que le fichier existe (par exemple, vous avez généré une nouvelle clé UUID de manière aléatoire et devez simplement vérifier qu'elle n'est pas prise), puis appelezgetObject est nettement moins cher selon mon calcul.

Par souci de sécurité, j'ai ajouté une range()spécification demandant à AWS de n'envoyer que quelques octets du fichier. Autant que je sache, le SDK respectera toujours cela et ne vous facturera pas pour le téléchargement du fichier entier. Mais je n'ai pas vérifié cela, alors comptez sur ce comportement à vos risques et périls! (De plus, je ne sais pas comment rangese comporte si l'objet S3 a une longueur de 0 octet.)

    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;
    }
}

Remarque: ce code suppose s3Clientet logest déclaré et initialisé ailleurs. La méthode renvoie un booléen, mais peut lever des exceptions.


On dirait que maintenant il y a un s3Client.headObject()dans V2 pour faire ceci: stackoverflow.com/a/56949742/9814131 , et vous vérifierez le S3Exceptioncode de statut 404 pour vérifier si l'objet existe selon le problème github github.com/aws/aws-sdk- java-v2 / issues / 297 . Mais je suppose que le vôtre est plus progressif car il a aussi très peu de frais généraux que 0-3 octets.
Shaung Cheng le


1

J'ai également rencontré ce problème lorsque j'utilisais

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

J'ai la clé d'erreur introuvable

Quand je frappe et essaie

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

cela a fonctionné, ce code fonctionne avec 1.9 jar sinon mettez à jour à 1.11 et utilisez doObjectExist comme indiqué ci-dessus


1

Comme d'autres l'ont mentionné, pour AWS S3 Java SDK 2.10+, vous pouvez utiliser l' objet HeadObjectRequest pour vérifier s'il existe un fichier dans votre compartiment S3. Cela agira comme une requête GET sans obtenir le fichier.

Exemple de code puisque les autres n'ont en fait ajouté aucun code ci-dessus:

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;
   }
}

jette NoSuchKeyException
Andrii Karaivanskyi

C'est parce que la clé n'existe pas. C'est exactement ce que vous recherchez. Alors gérez cette exception et retournez false pour elle. J'ai mis à jour le code ci-dessus pour inclure le try / catch.
Navigatron

Alors vous n'en avez pas du tout besoin headObjectResponse. throws Exceptionn'est pas nécessaire non plus.
Andrii Karaivanskyi

@AndriiKaraivanskyi c'est juste un exemple, je ne l'ai pas testé.
Navigatron

headObjectResponse.sdkHttpResponse () .isSuccessful (); est toujours réussi, que le fichier existe ou non?
marquer

0

Vous pouvez également utiliser la bibliothèque cliente Minio-Java , son Open Source et compatible avec l'API AWS S3.

Vous pouvez utiliser des exemples Minio-Java StatObject.java pour le même.

import io.minio.MinioClient;
import io.minio.errors.MinioException;

import java.io.InputStream;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;

import org.xmlpull.v1.XmlPullParserException;


classe publique GetObject {
  public static void main (String [] args)
    jette NoSuchAlgorithmException, IOException, InvalidKeyException, XmlPullParserException, MinioException {
    // Remarque: YOUR-ACCESSKEYID, YOUR-SECRETACCESSKEY et my-bucketname sont
    // valeurs factices, veuillez les remplacer par les valeurs d'origine.
    // Définit le point de terminaison s3, la région est calculée automatiquement
    MinioClient s3Client = new MinioClient ("https://s3.amazonaws.com", "YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY");
    InputStream stream = s3Client.getObject ("my-bucketname", "my-objectname");

    byte [] buf = nouvel octet [16384];
    int bytesRead;
    while ((bytesRead = stream.read (buf, 0, buf.length))> = 0) {
      System.out.println (nouvelle chaîne (buf, 0, bytesRead));
    }

    stream.close ();
  }
}

J'espère que cela aide.

Clause de non-responsabilité: je travaille pour Minio

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.