2

In the following snippet, I'm trying to extract a file's content (sent to a given service) using Spring's Part object and convert it into a String.

The issue is that it skips the mapper function and the code inside the mapper function doesn't execute like the filePartMono's content is empty, but when I inspect the object at runtime , its storage field has the file's data.

public void parseFilePart(Part filePartMono) {
    filePartMono.content().map(dataBuffer -> {
            byte[] bytes = new byte[dataBuffer.readableByteCount()];
            dataBuffer.read(bytes);
            DataBufferUtils.release(dataBuffer);
            String fileContent = new String(bytes, StandardCharsets.UTF_8);
        });
}
sharon182
  • 533
  • 1
  • 4
  • 19
  • why is the function void? what is calling this function. You should not have void functions, you need to return something so that the calling code can chain on. Please post the code calling this function. – Toerktumlare Feb 15 '21 at 23:09
  • The method should return some Mono, but even with that return type it behaves the same. the void is just for testing purpose. – sharon182 Feb 16 '21 at 08:26

1 Answers1

3

org.springframework.http.codec.multipart.Part.content() returns a Flux<DataBuffer>, meaning nothing happens until you subscribe to this Publisher.

If your code can be executed in a blocking way without causing errors, you can refactor it like this to get the String result:

public void parseFilePart(Part filePartMono) {
  List<String> parts = 
    filePartMono.content()
    .map(dataBuffer -> {
          byte[] bytes = new byte[dataBuffer.readableByteCount()];
          dataBuffer.read(bytes);
          DataBufferUtils.release(dataBuffer);
          return new String(bytes, StandardCharsets.UTF_8);
    })
    .collectList()
    .block();
  //do what you want here with the Strings you retrieved
}

If you're sure that the Flux<DataBuffer> will always emit 1 single DataBuffer, you can replace .collectList().block() with .blockFirst() and obtain a String result instead of List<String>.

If your code can't be executed in a blocking fashion, then you could refactor it like this:

public void parseFilePart(Part filePartMono) {
    filePartMono.content()
    .map(dataBuffer -> {
          byte[] bytes = new byte[dataBuffer.readableByteCount()];
          dataBuffer.read(bytes);
          DataBufferUtils.release(dataBuffer);
          return new String(bytes, StandardCharsets.UTF_8);
    })
    .subscribe(resultString -> {
      //do what you want with the result String here
    });
}

P.S. I didn't test your implementation to convert DataBuffer to String, so you might have to double-check that now that it's actually invoked

Domenico Sibilio
  • 1,189
  • 1
  • 7
  • 20
  • 1
    Your suggestion throws: java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-7 – sharon182 Feb 15 '21 at 19:37
  • Then you can't execute blocking code on that thread, I'll refactor the answer to be fully reactive then. – Domenico Sibilio Feb 15 '21 at 19:40
  • Still not working. it skips also the code block of the consumer and exit the method immediately – sharon182 Feb 15 '21 at 20:02
  • 1
    Seems like the source is empty but it's not. this solution works https://manhtai.github.io/posts/flux-databuffer-to-inputstream/ . but I'd like to understand what's wrong with mine... – sharon182 Feb 15 '21 at 20:10
  • subscribe is aysnc,may not get no data – tangyaya Jan 30 '23 at 08:04