Как проверить, существует ли указанный ключ в заданном ведре S3 с помощью Java

87

Я хотел бы проверить, существует ли ключ в данном ведре, используя Java. Я посмотрел API, но нет никаких полезных методов. Я попытался использовать, getObjectно возникла исключительная ситуация.

по его стопам
источник
2
В будущем, пожалуйста, предоставьте дополнительную информацию, например, какое у вас было исключение .. Я дал ответ, основанный на предположении ..
sethu
4
К вашему сведению: принятый ответ на этот вопрос - не лучший ответ.
malana

Ответы:

3

Используйте библиотеку jets3t. Это намного проще и надежнее, чем AWS sdk. Используя эту библиотеку, вы можете вызвать s3service.getObjectDetails (). Это будет проверять и извлекать только детали объекта (не содержимое) объекта. Если объект отсутствует, он выдаст ошибку 404. Таким образом, вы можете перехватить это исключение и обработать его в своем приложении.

Но для того, чтобы это работало, вам потребуется доступ к ListBucket для пользователя в этом сегменте. Просто получить доступ к GetObject не получится. Причина в том, что Amazon не позволит вам проверить наличие ключа, если у вас нет доступа к ListBucket. В некоторых случаях злоумышленникам достаточно просто знать, присутствует ли ключ или нет. Следовательно, если у них нет доступа к ListBucket, они не смогут этого сделать.

Сетху
источник
4
Все - см. Обновленный ответ на этот вопрос ниже: stackoverflow.com/a/36653034/49678
alexandroid
3
jets3t - это старая устаревшая библиотека. Вместо этого используйте файл aws-java-sdk.
the_storyteller
«проще и надежнее» очень субъективно,
Лев Романовский
291

Теперь в официальном API Java есть метод doesObjectExist .

Наслаждайтесь!

малана
источник
13
Добавлен в 1.10.51
steamer25
4
Мы должны проголосовать за это и вывести это на вершину!
SureshS
2
Правильнее было бы сделать это принятым ответом, но только OP может это сделать. meta.stackexchange.com/questions/120568/…
malana
4
Это должен сделать сетевой вызов, что дорого, если у вас много объектов ... Жаль, что он не может просто вернуть null в запросе метаданных.
Джоэл
9
Похоже, Amazon удален doesObjectExistиз SDK 2.x (в настоящее время 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);
}

Примечание об исключении: я знаю, что исключения не следует использовать для управления потоком. Проблема в том, что Amazon не предоставил API для проверки этого потока - только документация об исключении.

Алик Эльзин-килака
источник
14
Не используйте обработку исключений для управления программой.
Саймон Пек
34
@SimonPeck: ты прав. Проблема в том, что Amazon не предоставил API для проверки этого потока - просто документация об исключении. Пожалуйста, удалите свой голос против, если не проголосовали за него.
AlikElzin-kilaka
1
Это больше не соответствует действительности для Java SDK. Я вижу, что для my errorMessageустановлено значение «Not Found», но errorCodeэто null.
bstempi
3
Я бы пошел искать код состояния 404. Кажется более надежным, чем смотреть на строку
Оскар Кьеллин
2
Комментарий @rboarman неверен - это так NoSuchKey. Полный
Аллен Джордж
22

Используя AWS SDK, используйте метод 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;
}
user979051
источник
2
getObject также генерирует исключение AmazonServiceException, так зачем делать два вызова? Кроме того, как я узнаю, что объект не существует из этого исключения? Возможно, это произошло из-за очередной ошибки S3, и объект действительно найден.
AlikElzin-kilaka
5
Не используйте обработку исключений для управления программой.
Саймон Пек
4
@ AlikElzin-kilaka, потому что getObject () означает, что вам нужно загрузить содержимое объекта, которое потенциально может быть огромным.
Джейсон Николс
18
@SimonPeck, это не идеально, но когда Amazon предлагает подходящий метод exists (), ваша точка зрения верна.
Джейсон Николс
4
@SimonPeck у вас есть альтернатива в этом случае? Это не вопиющее злоупотребление исключениями как потоком управления программой ... это просто, точно в том, что делает, и безопасно. Если вы доведете свою идею до крайности (как очевидно, если вы думаете, что этот фрагмент кода злоупотребляет исключениями), тогда зачем вообще исключения на языке? Полагаю, вместо того, чтобы генерировать исключение, чтобы предупредить программу и изменить ход выполнения программы , среда выполнения должна просто завершиться.
Дон Чидл,
17

В 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 не прикреплены метаданные, то getObjectMetadata выдаст ошибку 404, даже если объект s3 существует. Я не буду рекомендовать это, если целью является проверка существования объекта s3.
Ашиш Гоэль
@AshishGoel, всегда будут метаданные, если объект существует. Фактически, базовый HTTP-запрос - это просто HEAD для URL-адреса объекта.
Пол Дрейпер,
5

Используйте параметр Prefix ListObjectsRequest в качестве ключа.

