Angular封装HttpClient文件下载

Angular HttpClient 文件下载

  • 前言
  • HttpRequest.ts
  • demo
  • 后端接口koa2示例
  • 功能优化实现下载进度监控

前言

使用Angular框架开发工作中,实现文件下载业务时,我们可以使用Angular自带的HttpClient。下面我们就封装一下HttpClient实现文件下载,当接口返回文件流正常下载,后端返回json错误信息时,前端可以获取到错误信息进行toast提示

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'] = headeroptions['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 = blobUrltempLink.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] : nulllet 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)}})}
}

demo

constructor(private router: Router,private service: AccountIdentifyService,private confirmationService: ConfirmationService,private toast: Toast,private req: HttpRequest,) { }export() {this.exportLoading = truethis.req.downFileBlobPromise(`koa2/download/2.txt`, param).then((res:any) => {this.exportLoading = falseif(res){this.toast.error(res.mess)}else{this.toast.success("下载成功")}}).catch(err => {this.exportLoading = falsethis.toast.showError("下载异常")})}

后端接口koa2示例

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.codectx.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
})

文件util

const fs = require('fs');
const path = require('path');function saveFile(file) {const reader = fs.createReadStream(file.path);const fileExtension = path.extname(file.name);const uniqueFileName = `${Date.now()}${fileExtension}`;const writer = fs.createWriteStream(path.join(__dirname, 'uploads', uniqueFileName));reader.pipe(writer);return uniqueFileName;
}function getFileStream(filename) {return fs.createReadStream(path.join(__dirname, '../uploads', filename));
}/*** [读文件]* @param  {String} range        [数据起始位]* @param  {String} filePath     [文件路径]* @param  {Number} chunkSize    [每次请求碎片大小 (900kb 左右)]*/
function readFile(range, filePath, chunkSize = 499999 * 2) {//mime类型const mime = {"css": "text/css","gif": "image/gif","html": "text/html","ico": "image/x-icon","jpeg": "image/jpeg","jpg": "image/jpeg","js": "text/javascript","json": "application/json","pdf": "application/pdf","png": "image/png","svg": "image/svg+xml","swf": "application/x-shockwave-flash","tiff": "image/tiff","txt": "text/plain","mp3": "audio/mp3","wav": "audio/x-wav","wma": "audio/x-ms-wma","wmv": "video/x-ms-wmv","xml": "text/xml","mp4": "video/mp4"};// 获取后缀名let ext = path.extname(filePath);ext = ext ? ext.slice(1) : 'unknown';//未知的类型一律用"text/plain"类型let contentType = mime[ext.toLowerCase()];//建立流对象,读文件let stat = fs.statSync(filePath)let fileSize = stat.size;let head = {code: 200,head: {'Content-Length': fileSize,'content-type': contentType,}};console.log("range: ",range);if (range) {// 大文件分片let parts = range.replace(/bytes=/, "").split("-");let start = parseInt(parts[0], 10);let end = parts[1] ? parseInt(parts[1], 10) : start + chunkSize;end = end > fileSize - 1 ? fileSize - 1 : end;chunkSize = (end - start) + 1;head = {code: 206,filePath,start,end,head: {'Content-Range': `bytes ${start}-${end}/${fileSize}`,'content-type': contentType,'Content-Length': chunkSize,'Accept-Ranges': 'bytes'}}}return head;
}module.exports = {saveFile,getFileStream,readFile
}

功能优化实现下载进度监控

import { HttpClient, HttpEvent, HttpEventType } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { environment } from "@env/environment";
import { filter, map, tap } from "rxjs/operators";
import { TlMessageService } from "./message.service";@Injectable({providedIn: 'root'
})
export class FileHttpRequest {constructor(private http: HttpClient, private message: TlMessageService) {}download(url: string, params = {}, progress = false) {const urlPrefix = environment.urlPrefix;if(url.startsWith('./')){url = url.substring(1)}if(!url.startsWith('/')){url = "/" + url}const ignore = ["/TsmAas","/portal","/asset","/koa2"]if(!ignore.includes("/" + url.split("/")[1])){url = url.startsWith('/') ? (urlPrefix + url) : (urlPrefix + '/' + url)}let options: any = {}let header: { [name: string]: string } = {}header['Content-Type'] = 'application/x-www-form-urlencoded'header['Accept'] = '*/*'options['headers'] = headeroptions['responseType'] = "arraybuffer"options['observe'] = "events"options["reportProgress"] = trueoptions = Object.assign(options, { params })console.log(options);return new Promise((resolve, reject) => {this.http.get(url, options).pipe(map(event => this.getEventMessage(event, progress)),filter(f => f != null)).subscribe(async res => {const txt = await this.getFileFromStream(res)resolve(txt)}, err => {console.log(err);reject(err)})})}status = falseprivate getEventMessage(event: any, progress = false): ArrayBuffer {switch (event.type) {case HttpEventType.ResponseHeader:if (event.status !== 200) {this.status = false} else {if (progress) {this.message.send({type: "downloadStart",content: ""})}this.status = true}return nullcase HttpEventType.DownloadProgress:const percentDone = Math.round(100 * event.loaded / event.total);console.log(event, percentDone);if (progress && this.status) {if (percentDone >= 100) {this.message.send({type: "downloading",content: "100"})setTimeout(() => {this.message.send({type: "downloadEnd",content: "100"})}, 1500);} else {this.message.send({type: "downloading",content: percentDone + "",})}}return null;case HttpEventType.Response:return event;default:return null;}}private getFileName(response: any) {const encode = response.headers.get('content-type')?.match(/charset=(.*)/) ? response.headers.get('content-type').match(/charset=(.*)/)[1] : nulllet 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.resultconst 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 = blobUrltempLink.setAttribute('download', fileName)document.body.appendChild(tempLink)tempLink.click()document.body.removeChild(tempLink)window.URL.revokeObjectURL(blobUrl)return null}}
}

