Como verificar se uma chave especificada existe em um determinado bucket S3 usando Java

87

Gostaria de verificar se existe uma chave em um determinado intervalo usando Java. Eu olhei para a API, mas não há métodos úteis. Tentei usar, getObjectmas gerou uma exceção.

em seus passos
fonte
2
No futuro, forneça mais informações, como qual foi a exceção que você obteve. Eu forneci uma resposta baseada em uma suposição ..
sethu
4
FYI: Para esta pergunta, a resposta aceita não é a melhor resposta.
malana

Respostas:

3

Use a biblioteca jets3t. É muito mais fácil e robusto do que o SDK da AWS. Usando esta biblioteca você pode chamar, s3service.getObjectDetails (). Isso verificará e recuperará apenas os detalhes do objeto (não o conteúdo) do objeto. Ele lançará um 404 se o objeto estiver faltando. Portanto, você pode capturar essa exceção e lidar com ela em seu aplicativo.

Mas para que isso funcione, você precisará ter acesso ListBucket para o usuário nesse intervalo. O acesso apenas GetObject não funcionará. O motivo é que a Amazon o impedirá de verificar a presença da chave se você não tiver acesso a ListBucket. Apenas saber se uma chave está presente ou não, também será suficiente para usuários mal-intencionados em alguns casos. Portanto, a menos que eles tenham acesso a ListBucket, eles não poderão fazer isso.

Sethu
fonte
4
Todos - veja uma resposta atualizada para esta pergunta abaixo: stackoverflow.com/a/36653034/49678
alexandroid
3
jets3t é uma biblioteca obsoleta. Em vez disso, use o aws-java-sdk.
the_storyteller
"mais fácil e mais robusto" é muito subjetivo
Leo Romanovsky
291

Agora existe um método doesObjectExist na API Java oficial.

Aproveitar!

Malana
fonte
13
Ele foi adicionado em 1.10.51
steamer25
4
Temos que votar a favor e levar isso ao topo!
SureshS
2
A coisa certa a fazer seria tornar essa resposta aceita, mas apenas o OP pode fazer isso. meta.stackexchange.com/questions/120568/…
malana
4
Isso deve fazer uma chamada de rede, o que é caro se você tiver muitos objetos ... Uma pena, ele não pode simplesmente retornar nulo na solicitação de metadados.
Joel
9
Parece que a Amazon foi removida doesObjectExistdo SDK 2.x (atualmente v2.3.9).
Bampfer
59

Atualizar:

Parece que há uma nova API para verificar exatamente isso. Veja outra resposta nesta página: https://stackoverflow.com/a/36653034/435605

Postagem original:

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

Observação sobre a exceção: eu sei que as exceções não devem ser usadas para controle de fluxo. O problema é que a Amazon não forneceu nenhuma API para verificar esse fluxo - apenas documentação sobre a exceção.

AlikElzin-kilaka
fonte
14
Não use tratamento de exceção para controle de programa.
Simon Peck de
34
@SimonPeck: você está certo. O problema é que a Amazon não forneceu nenhuma API para verificar esse fluxo - apenas documentação sobre a exceção. Remova seu voto negativo, se não for favorável.
AlikElzin-kilaka de
1
Isso não parece mais ser verdade para o Java SDK. Vejo que meu errorMessageestá definido como "Não encontrado", mas errorCodeé nulo.
bstempi
3
Eu procuraria o código de status 404. Parece mais robusto do que olhar para uma string
Oskar Kjellin
2
O comentário de @rboarman está incorreto - é NoSuchKey. Para obter uma lista definitiva dos códigos de erro S3, consulte a documentação: docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
Allen George
22

Usando o SDK da AWS, use o método getObjectMetadata. O método lançará uma AmazonServiceException se a chave não existir.

