3

I'm trying to export a document from Google Drive via their Drive API, and there's an example of it in the docs, but I didn't have any luck with it.

Here's my code:

@RequestMapping(value = "/test", method = RequestMethod.GET, produces = MediaType.APPLICATION_PDF_VALUE)
public ByteArrayOutputStream downloadSlidesAsPdf(@RequestParam String fileId) throws IOException {
    OutputStream outputStream = new ByteArrayOutputStream();
    driveService.files().export(fileId, "application/pdf").executeMediaAndDownloadTo(outputStream);

    return (ByteArrayOutputStream) outputStream;
}

Executing this results in a warning in the console:

WARN  o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Resolved [org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class java.io.ByteArrayOutputStream] with preset Content-Type 'null']

I tried doing something like adding HttpServletResponse in the request and setting the content type to application/pdf and setting the output stream as such:

driveService.files().export(copiedFile.getId(), "application/pdf")
            .executeMediaAndDownloadTo(response.getOutputStream());

This works, but it downloads the file without the extension, and besides, I'd rather have it work as it's designed to than with a bunch of unnecessary workarounds.

I also tried a midway solution in which I set the HttpServletResponse content-type type to application/pdf, and then try to return the ByteArrayOutputStream like in the first attempt, but no luck. Anyone know what I'm missing here?

Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449

1 Answers1

1

The problem is there is nothing to convert in OutputStream - we only write to OutputStream, but do not read.

If controller needs to return binary content, the options are following:

  • byte[]
  • org.springframework.core.io.Resource (InputStreamResource, ByteArrayResource, FileSystemResource, etc)
  • org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody - my own preference
Andrey B. Panfilov
  • 4,324
  • 2
  • 12
  • 18
  • 1
    Which of these, if any, allows me to stream the file all the way through, without loading the whole thing into memory? Right now I have a ByteArrayOutputStream that I pass into a ByteArrayResource, but that requires calling ".toByteArray()" on the output stream, which is implied to read the whole thing into memory first (before passing it to the ByteArrayResource, which I guess stores the whole thing in memory anyway). – Dex Jan 09 '23 at 23:19
  • @Dex either `FileSystemResource` or `StreamingResponseBody` (`out -> Files.copy(path, out))` – Andrey B. Panfilov Jan 10 '23 at 09:52
  • 1
    I don't think that does what I want. That would copy a file into an output stream. Like the question here, I already have the output stream of a file (given to me by some other library, like this question here) and want to pass it to Spring's response. The StreamingResponseBody gets me close, but doesn't seem close enough. It has its own output stream so it looks like I still need to copy from my existing output stream into its. – Dex Jan 11 '23 at 11:29
  • That being said, I did do some additional research after my comment yesterday. It seems my only options to pass along my output data to the response without reading the whole thing in to memory, is to either hand the Spring response's output stream directly to the other library so that the other library writes the file directly to the Spring response in the first place, or to loop through and copy the bytes piecemeal from the library output stream to the Spring response output stream. – Dex Jan 11 '23 at 11:34