截止目前,我们已经分了三个篇幅讲解了前端文件的下载。
本文,我们以文章 前端文件下载(三) 为基础来扩展讲解通过第三方库下载大文件。
我们为什么不对原生的进行封装呢?我们当然可以对原生进行封装,但是有现成成熟的库,我们为什么不用呢?
案例使用的代码来源 前端文件下载(三),开发环境不变,服务端的代码不做变更。
axios 是很受欢迎的 JavaScript
库,是基于 promise
的 HTTP
客户端,适用于浏览器和 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
。
axios
在 react
和 vue
框架开发的时,用的比较频繁。笔者使用的 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
}
}
})
}
}
这里我们采用了跨域来请求接口,读者可前往 【案例】同源策略 - CORS 处理 了解。
同理,我们这里也设置了 responseType
,开启 progress
-〉 reportProgress
,并设定 responseType: 'blob'
。不同的库大同小异,就看开发需要和团队要求来使用。
XMLHttpRequest
处理请求,让我们知道文件下载的前后发生了什么;使用 axios
和 @angular/common/http
能让我们更好管理和快速开发axios
也好,@angular/common/http
也罢,大同小异,看团队来使用关于前端文件下载,我们就讲到这里。后面我们会讲讲文件上传。