private AmazonS3 s3;
...
public boolean exists(String path, String name) {
    try {
        s3.getObjectMetadata(bucket, getS3Path(path) + name); 
    } catch(AmazonServiceException e) {
        return false;
    }
    return true;
}
usuário979051
fonte
2
getObject lança AmazonServiceException também, então por que duas chamadas? Além disso, como posso saber se o objeto não existe a partir desta exceção? Talvez tenha sido por causa de outro erro S3 e o objeto foi realmente encontrado.
AlikElzin-kilaka
5
Não use tratamento de exceção para controle de programa.
Simon Peck de
4
@ AlikElzin-kilaka, porque getObject () significa que você tem que baixar o conteúdo do objeto, que pode ser potencialmente enorme.
Jason Nichols
18
@SimonPeck, não é o ideal, mas quando a Amazon oferece um método exists () apropriado, então seu ponto é válido.
Jason Nichols
4
@SimonPeck você tem uma alternativa neste caso? Isso não é abuso flagrante de exceções como fluxo de controle do programa ... isso é simples, preciso no que faz e seguro. Se você levar sua ideia ao extremo (como aparentemente você está se você acha que este trecho de código está abusando de exceções), então por que ter exceções em uma linguagem? Em vez de lançar uma exceção para alertar o programa e alterar seu fluxo , o tempo de execução deve apenas terminar, suponho.
Don Cheadle
17

No Amazon Java SDK 1.10+, você pode usar getStatusCode()para obter o código de status da resposta HTTP, que será 404 se o objeto não existir.

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()consome menos recursos e a resposta não precisa ser fechada como getObject().


Nas versões anteriores, você pode usar getErrorCode()e verificar a string apropriada (depende da versão).

Paul Draper
fonte
Se o seu objeto s3 não tiver nenhum metadado anexado a ele, getObjectMetadata gerará um erro 404 mesmo se o objeto s3 existir. Não vou recomendar isso se o objetivo for verificar a existência do objeto s3.
Ashish Goel
@AshishGoel, sempre haverá metadados, se o objeto existir. Na verdade, a solicitação HTTP subjacente é simplesmente um HEAD para a URL do objeto.
Paul Draper de
5

Use o prefixo de configuração ListObjectsRequest como sua chave.

Código .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;
            }
        }
    }.
user34402
fonte
7
ATENÇÃO! A Amazon cobra extra para cada chamada LIST! Este método está ok, mas não o use para verificar se o arquivo existe antes de baixá-lo.
user34402
Essa não é uma boa maneira de saber se um arquivo existe, pois ele obtém todos os objetos que correspondem ao prefixo. Se você tiver vários arquivos que começam com a chave, ele baixará todos os objetos, incluindo aquele que você especificou.
Criptografia de
Em relação ao custo de LIST vs GET: observe que você também será cobrado por quaisquer dados transferidos para fora. Portanto, se for extremamente improvável que o arquivo exista (por exemplo, você gerou um UUID aleatório como uma chave e deseja ter certeza de que ainda não está em uso), GET é muito mais barato. Mas se os arquivos tiverem 0,5 MB e já tiverem 11% de chance de já existirem, então LIST parece um pouco mais barato. Mesmo se os arquivos tiverem 0,1 MB e 52% de chance de existirem ... Quanto maiores os arquivos, mais cedo LIST fica mais barato. Mas, novamente, um cenário comum é testar uma chave UUID recém-gerada, e GET é mais barato para isso.
Bampfer
5

Para PHP (eu sei que a questão é Java, mas o Google me trouxe aqui), você pode usar stream wrappers e file_exists

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

Este código java verifica se a chave (arquivo) existe no intervalo 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;
}
c0mrade
fonte
2
Isso deve funcionar, mas também deve ser lento no caso de haver milhares de arquivos, e para cada arquivo seria necessário um loop.
Danijel
como @Danijel disse, isso realmente determinará se um objeto de uma determinada chave existe ou não, mas para fazer isso, ele deve percorrer potencialmente dezenas de milhares de objetos em S3 antes de determinar se existe ou não
Don Cheadle
1
Eu discordo de @Danijel e mmcrae sobre isso ser lento. A solicitação listObjects especifica .withPrefix (arquivo), portanto, deve retornar no máximo o único arquivo correspondente, a menos que haja outros arquivos cujo nome comece com o nome do arquivo de destino.
davidwebster48
3

Divida seu caminho em balde e objeto. Testando o balde usando o método doesBucketExist, Testando o objeto usando o tamanho da listagem (0 caso não exista). Portanto, este código fará:

String bucket = ...;
String objectInBucket = ...;
AmazonS3 s3 = new AmazonS3Client(...);
return s3.doesBucketExist(bucket) 
       && !s3.listObjects(bucket, objectInBucket).getObjectSummaries().isEmpty();
roee
fonte
Fácil e simples. Obrigado
Thermech
3

Usando Object isting. Função Java para verificar se a chave especificada existe no 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;
    }
Kaustuv
fonte
1

Existe uma maneira fácil de fazer isso usando o método isObjectInBucket () da API jetS3t.

