title: Stream/Download file without keeping in memory in Spring Boot
body: <p>My application uses Google Cloud Storage for storing large files and Spring Boot as backend.</p>
  <p>I just finished implementing the controller responsible for the download, following what I thought could be correct, but the behavior I am getting from the browser is not what I expected.</p>
  <p><strong>The Steps</strong></p>
  <p>If I try to download a file, the following will happens:</p>
  <ol>
  <li>The file (&quot;blob&quot;) is read from google cloud storage using a ReadChannel</li>
  <li>The channel write to a ByteArrayOutputStream;</li>
  <li>The channel writes using a buffer</li>
  <li>The ByteArrayOutputStream is converted in a InputStreamResource</li>
  <li>The inputStream Resource is then passed in the ResponseEntity</li>
  </ol>
  <p><strong>The Unexpected Behavior</strong></p>
  <p>If a user goes to the controller of the download, and ask for a specific file, the file is first loaded completely on the server, and then served to the client. In the meanwhile, the client sees like the server &quot;not responding&quot; (because the server is loading the file), which is not nice. When the file is then completely loaded on the server, is gonna appear in the &quot;Downloaded&quot; really fast.</p>
  <p><strong>What I wanted</strong>, was to avoid the &quot;not responding&quot; part, and stream or the file to the client, like does it happens normally when you try to download a large file, that you see a &quot;downloading&quot; wheel around the file in Chrome.</p>
  <p>Any help understanding what's wrong with the following code would be appreciated!</p>
  <p>The code for the Sring controller is the following:</p>
  <pre><code>@RequestMapping(&quot;/**/*&quot;)
      public ResponseEntity&lt;InputStreamResource&gt; downloadFile(
                  RedirectAttributes redirectAttributes, HttpServletRequest request, HttpServletResponse response) throws Exception {
                  String fid = ....
                  WritableByteChannel channel;
  
                      Storage storage = new DownloadAction().getStorage();
                      BlobId blobId = BlobId.of(BUCKET_ID, blobPath);
                      Blob blob = storage.get(blobId);
                     
  
                      ByteArrayOutputStream writeTo = new ByteArrayOutputStream();
                      try (ReadChannel reader = blob.reader()) {
                          channel = Channels.newChannel(writeTo);
                          ByteBuffer bytes = ByteBuffer.allocate(64 * 1024);
                          while (reader.read(bytes) &gt; 0) {
                              bytes.flip();
                              channel.write(bytes);
                              bytes.clear();
                          }
                      }
  
                      channel.close();
                      writeTo.close();
  
                      InputStream tmp = new ByteArrayInputStream(writeTo.toByteArray());
                      InputStreamResource resource = new InputStreamResource(tmp);
                      MediaType mediaType = MediaType.parseMediaType(mimeType);
                      return ResponseEntity.ok()
                              .contentType(mediaType)
                              .contentLength(fileSize)
                              .header(HttpHeaders.CONTENT_DISPOSITION, &quot;attachment; filename=\&quot;&quot; + fileName + &quot;\&quot;&quot;)
                              .body(resource);
                  } catch (IOException e) {
                      return ResponseEntity.notFound().build();
                  }
              } catch (Exception e) {
                  return ResponseEntity.notFound().build();
              }
  
      }
  </code></pre>