微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 StreamSaver.js 流式传输大型 blob 文件 建议/背景你的问题

如何解决使用 StreamSaver.js 流式传输大型 blob 文件 建议/背景你的问题

我正在尝试使用 Angular 组件中的 StreamSaver.js 将大型数据文件从服务器直接下载到文件系统。但是在 ~2GB 之后会发生错误。似乎数据首先被流式传输到浏览器内存中的 blob 中。并且可能有 2GB 的限制。我的代码基本上取自 StreamSaver 示例。知道我做错了什么以及为什么文件没有直接保存在文件系统上吗?

服务:

public transferData(url: string): Observable<Blob> {
    return this.http.get(url,{ responseType: 'blob' });
}

组件:

download(url: string) {
    this.extractionService.transferData(url)
      .subscribe(blob => {
        const fileStream = streamSaver.createWriteStream('data.tel',{
          size: blob.size
        });
        const readableStream = blob.stream();
        if (window.WritableStream && readableStream.pipeto) {
          return readableStream
            .pipeto(fileStream)
            .then(() => console.log("done writing"));
        }
        const writer = fileStream.getWriter();
        const reader = readableStream.getReader();
        const pump = () =>
          reader.read()
            .then(res => res.done ? writer.close() : writer.write(res.value).then(pump));
        pump();
      });
}

请求文件的头部:
"内容类型:应用程序/八位字节流\r\n"
"内容处理:附件;文件名=data.tel\r\n"

解决方法

建议/背景

StreamSaver 适用于在客户端生成大量数据的用户,例如长时间的摄像头记录。如果文件来自云端,而您已经有了 Content-Disposition 附件标头,那么您唯一需要做的就是在浏览器中打开此 URL。

有几种方法可以下载文件:

  • location.href = url
  • <a href="url">download</a>
  • <iframe src="url" hidden>
  • 对于需要发布数据或使用其他 HTTP 方法的用户,他们可以发布(隐藏的)<form>

只要浏览器不知道如何处理文件,它就会触发下载,而这就是您已经在使用 Content-Type: application/octet-stream


由于您使用 Ajax 下载文件并且浏览器知道如何处理数据(将其提供给主 JS 线程),因此 Content-TypeContent-Disposition 没有任何作用。>

StreamSaver 试图模仿服务器如何使用 ServiceWorkers 和自定义响应来保存文件。
你已经在服务器上做了!您唯一需要做的就是停止使用 AJAX 下载文件。所以我认为您根本不需要 StreamSaver。


你的问题

... 是首先将整个数据作为 Blob 下载到内存中,然后保存文件。这违背了使用 StreamSaver 的全部目的,那么您也可以使用更简单的 FileSaver.js 库,或者像 FileSaver.js 那样从 Blob 手动创建对象 url + 链接。

Object.assign(
  document.createElement('a'),{ href: URL.createObjectURL(blob),download: 'name.txt' }
).click()

此外,您不能使用 Angular 的 HTTP 服务,因为它们使用旧的 XMLHttpRequest,并且它不能像 fetch 那样从 response.body 为您提供可读流,所以我的建议只是简单地使用 Fetch API。

https://github.com/angular/angular/issues/36246

,

我以前或多或少处于相同的情况。 Angular http 服务在此用例中不起作用,因为它不会为您提供 ReadableStream。我的解决方案是使用 fetch API。

但请注意,fetch流式响应正文是一项实验性功能,不与所有浏览器兼容。根据我的测试,它在 Google Chrome 上运行良好,但不适用于 Firefox 或 Safari。为了克服这个限制,我将一个名为 web-streams-polyfill 的 Javascript 库与 fetch 一起使用。

代码看起来像这样:

import { WritableStream } from 'web-streams-polyfill/ponyfill';
import streamSaver from 'streamsaver';

fetch(url,{
    method: 'POST',headers: {
        'Content-Type': 'application/json'
    },body: JSON.stringify(data)
})
.then(response => {

    let contentDisposition = response.headers.get('Content-Disposition');
    let fileName = contentDisposition.substring(contentDisposition.lastIndexOf('=') + 1);

    // These code section is adapted from an example of the StreamSaver.js
    // https://jimmywarting.github.io/StreamSaver.js/examples/fetch.html

    // If the WritableStream is not available (Firefox,Safari),take it from the ponyfill
    if (!window.WritableStream) {
        streamSaver.WritableStream = WritableStream;
        window.WritableStream = WritableStream;
    }

    const fileStream = streamSaver.createWriteStream(fileName);
    const readableStream = response.body;

    // More optimized
    if (readableStream.pipeTo) {
        return readableStream.pipeTo(fileStream);
    }

    window.writer = fileStream.getWriter();

    const reader = response.body.getReader();
    const pump = () => reader.read()
        .then(res => res.done
            ? writer.close()
            : writer.write(res.value).then(pump));

    pump();
})
.catch(error => {
    console.log(error);
});;

这个想法是检查 window.WritableStream 在当前浏览器中是否可用。如果不是,则将 WritableStream 中的 ponyfill 直接分配给 streamSaver.WritableStream 属性。

由于我前段时间遇到了这个问题,我的解决方案只在 Google Chrome 78、Firefox 70、Safari 13 上进行了测试; web-streams-polyfill 2.0.5StreamSaver.js 2.0.3

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?