如何使用 Angular 中浏览器的本机下载小部件以编程方式下载文件?

Posted

技术标签:

【中文标题】如何使用 Angular 中浏览器的本机下载小部件以编程方式下载文件?【英文标题】:How do I programmatically download a file using the browser's native download widget in Angular? 【发布时间】:2022-01-16 14:15:37 【问题描述】:

所以,当我请求我的网络服务下载一个 zip 文件时,它会秘密下载文件内容,突然间,文件出现在下载任务栏中,但已经下载完整(100%)

使用以下角度方法:


const endpoint = "http://localhost:8080/download/zip"
this.http.get<Blop>(endpoint, headers: httpHeaders, responseType: 'blob', reportProgress: true )

这就是我的订阅方式:

this.http.get<Blop>(endpoint, headers: httpHeaders, responseType: 'blob', reportProgress: true ).subscribe(
  next: data => 
    console.log('blocking or not');
    const blob = new Blob([data as any],  type: 'application/zip' );
    window.location.href = URL.createObjectURL(blob);
  
)

所以我注意到我的console.log(...) 直到下载结束才被调用,所以我认为浏览器用户界面在到达window.location.href 之前无法检测到下载。

如何在传输结束前强制在下载任务栏中显示下载,并在浏览器中查看下载进度?我找不到任何与异步 blop 或类似内容相关的内容。

PS:我的后端正在提供数据流,所以后端不是问题。通过浏览器直接调用我的api时,我们可以在下载任务栏看到下载进度。不过,如果你们有兴趣,这是 sn-p (spring-boot)

    @GetMapping("/download/zip")
    fun download(response: HttpServletResponse): StreamingResponseBody 
        val file = downloads.download("launcher")

        response.contentType = "application/zip"
        response.setHeader(
            "Content-Disposition",
            "attachment;filename=sample.zip"
        )
        response.setContentLengthLong(file.length())

        return StreamingResponseBody  outputStream: OutputStream ->
            var bytesRead: Int
            val buffer = ByteArray(2048)
            val inputStream: InputStream = file.inputStream()
            while (inputStream.read(buffer).also  bytesRead = it  != -1) 
                outputStream.write(buffer, 0, bytesRead)
            
        
    

【问题讨论】:

【参考方案1】:

下载有两种方式:

    http.get + msSaveOrOpenBlob(如果已定义)/createObjectURL
您可以完全控制请求、流程和错误。例如。你可以取消请求,添加额外的标题,等等。 下载仍保留在您的应用流程中,例如F5 将取消它。
    创建隐藏的下载链接并以编程方式单击它
你不能添加标题,你不能以简单的方式显示进度/错误(有一些技巧涉及到由 BE 设置的额外 cookie) 它被分层为单独的进程,例如你可以关闭你的应用标签或其他任何东西。

似乎没有机会混合这两种方法,总的来说,我会说第一种更现代,更适合小文件,但是如果您不满意,请尝试第二种 (https://***.com/a/49917066/4019404):

function download(url) 
  const a = document.createElement('a')
  a.href = url
  a.download = url.split('/').pop()
  document.body.appendChild(a)
  a.click()
  document.body.removeChild(a)

【讨论】:

所以..这意味着如果您的请求需要特殊标头,则无法触发浏览器的本机下载(包括我的意思是+进度条)功能,对吧? 我觉得只有当请求是 GET 时,浏览器才会处理下载 + 进度 因此,作为一种解决方法,您如何看待以下问题: - 由于下载 URL 需要 oauth2 和一些权限,我实际上无法使用您的第二种方式。但我认为生成动态唯一链接将是解决方案! 1. 请求带有 oauth 和特殊标头的下载 URL 2. 响应返回一个生成的一次性可用链接,用于下载受保护的内容 3. Angular 创建隐藏链接并单击它,这样浏览器下载进度条就可以工作了 是的,如果您需要一些 auth-headers,一次性令牌方法是一个不错的选择。我们使用它来将下载链接传递给 3rd 方 API。 因此,由于没有人能够证明可以使用角度和自定义标题触发浏览器下载的进度条,我接受您的,因为它提供了两种下载文件的可能方式。谢谢【参考方案2】:

假设您使用的是 Angular 的 HttpClient,这样的事情应该可以工作:

    const endpoint = "http://localhost:8080/download/zip"
    const req = new HttpRequest('GET', endpoint,  reportProgress: true, )
    this.Http.request(req).subscribe(event => 
      if (event.type === HttpEventType.DownloadProgress) 
        const percentDone = Math.round(100 * event.loaded / (event.total || 0))
        console.log(percentDone);
       else if (event instanceof HttpResponse) 
        const blob = new Blob([event.body as any],  type: 'application/zip' );
        window.location.href = URL.createObjectURL(blob);
      
    )

【讨论】:

好吧,感谢您抽出宝贵时间,但我要求将下载内容显示在 broswer 本地下载页面中。您在这里所做的只是记录下载进度 @Romain-p 如果我理解正确,您无法访问浏览器原生下载区。您可以使用自己的代码替换 console.log 以使用百分比更新您的页面 html 确实如此,但这不是我们想要的行为。我想使用原生下载区 @Romain-p 这是不可能的 我建议您在下载过程中只显示自己的进度指示器,完成后删除。您甚至可以模仿本地下载区域的外观。我不确定为什么这是期望的行为?【参考方案3】:

你可以简单地使用这个包

npm i ngx-filesaver

constructor(private _http: Http, private _FileSaverService: FileSaverService) 


onSave() 
  this._http.get('yourfile.png', 
    responseType: ResponseContentType.Blob // This must be a Blob type
  ).subscribe(res => 
    this._FileSaverService.save((<any>res)._body, fileName);
  );

在此处完成文档ngx file saver 还有file saver.js

【讨论】:

同样的问题,浏览器不显示下载进度。该文件在完全下载后可见 你确定吗?因为我再次尝试了文件保护程序演示,它在浏览器本机下载器中对我来说似乎工作正常【参考方案4】:

从技术角度来看,您无法更改浏览器在后台接收文件的行为方式。相反,您可以通过在发出 HTTP 请求时将选项 observe 设置为 events计算下载进度。这样,您不仅会收到请求的最终响应正文,而且还可以访问中间 HTTP 事件。 之后,您可以在浏览器之外显示自己的下载进度

Angular 中有多种类型的 HTTP 事件,它们都合并在 HttpEvent 类型下。我们还需要显式传递选项reportProgress 以接收HttpProgressEvents。 HTTP 请求最终将如下所示:

this.http.get(url, 
  reportProgress: true,
  observe: 'events',
  responseType: 'blob'
)

你可以在angular file download progress找到一个很好的实现。

【讨论】:

【参考方案5】:

试试这个:

$('a#someID').attr(target: '_blank', 
                    href  : 'http://localhost/directory/file.pdf');

它使用 jQuery

【讨论】:

【参考方案6】:

用户包https://www.npmjs.com/package/file-saver

npm i file-saver

然后在你的组件中,导入 SaveAs 方法

import  saveAs  from 'file-saver';


saveAs(url, name);

【讨论】:

以上是关于如何使用 Angular 中浏览器的本机下载小部件以编程方式下载文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何将动态外部组件加载到 Angular 应用程序中

Angular Gridster:调整小部件的大小

如何在 Tkinter 中使用“本机”GUI 外观?

Angular gridster 添加自定义内容

可在 angular-gridster 中的小部件之间拖动

如何使用 android 小部件在浏览器上打开图像 url