likes
comments
collection
share

前端文件下载(四)

作者站长头像
站长
· 阅读数 12

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

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

axios

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

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

<!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,如下:

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

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

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

// 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'
      }
    );
  }
}

接着我们调用服务:

// 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
        }
      }
    })
  }
}

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

前端文件下载(四)

总结

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

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