无法以非阻塞方式在 Docker 容器内读取类路径资源文件。

huangapple go评论104阅读模式
英文:

Unable to read classpath resource file in non-blocking way inside docker container

问题

我试图从 src/main/resources 目录加载一个名为 sample.json 的 JSON 文件。

我必须将该 JSON 映射到一个 Java 对象。我的应用程序是响应式的,我正在使用 Spring WebFlux。

我已经按照 Simon's Blog 的指导完成了以下代码:

  1. SimpleModule module = new SimpleModule();
  2. module.addDeserializer(OffsetDateTime.class, new JsonDeserializer<>() {
  3. @Override
  4. public OffsetDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
  5. return OffsetDateTime.parse(p.getValueAsString());
  6. }
  7. });
  8. ObjectMapper objectMapper = new ObjectMapper();
  9. objectMapper.registerModule(module);
  10. return Flux.using(() -> Files.lines(Paths.get(ClassLoader.getSystemResource("sample.json").toURI())),
  11. Flux::fromStream,
  12. BaseStream::close
  13. )
  14. .collectList()
  15. .map(lines -> String.join("\n", lines))
  16. .map(jsonContent -> {
  17. try {
  18. return objectMapper.readValue(jsonContent, MyPojo.class);
  19. } catch (JsonProcessingException e) {
  20. e.printStackTrace();
  21. return null;
  22. }
  23. })
  24. .map(MyPojo::getValues());

这在本地运行正常,但在 Docker 容器内运行时失败。(我使用 gradle 构建生成 JAR 文件,然后从中构建 Docker 镜像)

部分错误堆栈跟踪:

> java.nio.file.FileSystemNotFoundException: null
at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.getFileSystem(ZipFileSystemProvider.java:169) ~[jdk.zipfs:na]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:

英文:

I am trying to load a JSON file sample.json from src/main/resources directory.

I have to map that json to a Java Object. And my application is reactive, I am using Spring webflux.

I have followed Simon's Blog to come up with this:

  1. SimpleModule module = new SimpleModule();
  2. module.addDeserializer(OffsetDateTime.class, new JsonDeserializer<>() {
  3. @Override
  4. public OffsetDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
  5. return OffsetDateTime.parse(p.getValueAsString());
  6. }
  7. });
  8. ObjectMapper objectMapper = new ObjectMapper();
  9. objectMapper.registerModule(module);
  10. return Flux.using(() -> Files.lines(Paths.get(ClassLoader.getSystemResource("sample.json").toURI())),
  11. Flux::fromStream,
  12. BaseStream::close
  13. )
  14. .collectList()
  15. .map(lines -> String.join("\n", lines))
  16. .map(jsonContent -> {
  17. try {
  18. return objectMapper.readValue(jsonContent, MyPojo.class);
  19. } catch (JsonProcessingException e) {
  20. e.printStackTrace();
  21. return null;
  22. }
  23. })
  24. .map(MyPojo::getValues());

This works fine in local but fails when running inside a docker container. (I am using gradle build to build the jar file and then build the docker image from it)

Part of the error stacktrace:

> java.nio.file.FileSystemNotFoundException: null
at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.getFileSystem(ZipFileSystemProvider.java:169) ~[jdk.zipfs:na]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:

答案1

得分: 3

当运行Docker镜像时,您正在尝试从Jar内部访问文件。似乎直接将资源URI传递给Paths.get是行不通的。

您可以参考以下关于同一问题的stackoverflow讨论:

  1. 这个答案
  2. 回答这个问题
  3. 回答这个问题

我认为您应该查看Spring的这个类,它使用Jackson 2.9将字节流解码为JSON,并转换为对象,利用非阻塞解析。

以下代码应该满足您的需求:

首先以Spring的方式获取对Resource的引用

  1. @Value("classpath:/sample.json")
  2. Resource sampleFile;

然后进行以下操作:

  1. return new Jackson2JsonDecoder()
  2. .decodeToMono(
  3. DataBufferUtils.read(sampleFile, new DefaultDataBufferFactory(), 4096),
  4. ResolvableType.forClass(MyPojo.class),
  5. null,
  6. null)
  7. .map(object -> (MyPojo) object)
  8. .map(MyPojo::getValues);

通过这种方式,您可以使用DataBufferUtils.read以非阻塞方式读取文件,还可以将JSON映射到您的POJO中。

英文:

When running the docker image, you are trying to access the file from inside a Jar. And it seems that passing a resource URI directly to Paths.get won't work.

You can refer to the following stackoverflow discussions regarding the same:

  1. This answer
  2. Answers to this question
  3. Answers to this question

I think you should take a look at this class of Spring which decodes a byte stream into JSON and convert to Object's with Jackson 2.9, leveraging non-blocking parsing.

This code should serve your needs:

First get reference to the Resource in Spring's way

  1. @Value("classpath:/sample.json")
  2. Resource sampleFile;

Then do this:

  1. return new Jackson2JsonDecoder()
  2. .decodeToMono(
  3. DataBufferUtils.read(sampleFile, new DefaultDataBufferFactory(), 4096),
  4. ResolvableType.forClass(MyPojo.class),
  5. null,
  6. null)
  7. .map(object -> (MyPojo) object)
  8. .map(MyPojo::getValues)

In this way, you can read the file in a non-blocking manner using DataBufferUtils.read and also map the json to your POJO.

huangapple
  • 本文由 发表于 2020年8月10日 17:37:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/63337681.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定