0

I want to implement a Liferay Portlet that downloads a ~1GB file from a separate server, and serves it to the website visitor who clicked the link.

The file must be streamed in a memory-efficient way (so no loading everything into memory), and the user should see the download progress shortly after clicking (so no storing everything onto the local disk).

I must use WebClient because it seems to be the standard for making web requests within Liferay 7 (RestTemplate will be deprecated).

I started writing something like this, inspired by an example from the javadoc:

Mono<DataBuffer> bodyMono = client.get()
 .uri("https://theotherserver.com/file94875.pdf")
 .retrieve()
 .bodyToMono(DataBuffer.class);

... which I would feed into the portlet's MVCResourceCommand.serveResource() via PortletResponseUtil.sendFile, which expects a java.io.InputStream.

Unfortunately WebClient gives me a Mono<DataBuffer> (or Flux<DataBuffer>), and another answer claims that reconstructing the InputStream defeats the purpose of using WebClient in the first place.

What would be the most efficient and WebClient-savvy way to implement this?

Nicolas Raoul
  • 58,567
  • 58
  • 222
  • 373
  • I might understand something wrong, but: Aren't you quite unaffected by Liferay's choices when writing a servlet? – Olaf Kock Feb 12 '19 at 11:06
  • bodyToFlux(DataBuffer.class) is probably the better way, since it'll stream the set of DataBuffers out. I had a related question once: https://stackoverflow.com/questions/49426304/convert-writes-to-outputstream-into-a-fluxdatabuffer-usable-by-serverresponse although I think this might be too complicated for your usecase - hence just here in comments, not in answer. – Frischling Feb 12 '19 at 12:45
  • For those (like me) who aren't familiar with Liferay's programming model, can you elaborate on your constraints? Can you use a Spring MVC Controller or are you constrained to work with the Servlet response directly? – Brian Clozel Feb 12 '19 at 20:00
  • @OlafKock: Sorry my wording was very bad, I am not using any servlet, I am going via MVCResourceCommand. I edited my question. – Nicolas Raoul Feb 13 '19 at 02:58
  • @Frischling, Brian: Sorry I really should have said the servlet is powered via https://docs.liferay.com/portal/7.0/javadocs/portal-kernel/com/liferay/portal/kernel/portlet/bridges/mvc/MVCResourceCommand.html I edited my question to add more details. It is really a Liferay question. – Nicolas Raoul Feb 13 '19 at 03:00
  • Granted this is based on a `Flux`, but does this provide any useful guidance? https://stackoverflow.com/a/51801335 – Brian Feb 13 '19 at 03:24
  • Ah, right Liferay. The documentation specifies, that you can .getPortletOutputStream(); after setting contentlengh (so browser knows how much to expect), you can use this: https://stackoverflow.com/questions/49426304/convert-writes-to-outputstream-into-a-fluxdatabuffer-usable-by-serverresponse – Frischling Feb 13 '19 at 13:40
  • @Frischling: Feel free to post an answer, thanks! :-) – Nicolas Raoul Feb 14 '19 at 01:21

1 Answers1

0

In case of Liferay, the documentation states, that you can use ....getPortletOutputStream() to retrieve an OutputStream. After setting contentlengh (so browser knows how much to expect), you can use this: Convert writes to OutputStream into a Flux<DataBuffer> usable by ServerResponse

To write your data to the OutputStream

Frischling
  • 2,100
  • 14
  • 34