Код .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
источник
7
ПРЕДУПРЕЖДЕНИЕ! Amazon взимает дополнительную плату за каждый вызов СПИСКА! Этот метод подходит, но не используйте его для проверки наличия файла перед его загрузкой.
user34402 05
Это не лучший способ узнать, существует ли файл, поскольку он получает все объекты, соответствующие префиксу. Если у вас есть несколько файлов, которые начинаются с ключа, он загрузит все объекты, включая тот, который вы указали.
Crypth
Что касается стоимости LIST и GET: обратите внимание, что с вас также взимается плата за любые передаваемые данные. Поэтому, если крайне маловероятно, что файл существует (например, вы сгенерировали случайный UUID в качестве ключа и хотите убедиться, что он еще не используется), то GET намного дешевле. Но если файлы размером 0,5 МБ и с вероятностью 11% уже существуют, то LIST выглядит немного дешевле. То же самое, если файлы имеют размер 0,1 МБ и вероятность существования 52% ... Чем больше файлы, тем быстрее LIST станет дешевле. Но опять же, обычным сценарием является тестирование вновь сгенерированного ключа 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;
}
c0mrade
источник
2
Это должно сработать, но также должно быть медленным в случаях, когда есть тысячи файлов, и для каждого файла потребуется цикл.
Danijel
как сказал @Danijel, это действительно определит, существует ли объект данного ключа, но для этого он должен перебрать потенциально десятки тысяч объектов в S3, прежде чем определять, существует он или нет
Дон Чидл
1
Я не согласен с @Danijel и, ммкр, насчет того, что это происходит медленно. Запрос listObjects указывает .withPrefix (file), поэтому он должен возвращать не более одного совпадающего файла, если только нет других файлов, имя которых начинается с имени целевого файла.
davidwebster48
3

Разбейте свой путь на ведро и объект. Тестирование корзины с использованием метода doesBucketExistТестирование объекта с использованием размера листинга (0 в случае, если не существует). Итак, этот код будет делать:

String bucket = ...;
String objectInBucket = ...;
AmazonS3 s3 = new AmazonS3Client(...);
return s3.doesBucketExist(bucket) 
       && !s3.listObjects(bucket, objectInBucket).getObjectSummaries().isEmpty();
косуля
источник
Легко и просто. Спасибо
Thermech
3

Использование Object isting. Функция Java для проверки наличия указанного ключа в 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

Есть простой способ сделать это, используя метод isObjectInBucket () API JetS3t.

Образец кода:

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
источник
Он выполняет тот же вызов get-metadata под капотом + перехват исключения: grepcode.com/file/repo1.maven.org/maven2/net.java.dev.jets3t/…
alexandroid
1

Остальные ответы относятся к AWS SDK v1. Вот метод для AWS SDK v2 (в настоящее время 2.3.9).

Следует отметить , что getObjectMetadataи doesObjectExistметоды в настоящее время не в v2 SDK! Так что это уже не варианты. Мы вынуждены использовать либо getObjectили listObjects.

listObjectsзвонки в настоящее время в 12,5 раз дороже, чем getObject. Но AWS также взимает плату за любые загруженные данные, что повышает цену, getObject если файл существует . Пока файл вряд ли существует (например, вы случайно сгенерировали новый ключ UUID, и вам просто нужно дважды проверить, не используется ли он), то getObjectпо моим расчетам вызов значительно дешевле.

Однако на всякий случай я добавил range()спецификацию, чтобы попросить AWS отправить только несколько байтов файла. Насколько я знаю, SDK всегда будет учитывать это и не взимать плату за загрузку всего файла. Но я не проверял это, поэтому полагайтесь на такое поведение на свой страх и риск! (Кроме того, я не уверен, как rangeсебя ведет, если объект S3 имеет длину 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;
    }
}

Примечание: этот код предполагает , s3Clientи logобъявляется и инициализируется в другом месте. Метод возвращает логическое значение, но может вызывать исключения.

Бампфер
источник
Похоже, что теперь s3Client.headObject()в V2 есть для этого: stackoverflow.com/a/56949742/9814131 , и вы проверите S3Exceptionкод состояния 404, чтобы проверить, существует ли объект в соответствии с проблемой github github.com/aws/aws-sdk- Ява-v2 / вопросы / 297 . Но я думаю, ваши более прогрессивные, поскольку у них очень мало накладных расходов, как 0–3 байта.
Шаунг Ченг
1

Правильный способ сделать это в SDK V2 без перегрузки фактического получения объекта - использовать S3Client.headObject . Официально поддерживается AWS Change Log .

ET
источник
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 и используйте doObjectExist, как указано выше

Аакаш Шарма
источник
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
Андрей Караиванский
Это потому, что ключа не существует. Это именно то, что вы ищете. Так что обработайте это исключение и верните для него false. Я обновил приведенный выше код, включив в него команду try / catch.
Navigatron,
Тогда вам совсем не нужно headObjectResponse. throws Exceptionтоже не нужен.
Андрей Караиванский
@AndriiKaraivanskyi это просто пример, я не тестировал.
Navigatron,
headObjectResponse.sdkHttpResponse () .isSuccessful (); всегда успешно независимо от того, существует файл или нет?
отметка
0

В качестве альтернативы вы можете использовать клиентскую библиотеку Minio-Java , ее открытый исходный код и совместимую с AWS S3 API.

Для этого вы можете использовать примеры Minio-Java StatObject.java .

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)
    выдает исключение NoSuchAlgorithmException, IOException, InvalidKeyException, XmlPullParserException, MinioException {
    // Примечание: YOUR-ACCESSKEYID, YOUR-SECRETACCESSKEY и my-bucketname являются
    // фиктивные значения, замените их исходными значениями.
    // Устанавливаем конечную точку s3, регион рассчитывается автоматически
    MinioClient s3Client = новый MinioClient («https://s3.amazonaws.com», «YOUR-ACCESSKEYID», «YOUR-SECRETACCESSKEY»);
    InputStream stream = s3Client.getObject ("my-bucketname", "my-objectname");

    byte [] buf = новый байт [16384];
    int bytesRead;
    while ((bytesRead = stream.read (buf, 0, buf.length))> = 0) {
      System.out.println (новая строка (buf, 0, bytesRead));
    }

    stream.close ();
  }
}

Я надеюсь, что это помогает.

Отказ от ответственности: я работаю на Minio

koolhead17
источник