【问题标题】:Fetching specific fields from an S3 document从 S3 文档中获取特定字段
【发布时间】:2021-07-22 17:12:53
【问题描述】:

我在我的应用程序中使用 AWS Java SDK 与我的一个 S3 存储桶通信,该存储桶以 JSON 格式保存对象。

文档可能如下所示:

{
    "a" : dataA,
    "b" : dataB,
    "c" : dataC,
    "d" : dataD,
    "e" : dataE
} 

现在,对于某个文档,假设 document1 我需要获取与字段 ab 对应的值,而不是获取整个文档。

这听起来不可能,因为 S3 存储桶中可以包含任何类型的文档,而不仅仅是 JSON。

这是可以实现的吗?

【问题讨论】:

  • 据我所知,S3 只处理 blob(可能是二进制数据或文本),因此不提供解析 S3 本身上的存储桶内容的方法。因此,您需要将其转移到其他地方进行解析和提取,例如一个拉姆达。根据您的需要,您可能还需要考虑使用不同的存储桶布局(例如,使用更小或更具体的存储桶)或使用其他东西,例如一个 DynamoDB。
  • 可以查询S3对象的内容。 AWS Java SDK 支持这一点。请参阅以下链接以供参考:docs.aws.amazon.com/AmazonS3/latest/userguide/using-select.html
  • S3 选择仅支持“CSV、JSON 或 Parquet 格式”。因此,您可以根据需要进行查询。查询与sql查询类似。

标签: java amazon-s3 document aws-java-sdk


【解决方案1】:

实际上是doable。您可以像您描述的那样进行选择,但仅限于特定格式:JSON、CSV、Parquet。

想象在so67315601 存储桶中有一个data.json 文件在eu-central-1 中:

{
  "a": "dataA",
  "b": "dataB",
  "c": "dataC",
  "d": "dataD",
  "e": "dataE"
}

首先,了解如何通过 S3 控制台选择字段。使用“对象操作”→“使用 S3 选择查询”:


AWS Java 开发工具包 1.x

这是使用 AWS Java SDK 1.x 进行选择的代码:

@ExtendWith(S3.class)
class SelectTest {
    @AWSClient(endpoint = Endpoint.class)
    private AmazonS3 client;

    @Test
    void test() throws IOException {
        // LINES: Each line in the input data contains a single JSON object
        // DOCUMENT: A single JSON object can span multiple lines in the input
        final JSONInput input = new JSONInput();
        input.setType(JSONType.DOCUMENT);

        // Configure input format and compression
        final InputSerialization inputSerialization = new InputSerialization();
        inputSerialization.setJson(input);
        inputSerialization.setCompressionType(CompressionType.NONE);

        // Configure output format
        final OutputSerialization outputSerialization = new OutputSerialization();
        outputSerialization.setJson(new JSONOutput());

        // Build the request
        final SelectObjectContentRequest request = new SelectObjectContentRequest();
        request.setBucketName("so67315601");
        request.setKey("data.json");
        request.setExpression("SELECT s.a, s.b FROM s3object s LIMIT 5");
        request.setExpressionType(ExpressionType.SQL);
        request.setInputSerialization(inputSerialization);
        request.setOutputSerialization(outputSerialization);

        // Run the query
        final SelectObjectContentResult result = client.selectObjectContent(request);

        // Parse the results
        final InputStream stream = result.getPayload().getRecordsInputStream();

        IOUtils.copy(stream, System.out);
    }
}

输出是:

{"a":"dataA","b":"dataB"}

AWS Java SDK 2.x

AWS Java SDK 2.x 的代码更加巧妙。更多信息请参考this ticket

@ExtendWith(S3.class)
class SelectTest {
    @AWSClient(endpoint = Endpoint.class)
    private S3AsyncClient client;

    @Test
    void test() throws Exception {
        final InputSerialization inputSerialization = InputSerialization
            .builder()
            .json(JSONInput.builder().type(JSONType.DOCUMENT).build())
            .compressionType(CompressionType.NONE)
            .build();

        final OutputSerialization outputSerialization = OutputSerialization.builder()
            .json(JSONOutput.builder().build())
            .build();

        final SelectObjectContentRequest select = SelectObjectContentRequest.builder()
            .bucket("so67315601")
            .key("data.json")
            .expression("SELECT s.a, s.b FROM s3object s LIMIT 5")
            .expressionType(ExpressionType.SQL)
            .inputSerialization(inputSerialization)
            .outputSerialization(outputSerialization)
            .build();
        final TestHandler handler = new TestHandler();

        client.selectObjectContent(select, handler).get();

        RecordsEvent response = (RecordsEvent) handler.receivedEvents.stream()
            .filter(e -> e.sdkEventType() == SelectObjectContentEventStream.EventType.RECORDS)
            .findFirst()
            .orElse(null);

        System.out.println(response.payload().asUtf8String());
    }

    private static class TestHandler implements SelectObjectContentResponseHandler {
        private SelectObjectContentResponse response;
        private List<SelectObjectContentEventStream> receivedEvents = new ArrayList<>();
        private Throwable exception;

        @Override
        public void responseReceived(SelectObjectContentResponse response) {
            this.response = response;
        }

        @Override
        public void onEventStream(SdkPublisher<SelectObjectContentEventStream> publisher) {
            publisher.subscribe(receivedEvents::add);
        }

        @Override
        public void exceptionOccurred(Throwable throwable) {
            exception = throwable;
        }

        @Override
        public void complete() {
        }
    }
}

如您所见,可以通过编程方式进行 S3 选择!

您可能想知道@AWSClient@ExtendWith( S3.class ) 是什么?

这是一个小型库,用于在您的测试中注入 AWS 客户端,名为 aws-junit5。这将大大简化您的测试。我是作者。 The usage 非常简单——在你的下一个项目中试试吧!

【讨论】:

  • 像魅力一样工作。 aws-junit 听起来也不错。我要检查一下。 :)
猜你喜欢
  • 2018-09-04
  • 2020-09-20
  • 2021-07-04
  • 1970-01-01
  • 2023-02-13
  • 1970-01-01
  • 2021-02-01
  • 2019-05-28
  • 2020-08-01
相关资源
最近更新 更多