TlMessageService 消息订阅,传递文件下载进度

import { Injectable } from "@angular/core";
import { BehaviorSubject, Observable, Subject } from "rxjs";export type TlMessage = {type: "success" | "error" | "warn" | "info" | "upload" | "downloadStart" | "downloading" | "downloadEnd",content: string
}@Injectable({providedIn: 'root',})
export class TlMessageService {//private subject = new Subject<any>();private subject = new BehaviorSubject<TlMessage>({type:"info",content:""});send(message: TlMessage) {this.subject.next(message);}get(): Observable<TlMessage> {return this.subject.asObservable();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/647946.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端网络请求之JavaScript XHR、Fetch、Axios

一、JavaScript XHR、Fetch AJAX&#xff1a;一种在无需重新加载整个网页的情况下&#xff0c;能够更新部分网页的技术。在后台与服务器进行少量数据交换&#xff0c;Ajax 可以使网页实现异步更新。在不重新加载整个网页的情况下&#xff0c;对网页的某部分进行更新 Fetch&…

主流电商平台:item_get-通过商品ID取商品详情,主图,sku

随着全球化的加速和互联网技术的不断发展&#xff0c;跨境电商已经成为了全球商业的重要组成部分。在这个环境下&#xff0c;如何有效地获取商品详情成为了关键的问题。本文将探讨一种基于商品ID获取商品详情的跨境电商创新方式&#xff0c;即item_get接口&#xff0c;以及其潜…

Vue3-在HTML标签、组件标签上使用v-model

本篇为Vue3学习中遇到的v-model相关的记录&#xff0c;如有问题欢迎指正 一、在标签上使用v-model v-model通常在input、select等标签上来实现数据双向绑定 <input type"text" v-model"username"> 原理&#xff1a;v-model通过给标签value赋值来实…

Windows10上使Git Bash支持rsync命令操作步骤

rsync命令是linux上常用的工具之一&#xff0c;用于远程以及本地系统中拷贝/同步文件和文件夹。 Windows Git Bash默认并不支持rsync&#xff0c;如下图所示&#xff1a; 使Git Bash支持rsync命令操作步骤&#xff1a; 1.从https://repo.msys2.org/msys/x86_64/ 下…

一、MongoDB、express的安装和基本使用

数据库【Sqlite3、MongoDB、Mysql】简介&小记 Sqlite3&#xff1a; SQLite3是一个轻量级的数据库系统&#xff0c;它被设计成嵌入式数据库。这意味着它是一个包含在应用程序中的数据库&#xff0c;而不是独立运行的系统服务。适用场景&#xff1a;如小型工具、游戏、本地…

算力总规模位列全球第二!中创:助推数据中心建设

近日&#xff0c;国新办举办新闻发布会&#xff0c;介绍2023年工业和信息化发展情况。算力、数据中心、云计算等与数字化转型、能源利用息息相关&#xff0c;被多次提及。 工业和信息化部新闻发言人、运行监测协调局局长陶青表示&#xff1a; 数字基础设施支撑有力&#xff0c;…

力扣算法-Day18

18.四数之和 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元组元素一一对应&#xff0c;则认为两个四元组重复&#xff09;&#xff1…

关于数据库被勒索如何解决?

今天发现数据库被删&#xff0c;原因是勒索病毒&#xff0c;这期文章来说明下: **使用binlog日志恢复**&#xff1a; - **确认binlog状态**&#xff1a;检查MySQL服务器是否开启了binlog日志功能。如果服务器启用了binlog&#xff0c;那么在数据被篡改之前的操作都有可能被记…

golang 根据URL获取文件名

只有一个文件地址&#xff0c;但是没有文件名称&#xff0c;文件地址&#xff1a;http://XXXXXXX/getfile.aspx?fileid999 但是系统需要把文件名称也写入到数据库 可以根据 resp.Header["Content-Disposition"] 获取文件名 resp.Header["Content-Disposition&q…

继承和原型链

基于原型链的继承示例 // 继承方法 const parent { value: 2, method() { console.log(this); return this.value 1 } } console.log(parent.method());//3 不难理解 const child { __proto__: parent, } console.log(child.method());//3 this 指向child 但是属性中没有v…

亚马逊鲲鹏系统:批量注册买家号的新利器

近年来&#xff0c;随着电商市场的迅速发展&#xff0c;亚马逊作为全球最大的在线零售平台之一&#xff0c;其买家号的需求也日益增长。而亚马逊鲲鹏系统是一个能够批量全自动注册亚马逊买家号的系统。而对于全自动批量注册&#xff0c;账号资料方面&#xff0c;也有一定的要求…

vue3 + antd 封装动态表单组件(二)

传送带&#xff1a; vue3 antd 封装动态表单组件&#xff08;一&#xff09; 前置条件&#xff1a; vue版本 v3.3.11 ant-design-vue版本 v4.1.1 vue3 antd 封装动态表单组件&#xff08;一&#xff09;是基础版本&#xff0c;但是并不好用&#xff0c; 因为需要配置很多表…

【QT+QGIS跨平台编译】之八:【zstd+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、zstd介绍二、文件下载三、文件分析四、pro文件五、编译实践一、zstd介绍 ZSTD(Zstandard的缩写),是一种快速压缩算法,提供了高压缩比功能。ZSTD还为小数据提供了一种特殊的模式,称为字典压缩。ZSTD库使用BSD许可证作为开放源码软件提供的。它的格式是稳定的,…

【java题解】题目 1779: 你的第一个程序;题目 1779: 你的第一个程序;题目 1173: 计算球体积

目录 题目 1779: 你的第一个程序 题目描述 输入格式 输出格式 样例输入 样例输出 题解 题目 1173: 计算球体积 题目描述 输入格式 输出格式 样例输入 样例输出 题解 题目 1267: AB Problem 题目描述 输入格式 输出格式 样例输入 样例输出 题解 从今天开始…

CANoe的python API,使用python控制CANoe工具

CANoe是一款用于开发、测试和仿真汽车通信系统的工具&#xff0c;它提供了Python API&#xff0c;使得开发者可以使用Python脚本来控制CANoe工具的各项功能。 CANoe的Python API提供了丰富的功能&#xff0c;可以用于配置网络和节点、发送和接收消息、执行测量和仿真等。 注意…

TS学习笔记十:装饰器及三斜线指令

本节介绍TS中的装饰器和三斜线指令&#xff0c; 装饰器&#xff08;Decorators&#xff09;为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式。   三斜线指令是包含单个XML标签的单行注释。 注释的内容会做为编译器指令使用。 讲解视频 20240116-205052装饰器…

基于一款热门大屏可视化设计器使用教程

乐吾乐大屏可视化设计器是一个用于创建和定制大屏幕数据可视化展示的工具&#xff0c;支持零代码实现物联网、工业智能制造等领域的可视化大屏、触摸屏端UI以及工控可视化的解决方案。同时也是一个Web组态工具&#xff0c;支持2D、3D等多种形式&#xff0c;用于构建具有实时数据…

华硕ASUS K43SD笔记本安装win7X64(ventoy为入口以支撑一盘多系统);友善之臂mini2440开发板学习

记录 老爷机 白色 华硕 K43SD 笔记本 安装 win7X64 1. MBR样式常规安装win7X64Sp1 (华硕 K43SD 安装 win7X64 ) 老爷机 白色 华硕 K43SD 笔记本 安装 win7X64 (常规安装) 设置: 禁用UEFI 启用AHCI ventoy制作MBR(非UEFI)方式的启动U盘 U盘中放cn_windows_7_ultimate_wit…

TCP 三次握手以及滑动窗口

TCP 三次握手 简介&#xff1a; TCP 是一种面向连接的单播协议&#xff0c;在发送数据前&#xff0c;通信双方必须在彼此间建立一条连接。所谓的 “ 连接” &#xff0c;其实是客户端和服务器的内存里保存的一份关于对方的信息&#xff0c;如 IP 地址、端口号等。 TCP 可以…

【机器学习300问】19、深度学习和机器学习什么关系?

之前的文章都聚焦在传统的机器学习上&#xff0c;作为入门&#xff0c;学了许多机器学习的基础。往后的文章我会穿插着机器学习和深度学习的内容进行&#xff0c;所有有必要在这里先说下两者的关系。 一、从范围上讲 深度学习和机器学习都是人工智能的一个子领域&#xff0c;它…