前端文件下载(四)

截止目前,我们已经分了三个篇幅讲解了前端文件的下载。

本文,我们以文章 前端文件下载(三) 为基础来扩展讲解通过第三方库下载大文件。

我们为什么不对原生的进行封装呢?我们当然可以对原生进行封装,但是有现成成熟的库,我们为什么不用呢?

案例使用的代码来源 前端文件下载(三),开发环境不变,服务端的代码不做变更。

axios

axios 是很受欢迎的 JavaScript 库,是基于 promiseHTTP 客户端,适用于浏览器和 nodejs

我们在前端模版上做些更改:

html 复制代码
<!DOCTYPE html>
<html>
<head>
  <title>SSR Download File</title>
  <!-- import -->
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
  <h1>Hello, Jimmy!</h1>
  <button id="download">Download File</button>
  <div style="display: flex; align-items: center;">
    <span>Downloading progress:</span>
    <progress id="progress" value="0" max="100"></progress>
    <span id="progressVal">0%</span>
  </div>
  <script>
    (function(){

      let downloadBtn = document.getElementById("download");
      let progressDom = document.getElementById("progress");
      let progressValDom = document.getElementById("progressVal");
      let hintDom = document.getElementById('hint');

      downloadBtn.addEventListener('click', function(){
        axios({
          method: 'get',
          url: '/download/file',
          responseType: 'blob',  // set to blob
          onDownloadProgress: function(progressEvent) {
            const percent_complete = ((progressEvent.loaded / progressEvent.total) * 100).toFixed(2);
            progressDom.value = percent_complete;
            progressVal.innerText = percent_complete + '%';
          }
        })
          .then(response => {
            const downloadLink = document.createElement('a');
            downloadLink.href = URL.createObjectURL(response.data); // createObjectURL
            downloadLink.download = 'demo.zip'; // should add, or filename extension not work
            downloadLink.click();
            URL.revokeObjectURL(downloadLink.href); // revoke
          })
          .catch(error => {
            // handle error
            console.error(error);
          });
      })
    })()
  </script>
</body>
</html>

我们做了下面的更改:

  • header 中引入 axios
  • axios 调用替换原生的 XMLHttpRequest

上面的调用方式,中规中矩,多多少少看到原生调用的影子,比如 responseType: 'blob'onDownloadProgress

@angular/common/http

axiosreactvue 框架开发的时,用的比较频繁。笔者使用的 angular 框架来开发,其中集成了 @angular/common/http。那么,它又是如何像 axios 调用文件下载的呢?

我们简单写了个 demo,如下:

html 复制代码
<!-- demo.component.html -->
<button
    type="button"
    class="btn btn-primary"
    (click)="downloadDemo()">
  <fa-icon [icon]="faDownload"></fa-icon>
  <span>download demo</span>
</button>

上面生成了一个调用下载接口的按钮。

我们简单生成一个服务类:

typescript 复制代码
// demo.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class DemoService {
  constructor(
    protected http: HttpClient
  ){}
  
  public dowloadFile(url: string): Observable<any> {
    return this.http.get(
      url,
      {
        observe: 'events',
        reportProgress: true, // trigger progress
        responseType: 'blob'
      }
    );
  }
}

接着我们调用服务:

typescript 复制代码
// demo.component.ts
import { Component, OnInit } from '@angular/core';
import { faDownload } from '@fortawesome/free-solid-svg-icons';
import { DemoService } from 'path/to/demo.service.ts';

@Component({
  selector: 'demo',
  templateUrl: './demo.component.html',
  styleUrls: ['./demo.component.css']
})
export class DemoComponent implements OnInit {
  public faDownload = faDownload;
  
  constructor(
    protected demoService: DemoService
  ){}
  
  ngOnInit(): void {
  }
  
  public downloadDemo(): void {
    let url = 'http://localhost:3000/download/file';
    this.demoService.downloadFile(url).subscribe({
      next: (event: any) => {
        if (event.type === HttpEventType.DownloadProgress) {
          // progress notify
          const percentDone = Math.round(100 * event.loaded / event.total);
          console.log(`File is ${percentDone}% downloaded.`);
        } else if (event.type === HttpEventType.Response) {
          // HTTP response finish
          const downloadLink = document.createElement('a');
          downloadLink.href = URL.createObjectURL(event.body); // createObjectURL
          downloadLink.download = 'demo.zip'; // should add, or filename extension not work
          downloadLink.click();
          URL.revokeObjectURL(downloadLink.href); // revoke
        }
      }
    })
  }
}

这里我们采用了跨域来请求接口,读者可前往 【案例】同源策略 - CORS 处理 了解。

同理,我们这里也设置了 responseType ,开启 progress -〉 reportProgress,并设定 responseType: 'blob'。不同的库大同小异,就看开发需要和团队要求来使用。

总结

  • 使用原生 XMLHttpRequest 处理请求,让我们知道文件下载的前后发生了什么;使用 axios@angular/common/http 能让我们更好管理和快速开发
  • axios 也好,@angular/common/http 也罢,大同小异,看团队来使用

关于前端文件下载,我们就讲到这里。后面我们会讲讲文件上传

相关推荐
web小白成长日记6 小时前
企业级 Vue3 + Element Plus 主题定制架构:从“能用”到“好用”的进阶之路
前端·架构
APIshop6 小时前
Python 爬虫获取 item_get_web —— 淘宝商品 SKU、详情图、券后价全流程解析
前端·爬虫·python
风送雨6 小时前
FastMCP 2.0 服务端开发教学文档(下)
服务器·前端·网络·人工智能·python·ai
XTTX1106 小时前
Vue3+Cesium教程(36)--动态设置降雨效果
前端·javascript·vue.js
LYFlied7 小时前
WebGPU与浏览器边缘智能:开启去中心化AI新纪元
前端·人工智能·大模型·去中心化·区块链
Setsuna_F_Seiei7 小时前
2025 年度总结:人生重要阶段的一年
前端·程序员·年终总结
model20058 小时前
alibaba linux3 系统盘网站迁移数据盘
java·服务器·前端
武藤一雄8 小时前
C# 关于多线程如何实现需要注意的问题(持续更新)
windows·后端·microsoft·c#·.net·.netcore·死锁
han_8 小时前
从一道前端面试题,谈 JS 对象存储特点和运算符执行顺序
前端·javascript·面试
aPurpleBerry8 小时前
React 01 目录结构、tsx 语法
前端·react.js