Código de amostra:

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

        }
Dhwaneel
fonte
Ele faz a mesma chamada get-metadata nos bastidores + captura de exceção: grepcode.com/file/repo1.maven.org/maven2/net.java.dev.jets3t/…
alexandroid
1

As outras respostas são para AWS SDK v1. Aqui está um método para AWS SDK v2 (atualmente 2.3.9).

Observe que os métodos getObjectMetadatae doesObjectExistnão estão atualmente no SDK v2! Portanto, essas não são mais opções. Somos forçados a usar getObjectou listObjects.

listObjectsas chamadas são atualmente 12,5 vezes mais caras do que fazer getObject. Mas a AWS também cobra por quaisquer dados baixados, o que aumenta o preço getObject se o arquivo existir . Contanto que seja muito improvável que o arquivo exista (por exemplo, você gerou uma nova chave UUID aleatoriamente e só precisa verificar se ela não foi usada), então chamar getObjecté significativamente mais barato pelos meus cálculos.

Porém, apenas para garantir a segurança, adicionei uma range()especificação para solicitar que a AWS envie apenas alguns bytes do arquivo. Pelo que eu sei, o SDK sempre respeitará isso e não cobrará pelo download de todo o arquivo. Mas eu não verifiquei isso, então confie nesse comportamento por sua própria conta e risco! (Além disso, não tenho certeza de como rangese comporta se o objeto S3 tiver 0 bytes de comprimento.)

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

Nota: este código assume s3Cliente logé declarado e inicializado em outro lugar. O método retorna um booleano, mas pode lançar exceções.

Bampfer
fonte
Parece que agora há um s3Client.headObject()na V2 para fazer isso: stackoverflow.com/a/56949742/9814131 , e você verificará o S3Exceptioncódigo de status 404 para verificar se o objeto existe de acordo com o problema do github github.com/aws/aws-sdk- java-v2 / issues / 297 . Mas eu acho que o seu é mais progressivo, pois tem tão pouca sobrecarga quanto 0-3 bytes.
Shaung Cheng
1

Eu também enfrentei esse problema quando usei

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

Recebi a chave de erro não encontrada

Quando eu bato e tento

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

funcionou, este código está funcionando com jar 1.9, caso contrário, atualize para 1.11 e use doesObjectExist como dito acima

Aakash Sharma
fonte
1

Como outros mencionaram, para o AWS S3 Java SDK 2.10+ você pode usar o objeto HeadObjectRequest para verificar se há um arquivo em seu balde S3. Isso funcionará como uma solicitação GET sem realmente obter o arquivo.

Código de exemplo, já que outros não adicionaram nenhum código acima:

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;
   }
}
Navigatron
fonte
lança NoSuchKeyException
Andrii Karaivanskyi
Isso porque a chave não existe. Isso é exatamente o que você está procurando. Portanto, trate essa exceção e retorne falso para ela. Eu atualizei o código acima para incluir o try / catch.
Navigatron
Então você não precisa de headObjectResponsenada. throws Exceptionnão é necessário também.
Andrii Karaivanskyi
@AndriiKaraivanskyi é apenas um exemplo, não testei.
Navigatron
headObjectResponse.sdkHttpResponse () .isSuccessful (); é sempre bem-sucedido se o arquivo existe ou não?
marcar
0

Alternativamente, você pode usar a biblioteca cliente Minio-Java , seu código aberto e compatível com AWS S3 API.

Você pode usar exemplos Minio-Java StatObject.java para o mesmo.

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;


public class GetObject {
  public static void main (String [] args)
    lança NoSuchAlgorithmException, IOException, InvalidKeyException, XmlPullParserException, MinioException {
    // Observação: YOUR-ACCESSKEYID, YOUR-SECRETACCESSKEY e my-bucketname são
    // valores fictícios, substitua-os pelos valores originais.
    // Definir endpoint s3, região é calculada automaticamente
    MinioClient s3Client = new MinioClient ("https://s3.amazonaws.com", "YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY");
    InputStream stream = s3Client.getObject ("my-bucketname", "my-objectname");

    byte [] buf = novo byte [16384];
    int bytesRead;
    while ((bytesRead = stream.read (buf, 0, buf.length))> = 0) {
      System.out.println (nova String (buf, 0, bytesRead));
    }

    stream.close ();
  }
}

Espero que ajude.

Disclaimer: Eu trabalho para Minio

koolhead17
fonte