Як перевірити, чи існує вказаний ключ у даному сегменті S3 за допомогою Java


87

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


2
Надалі, будь ласка, надайте більше інформації, наприклад, що було винятком, який ви отримали .. Я дав відповідь, базуючись на припущенні ..
Сету,

4
FYI: На це питання прийнята відповідь - не найкраща відповідь.
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 є метод doObjectExist .

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


13
Він був доданий в 1.10.51
пароплав 25

4
Ми повинні проголосувати за це і взяти це на вершину!
SureshS

2
Правильним буде зробити цю прийняту відповідь, але це може зробити лише ОП. meta.stackexchange.com/questions/120568/…
Малана

4
Це повинно зробити мережевий виклик, що дорого, якщо у вас багато об’єктів ... Шкода, що він не може просто повернути null на запит метаданих.
Джоел

9
Схоже, Amazon вилучено doesObjectExistз SDK 2.x (на даний момент v2.3.9).
Бампфер

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. Я бачу, що для мого errorMessageвстановлено значення "Не знайдено", але значення errorCodeє нулем.
bstempi

3
Я б пішов шукати код стану 404. Здається здається більш надійним, ніж дивлячись на рядок
Оскар К’єллін

2
Коментар @rboarman неправильний - він є NoSuchKey. Остаточний перелік кодів помилок S3 див. У документації: docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
Аллен Джордж

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

2
getObject також кидає AmazonServiceException, так навіщо робити два дзвінки? Крім того, як я можу дізнатися, що об’єкт не існує від цього напруження? Можливо, це сталося через чергову помилку S3, і об’єкт справді знайдений.
АликЕльзін-кілака

5
Не використовуйте обробку винятків для управління програмою.
Саймон Пек,

4
@ AlikElzin-kilaka, оскільки getObject () означає, що вам потрібно завантажити вміст об'єкта, який потенційно може бути величезним.
Джейсон Ніколс

18
@SimonPeck, це не ідеально, але коли Amazon пропонує відповідний метод існує (), тоді ваша точка зору є дійсною.
Джейсон Ніколс

4
@SimonPeck у вас є альтернатива в цьому випадку? Це не грубе зловживання винятками, як потік керування програмою ... це просто, точно, що робить, і безпечно. Якщо ви доводите свою ідею до крайності (як ви, мабуть, вважаєте, якщо цей фрагмент коду зловживає винятками), то навіщо взагалі існувати винятки в мові? Замість того, щоб видавати виняток, щоб попередити програму та змінити потік програми , час виконання, я гадаю, просто припиняється.
Дон Чідл

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 не додано жодних метаданих, тоді getObjectMetadata видасть помилку 404, навіть якщо об'єкт s3 існує. Я не буду рекомендувати цього, якщо метою є перевірка існування об'єкта s3.
Ashish Goel,

@AshishGoel, завжди існуватимуть метадані, якщо об’єкт існує. Насправді базовий HTTP-запит - це просто HEAD до URL-адреси об’єкта.
Пол Дрейпер,

5

Як ключ використовуйте префікс налаштування 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;
            }
        }
    }.

7
УВАГА! Amazon бере додаткову плату за кожен дзвінок зі списку! Цей спосіб нормальний, але не використовуйте його, щоб перевірити, чи існує файл перед його завантаженням.
user34402

Це невдалий спосіб отримати, якщо файл існує, оскільки він отримує всі об’єкти, що відповідають префіксу. Якщо у вас є кілька файлів, які починаються з ключа, він завантажить усі об’єкти, включаючи вказаний вами.
Crypth

Щодо вартості LIST проти GET: зверніть увагу, що ви також отримуєте плату за будь-які передані дані. Отже, якщо надзвичайно малоймовірно, що файл існує (наприклад, ви створили випадковий UUID як ключ і хочете переконатися, що він уже не використовується), тоді GET набагато дешевший. Але якщо файли розміром 0,5 МБ і мають 11% шансів вже існувати, то LIST виглядає трохи дешевше. Те саме, якщо файли розміром 0,1 МБ і мають 52% шансів на існування ... Чим більше файли, тим швидше СПИСОК стає дешевшим. Але знову ж таки, загальний сценарій тестує нещодавно згенерований ключ UUID, і GET дешевший для цього.
Бампфер

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

як сказав @Danijel, це справді визначатиме, чи існує об'єкт даного ключа чи ні, але для цього він повинен проходити через потенційно десятки тисяч об'єктів у S3 перед тим, як визначити, чи існує він чи ні
Don Cheadle

1
Я не згоден з @Danijel та mmcrae щодо того, що це повільно. Запит listObjects визначає .withPrefix (файл), тому він повинен повертати щонайбільше один відповідний файл, якщо тільки немає інших файлів, ім'я яких починається з імені цільового файлу.
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

        }

Він робить той самий виклик мета-даних get під кришкою + виняток catch: 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- java-v2 / issues / 297 . Але я думаю, ваші є більш прогресивними, оскільки вони мають настільки мало накладних витрат, як 0-3 байти.
Шаун Ченг

1

Правильним способом зробити це в SDK V2, без перевантаження фактично отримання об’єкта, є використання S3Client.headObject . Офіційно підкріплено журналом змін AWS .


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 і використовуйте doesObjectExist, як зазначено вище


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.
Навігатрон

Тоді вам зовсім не потрібно headObjectResponse. throws Exceptionтакож не потрібно.
Андрій Караїванський

@AndriiKaraivanskyi це лише приклад, я його не тестував.
Навігатрон

headObjectResponse.sdkHttpResponse () .isSuccessful (); завжди успішно, існує файл чи його немає?
позначка

0

Крім того, ви можете використовувати клієнтську бібліотеку Minio-Java , її Open Source і сумісну з 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 {
  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");

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

    stream.close ();
  }
}

Сподіваюся, це допоможе.

Застереження: я працюю на Minio

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.