流/下载文件而不在Spring Boot中保留内存

时间:2019-02-05 10:53:32

标签: java spring spring-boot google-cloud-storage

我的应用程序使用Google Cloud Storage存储大文件,并使用Spring Boot作为后端。

我刚刚按照我认为可能是正确的方式实现了负责下载的控制器,但是我从浏览器中得到的行为不是我所期望的。

步骤

如果我尝试下载文件,则会发生以下情况:

  1. 使用ReadChannel从Google云存储中读取文件(“ blob”)
  2. 该通道写入ByteArrayOutputStream;
  3. 通道使用缓冲区写入
  4. 将ByteArrayOutputStream转换为InputStreamResource
  5. 然后将inputStream资源传递到ResponseEntity

意外行为

如果用户转到下载的控制器,并要求一个特定的文件,则首先将文件完全加载到服务器上,然后再提供给客户端。同时,客户端认为服务器“无响应”(因为服务器正在加载文件),这不是很好。然后,当文件完全加载到服务器上时,它将很快出现在“已下载”中。

我想要的是避免出现“不响应”部分,并将文件或文件流式传输到客户端,就像您尝试下载大文件时通常会发生的那样Chrome中一个围绕文件的“下载”轮。

任何帮助您理解以下代码有什么问题的人,将不胜感激!

Sring控制器的代码如下:

@RequestMapping("/**/*")
    public ResponseEntity<InputStreamResource> 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) > 0) {

                            System.out.println("here");
                            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, "attachment; filename=\"" + fileName + "\"")
                            .body(resource);
                } catch (IOException e) {
                    return ResponseEntity.notFound().build();
                }
            } catch (Exception e) {
                return ResponseEntity.notFound().build();
            }

    }

1 个答案:

答案 0 :(得分:0)

根据您的问题,存储桶中的对象是私有的,因此您希望为有限的人员提供访问权限。 如果是这样,则需要签名URL。

您可以为存储桶中的特定对象生成签名的URL,然后将用户重定向到生成的URL-这样他就可以自己下载文件。

更多信息:GCloud Signed URLs