当前位置: 首页 > 知识库问答 >
问题:

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

翟青青
2023-03-14

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

我刚刚完成了负责下载的控制器的实现,遵循我认为可能是正确的方法,但是我从浏览器中得到的行为并不是我所期望的。

步骤

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

  1. 使用ReadChannel从google云存储读取文件(blob)
  2. 将通道写入ByteArrayOutputStream;
  3. 通道使用缓冲区写入
  4. 字节数组输出流在InputStreamResources中转换
  5. 输入流资源然后在响应实体中传递

意想不到的行为

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

我想要的是,避免出现“无响应”部分,将文件或流式传输到客户端,就像您尝试下载大文件时通常会发生的情况一样,在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) {
                            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个答案

徐子石
2023-03-14

根据你的问题,你的桶中的对象是私有的,你想让有限的人群访问它们。如果是这样,签名网址就是您需要的。

您可以为桶中的特定对象生成带签名的URL,并将您的用户重定向到生成的URL-因此他将自己下载文件。

更多信息:GCloud签名URL

 类似资料:
  • 问题内容: 我正在编写一个简单的部署工具,该工具需要从s3中获取tar文件,然后将其解压缩,然后将其上传到我们的登台服务器。我想这样做而不将文件存储在本地,而是将它们保存在内存中。 这是我的代码,用于从s3下载文件 我想避免在此示例中创建目录和文件,而只是将所有内容保留在内存中。我省去了从代码中提取文件的步骤。下一步将使用go- scp 上传文件,如下所示: 然后,我的问题将集中在这段代码的一部分

  • 问题内容: 我正在使用vid-streamer https://github.com/meloncholy/vid- streamer 。 要安装,只需在您的电脑上创建一个文件夹 打开终端并做CD到文件夹 执行npm init 为应用命名,例如“ MyVidStreamer” 一个package.json将被创建 npm安装快递 在package.json的同一文件夹上创建server.js 写下

  • 我正在尝试将一个xml文件保存到Android的内部存储中。但是,该文件没有得到保留。每次我重新启动模拟器时,该文件都会是空的。只有当我在应用程序中手动选择向其写入内容时,该文件才会被写入内容。然后,如果我关闭应用程序或其他什么,文件内容会被保留。但是,当我关闭模拟器时,文件会在那里,但是空的。 因此,我不确定我是否正确地保存了它。 请看看我的代码,也许它有问题,或者它与我的Android模拟器的

  • 问题内容: 我正在尝试将csv文件作为附件下载流式传输。CSV文件的大小已达到4MB或更大,我需要一种让用户主动下载文件的方法,而不必等待所有数据都先创建并提交到内存中。 我首先使用了我自己的基于Django类的文件包装器。那失败了。然后,我在这里看到了一种使用生成器流式传输响应的方法: 如何使用Django流式传输HttpResponse 当我在生成器中引发错误时,可以看到我正在使用该函数创建正

  • vue 下载文件流怎么保留原文件名? elink.download 怎么获取原文件名