使用Angular框架开发工作中,实现文件下载业务时,我们可以使用ANgular自带的HttpClient。下面我们就封装一下HttpClient实现文件下载,当接口返回文件流正常下载,返回json信息时也能返回,让使用者自行处理。
HttpRequest.ts
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams, HttpUrlEncodingCodec } from "@angular/common/http"; import { Injectable, Component } from "@angular/core"; import { throwError } from "rxjs"; import { catchError, map } from "rxjs/operators"; import { environment } from "src/environments/environment"; @Injectable({ providedIn: 'root' }) export class HttpRequest{ public downFileBlobPromise(url: string, data = {}) { let options: any = {} let header: { [name: string]: string } = {} header['Content-Type'] = 'application/x-www-form-urlencoded' header['Accept'] = '*/*' options['headers'] = header options['responseType'] = "blob" options['observe'] = "response" let obj = Object.assign({}, options, { params: data }) return new Promise((resolve, reject) => { this.http.get(url, obj).subscribe(async res => { const txt = await this.convertRes2Blob(res) resolve(txt) }, err => { reject(err) }) }) } private async convertRes2Blob(response: any) { if (!response.headers.has("content-disposition")) { const blob = new Blob([response.body], { type: 'application/octet-stream' }) const resultJson = await this.readBlob(blob) return resultJson } const fileName = this.getFileName(response) const blob = new Blob([response.body], { type: 'application/octet-stream' }) if (typeof window.navigator.msSaveBlob !== 'undefined') { window.navigator.msSaveBlob(blob, fileName) return null } else { const blobUrl = window.URL.createObjectURL(blob) const tempLink = document.createElement('a') tempLink.style.display = 'none' tempLink.href = blobUrl tempLink.setAttribute('download', fileName) document.body.appendChild(tempLink) tempLink.click() document.body.removeChild(tempLink) window.URL.revokeObjectURL(blobUrl) return null } } private getFileName(response: any) { const encode = response.headers.get('content-type')?.match(/charset=(.*)/) ? response.headers.get('content-type').match(/charset=(.*)/)[1] : null let fileName: string = response.headers.get('content-disposition').match(/filename=(.*)/)[1].replaceAll("\"", "") if (encode && encode == 'ISO8859-1') { const fn = escape(fileName) fileName = decodeURI(escape(fileName)).replace(new RegExp("%3A", "gm"), ":") } else { fileName = decodeURI(fileName) } return fileName } private readBlob(blob:Blob){ const f = new FileReader() f.readAsText(blob, "UTF-8") return new Promise((resolve,reject) => { f.onload = (evt: any) => { const re = evt.target.result const result = JSON.parse(re) resolve(result) } f.onerror = (evt:any) => { reject(evt) } }) } }
使用
constructor( private router: Router, private service: AccountIdentifyService, private confirmationService: ConfirmationService, private toast: Toast, private req: HttpRequest, ) { } export() { this.exportLoading = true this.req.downFileBlobPromise(`koa2/download/2.txt`, param).then((res:any) => { this.exportLoading = false if(res){ this.toast.error(res.mess) }else{ this.toast.success("下载成功") } }).catch(err => { this.exportLoading = false this.toast.showError("下载异常") }) }
后端文件下载接口示例,使用koa2写的demo
/** 文件下载 */ router.get("/download/:filename",async function(ctx,next){ const filename = ctx.params.filename; if(filename != "1.txt"){ setTimeout(() => { ctx.body = { resultStat: "1", mess:"文件不存在", }; return }, 5000); } //request里面切出标识符字符串 let requestUrl = ctx.request.originalUrl; //获取资源文件的绝对路径 let filePath = path.resolve(__dirname + "/uploads/" + decodeURI(filename)); console.log(filePath); let resHred = readFile(ctx.headers.range, filePath); ctx.status = resHred.code ctx.set(resHred.head); ctx.set('Content-Disposition', `attachment; filename=${encodeURIComponent(filename)}`); ctx.set('Content-Type', 'application/octet-stream'); let stream = fs.createReadStream(filePath, resHred.code == 200 ? {} : { start: resHred.start, end: resHred.end }); stream.pipe(ctx.res); // //也可使用这种方式。 // stream.on('data', e => ctx.res.write(e)); // // 接收完毕 // stream.on('end', e => ctx.res.end()); ctx.respond = false; return })
进一步改善我们封装类实现文件下载进度监控
tsimport { HttpClient, HttpEvent, HttpEventType } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { filter, map } from "rxjs/operators";
@Injectable({
providedIn: 'root'
})
export class FileHttpRequest {
constructor(private http: HttpClient) {
}
download(url: string, params: any) {
let options: any = {}
let header: { [name: string]: string } = {}
header['Content-Type'] = 'application/x-www-form-urlencoded'
header['Accept'] = '*/*'
options['headers'] = header
options['responseType'] = "arraybuffer"
options['observe'] = "events"
options["reportProgress"] = true
options = Object.assign(options, { params })
console.log(options);
return new Promise((resolve, reject) => {
this.http.get(url, options).pipe(
map(event => this.getEventMessage(event)),
filter(f => f != null)
).subscribe(async res => {
const txt = await this.getFileFromStream(res)
resolve(txt)
}, err => {
console.log(err);
reject(err)
})
})
}
private getEventMessage(event: any): ArrayBuffer {
console.log(event);
switch (event.type) {
case HttpEventType.DownloadProgress:
// 计算出下载进度
const percentDone = Math.round(100 * event.loaded / event.total);
console.log(event, percentDone);
return null;
case HttpEventType.Response:
return event;
default:
return null;
}
}
private getFileName(response: any) {
debugger
const encode = response.headers.get('content-type')?.match(/charset=(.*)/) ? response.headers.get('content-type').match(/charset=(.*)/)[1] : null
let fileName: string = response.headers.get('content-disposition').match(/filename=(.*)/)[1].replaceAll("\"", "")
if (encode && encode == 'ISO8859-1') {
const fn = escape(fileName)
fileName = decodeURI(escape(fileName)).replace(new RegExp("%3A", "gm"), ":")
} else {
fileName = decodeURI(fileName)
}
return fileName
}
private readBlob(blob:Blob){
const f = new FileReader()
f.readAsText(blob, "UTF-8")
return new Promise((resolve,reject) => {
f.onload = (evt: any) => {
const re = evt.target.result
const result = JSON.parse(re)
resolve(result)
}
f.onerror = (evt:any) => {
reject(evt)
}
})
}
private async getFileFromStream(response: any){
if (!response.headers.has("content-disposition")) {
const blob = new Blob([response.body], { type: 'application/octet-stream' })
const resultJson = await this.readBlob(blob)
return resultJson
}
const fileName = this.getFileName(response)
const blob = new Blob([response.body], { type: 'application/octet-stream' })
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(blob, fileName)
return null
} else {
const blobUrl = window.URL.createObjectURL(blob)
const tempLink = document.createElement('a')
tempLink.style.display = 'none'
tempLink.href = blobUrl
tempLink.setAttribute('download', fileName)
document.body.appendChild(tempLink)
tempLink.click()
document.body.removeChild(tempLink)
window.URL.revokeObjectURL(blobUrl)
return null
}
}
}


本文作者:千寻
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!