在开发鸿蒙HarmonyOS应用时,网络请求功能是必不可少的。axios是一个非常流行的基于Promise的HTTP客户端,适用于浏览器和Node.js环境。本文将介绍如何在鸿蒙HarmonyOS中封装axios库,使其能够支持文件上传,并提供额外的配置选项以满足不同的业务需求。

封装目的

  • 简化网络请求:通过封装,我们可以将常用的HTTP请求操作(如GET、POST等)封装成简洁易用的方法。
  • 统一错误处理:封装后的库可以统一处理HTTP请求中的错误,提供更友好的错误提示和处理逻辑。
  • 支持文件上传:提供对文件上传的支持,包括单个文件和多个文件的上传。

使用的库

axios:一个流行的JavaScript库,用于处理HTTP请求。

封装实现

以下是封装后的AxiosHttpRequest类的实现,支持文件上传:

/*** author:csdn猫哥* qq:534117529* blog:https://blog.csdn.net/yyz_1987*/
//axiosHttp.etsimport axios, {AxiosError,AxiosInstance,AxiosHeaders,AxiosRequestHeaders,AxiosResponse,FormData,AxiosProgressEvent,InternalAxiosRequestConfig
} from "@ohos/axios";interface HttpResponse<T>{data: T;status: number;statusText: string;config: HttpRequestConfig;
}
export type HttpPromise<T> = Promise<HttpResponse<T>>;// 鸿蒙ArkTS文件上传相关接口定义
/**
上传类型支持uri和ArrayBuffer,
uri支持“internal”协议类型和沙箱路径。
"internal://cache/"为必填字段,示例: internal://cache/path/to/file.txt;
沙箱路径示例:cacheDir + '/hello.txt'*/
export interface UploadFile {buffer?: ArrayBuffer;fileName?: string;mimeType?: string;uri?:string;
}export interface FileUploadConfig extends HttpRequestConfig {file?: UploadFile | UploadFile[];fileFieldName?: string; // 文件字段名,默认为 'file'additionalData?: Record<string, any>; // 额外的表单数据onUploadProgress?: (progressEvent: any) => void; // 上传进度回调
}export interface FileInfo {name: string;size: number;type: string;
}
/*** 封装后,不支持传入拦截器* 需要自己定义接口继承 AxiosRequestConfig类型* 从而支持传入拦截器,但拦截器选项应为可选属性* 之后请求实例传入的options为继承了AxiosRequestConfig的自定义类型*/
interface InterceptorHooks {requestInterceptor?: (config: HttpRequestConfig) => Promise<HttpRequestConfig>;requestInterceptorCatch?: (error: any) => any;responseInterceptor?: (response: AxiosResponse) => AxiosResponse | Promise<AxiosResponse>;responseInterceptorCatch?: (error: any) => any;
}// @ts-ignore
interface HttpRequestConfig extends InternalAxiosRequestConfig {showLoading?: boolean; //是否展示请求loadingcheckResultCode?: boolean; //是否检验响应结果码checkLoginState?: boolean; //校验用户登陆状态needJumpToLogin?: boolean; //是否需要跳转到登陆页面interceptorHooks?: InterceptorHooks;//拦截器headers?: AxiosRequestHeaders;errorHandler?: (error: any) => void; //错误处理
}/*** 网络请求构造* 基于axios框架实现*/
export class AxiosHttpRequest {config: HttpRequestConfig;interceptorHooks?: InterceptorHooks;instance: AxiosInstance;constructor(options: HttpRequestConfig) {this.config = options;this.interceptorHooks = options.interceptorHooks;this.instance = axios.create(options);this.setupInterceptor()}setupInterceptor(): void {this.instance.interceptors.request.use(// 这里主要是高版本的axios中设置拦截器的时候里面的Config属性必须是InternalAxiosRequestConfig,// 但是InternalAxiosRequestConfig里面的headers是必传,所以在实现的子类我设置成非必传会报错,加了个忽略注解// @ts-ignorethis.interceptorHooks?.requestInterceptor,this.interceptorHooks?.requestInterceptorCatch,);this.instance.interceptors.response.use(this.interceptorHooks?.responseInterceptor,this.interceptorHooks?.responseInterceptorCatch,);}// 类型参数的作用,T决定AxiosResponse实例中data的类型request<T = any>(config: HttpRequestConfig): HttpPromise<T> {return new Promise<HttpResponse<T>>((resolve, reject) => {this.instance.request<any, HttpResponse<T>>(config).then(res => {resolve(res);}).catch((err) => {// 使用传入的 errorHandler 处理错误const errorHandler = config.errorHandler || errorHandlerDefault;errorHandler(err); if (err) {reject(err);}});});}get<T = any>(config: HttpRequestConfig): HttpPromise<T> {return this.request({ ...config, method: 'GET' });}post<T = any>(config: HttpRequestConfig): HttpPromise<T> {return this.request({ ...config, method: 'POST' });}delete<T = any>(config: HttpRequestConfig): HttpPromise<T> {return this.request({ ...config, method: 'DELETE' });}patch<T = any>(config: HttpRequestConfig): HttpPromise<T> {return this.request({ ...config, method: 'PATCH' });}/*** 上传单个文件或多个文件* @param config 文件上传配置* @returns Promise<HttpResponse<T>>*/uploadFile<T = any>(config: FileUploadConfig): HttpPromise<T> {return new Promise<HttpResponse<T>>((resolve, reject) => {if (!config.file) {reject(new Error('文件不能为空'));return;}const formData = new FormData();const fileFieldName = config.fileFieldName || 'file';// 处理单个或多个文件const files = Array.isArray(config.file) ? config.file : [config.file];files.forEach((file, index) => {const fieldName = Array.isArray(config.file) ? `${fileFieldName}[${index}]` : fileFieldName;// 鸿蒙ArkTS FormData.append 支持第三个参数设置文件名和类型if (file.mimeType) {formData.append(fieldName, file.buffer, {filename: file.fileName,type: file.mimeType});} else if (file.buffer){formData.append(fieldName, file.buffer, {filename: file.fileName});}else if (file.uri){formData.append(fieldName, file.uri);}});// 添加额外的表单数据if (config.additionalData) {Object.keys(config.additionalData).forEach(key => {formData.append(key, config.additionalData![key]);});}const uploadConfig: HttpRequestConfig = {...config,method: 'POST',data: formData,headers: new AxiosHeaders({...config.headers,'Content-Type': 'multipart/form-data'})};// 添加上传进度监听if (config.onUploadProgress) {uploadConfig.onUploadProgress = config.onUploadProgress;}this.request<T>(uploadConfig).then(resolve).catch(reject);});}/*** 上传多个文件* @param config 文件上传配置* @returns Promise<HttpResponse<T>>*/uploadFiles<T = any>(config: FileUploadConfig): HttpPromise<T> {if (!Array.isArray(config.file)) {return Promise.reject(new Error('uploadFiles方法需要传入文件数组'));}return this.uploadFile<T>(config);}/*** 获取文件信息* @param file 文件对象* @returns FileInfo*/getFileInfo(file: UploadFile): FileInfo {return {name: file.fileName,size: file.buffer.byteLength,type: file.mimeType || 'application/octet-stream'};}/*** 验证文件类型* @param file 文件对象* @param allowedTypes 允许的文件类型数组* @returns boolean*/validateFileType(file: UploadFile, allowedTypes: string[]): boolean {const fileType = file.mimeType || 'application/octet-stream';return allowedTypes.includes(fileType);}/*** 验证文件大小* @param file 文件对象* @param maxSize 最大文件大小(字节)* @returns boolean*/validateFileSize(file: UploadFile, maxSize: number): boolean {return file.buffer.byteLength <= maxSize;}/*** 创建文件上传配置* @param url 上传地址* @param file 文件对象* @param options 其他配置选项* @returns FileUploadConfig*/createUploadConfig(url: string,file: UploadFile | UploadFile[],options: Partial<FileUploadConfig> = {}): FileUploadConfig {return {url,file,fileFieldName: 'file',...options};}
}function errorHandlerDefault(error: any) {if (error instanceof AxiosError) {//showToast(error.message)} else if (error != undefined && error.response != undefined && error.response.status) {switch (error.response.status) {// 401: 未登录// 未登录则跳转登录页面,并携带当前页面的路径// 在登录成功后返回当前页面,这一步需要在登录页操作。case 401:break;// 403 token过期// 登录过期对用户进行提示// 清除本地token和清空vuex中token对象// 跳转登录页面case 403://showToast("登录过期,请重新登录")// 清除token// localStorage.removeItem('token');break;// 404请求不存在case 404://showToast("网络请求不存在")break;// 其他错误,直接抛出错误提示default://showToast(error.response.data.message)}}
}
export{AxiosRequestHeaders,AxiosError,AxiosHeaders,AxiosProgressEvent,FormData};
export default AxiosHttpRequest;

使用举例

import fs from '@ohos.file.fs';
import { axiosClient, FileUploadConfig, HttpPromise, UploadFile } from '../../utils/axiosClient';
import { AxiosProgressEvent } from '@nutpi/axios';async function uploadSingleFile() {try {// 创建测试文件并读取为ArrayBufferlet context = getContext() as common.UIAbilityContext;const cacheDir = context.cacheDir;const path = cacheDir + '/test.jpg';// 写入测试文件const file = fs.openSync(path, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);fs.writeSync(file.fd, "这是一个测试文件内容");fs.fsyncSync(file.fd);fs.closeSync(file.fd);// 读取文件为ArrayBufferconst file2 = fs.openSync(path, 0o2);const stat = fs.lstatSync(path);const buffer = new ArrayBuffer(stat.size);fs.readSync(file2.fd, buffer);fs.fsyncSync(file2.fd);fs.closeSync(file2.fd);const uploadFile:UploadFile = {fileName: 'test.jpg',mimeType: 'text/plain',uri:path};const config:FileUploadConfig = axiosClient.createUploadConfig('upload/image',uploadFile,{context:getContext(),onUploadProgress: (progressEvent:AxiosProgressEvent) => {const percentCompleted = Math.round((progressEvent.loaded * 100) / (progressEvent?.total ?? 1));console.log(`上传进度: ${percentCompleted}%`);}});axiosClient.uploadFile<string>(config).then((res) => {//Log.debug(res.data.code)console.log('文件上传成功:');}).catch((err:BusinessError) => {Log.debug("request","err.code:%d",err.code)Log.debug("request",err.message)console.error('文件上传失败:', err);});} catch (error) {}
}

核心功能解析

1. 文件上传接口

  • UploadFile接口:定义了文件上传的数据结构,支持uriArrayBuffer两种方式。
  • FileUploadConfig接口:继承自HttpRequestConfig,增加了对文件和额外表单数据的支持。
  • uploadFile方法:实现文件上传逻辑,支持单个文件和多个文件的上传。
  • uploadFiles方法:专门用于上传文件数组。

2. 文件信息处理

  • getFileInfo方法:获取文件的名称、大小和类型。
  • validateFileType方法:验证文件类型是否在允许的类型列表中。
  • validateFileSize方法:验证文件大小是否超过指定的最大值。

3. 请求拦截器与响应拦截器

  • setupInterceptor方法:设置请求和响应拦截器,以便在请求发送前和响应返回后执行自定义逻辑。

4. 统一错误处理

  • errorHandlerDefault函数:定义了默认的错误处理逻辑。根据响应的状态码提供不同的错误提示。

使用示例

const httpRequest = new AxiosHttpRequest({baseURL: 'https://example.com/api',interceptorHooks: {requestInterceptor: (config) => {console.log('Request Interceptor:', config);return config;},responseInterceptor: (response) => {console.log('Response Interceptor:', response);return response;},},errorHandler: (error) => {console.error('Custom Error Handler:', error);}
});const file: UploadFile = {buffer: new ArrayBuffer(1024),fileName: 'example.txt',mimeType: 'text/plain'
};const config = httpRequest.createUploadConfig('https://example.com/api/upload', file, {additionalData: { description: '这是一个示例文件' },onUploadProgress: (progress) => {console.log('上传进度:', progress);}
});httpRequest.uploadFile(config).then((response) => {console.log('上传成功:', response);
}).catch((error) => {console.error('上传失败:', error);
});

总结

通过封装axios库,我们可以在鸿蒙HarmonyOS中更方便地实现网络请求和文件上传功能。封装后的库提供了统一的错误处理机制和丰富的配置选项,使得我们的网络请求更加高效和灵活。希望本文能帮助大家更好地理解和使用封装后的axios库。

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

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

相关文章

【AI】从零开始的文本分类模型实战:从数据到部署的全流程指南

目录 引言 一、项目背景与目标 二、环境准备 三、数据获取与探索 3.1 数据获取 3.2 数据探索 四、数据预处理 4.1 文本清洗 4.2 分词 4.3 标签编码 4.4 数据集划分 4.5 特征提取 五、模型构建与训练 5.1 逻辑回归模型 5.2 LSTM 模型 六、模型评估 6.1 逻辑回归…

Rust学习心得---特征对象和泛型区别

区别特性泛型&#xff08;静态分发&#xff09;特征对象&#xff08;动态分发&#xff09;决策时机编译时单态化&#xff08;生成具体类型的代码&#xff09;运行时通过vtable查找方法运行性能零运行时开销&#xff08;直接内联调用&#xff09;有额外开销&#xff08;指针跳转…

ESP32-menuconfig(2) -- Application manager

按顺序来说&#xff0c;第二篇本来应该是Security features&#xff0c;但是这块内容应该到小批量才用的到&#xff0c;而一些爱好者可能永远都不会修改这块&#xff0c;所以先看看更常用Application manager&#xff0c;这部分内容也比较少。 Application managerCONFIG_APP_C…

ArgoCD 与 GitOps:K8S 原生持续部署的实操指南

容器技术的爆发让 Kubernetes&#xff08;K8s&#xff09;成为了「云原生时代的操作系统」—— 它能高效编排成千上万的容器&#xff0c;解决弹性伸缩、资源调度等核心问题。但随着企业应用规模扩大&#xff0c;K8s 的「部署与管理」逐渐暴露新的挑战&#xff1a; 多环境&…

Day36--动态规划--1049. 最后一块石头的重量 II,494. 目标和,474. 一和零

Day36–动态规划–1049. 最后一块石头的重量 II&#xff0c;494. 目标和&#xff0c;474. 一和零 遇到难题&#xff0c;思考超过20分钟没有思路的&#xff0c;要跳过&#xff01;不然时间效率太低了。 **看题解同理&#xff0c;看20分钟看不懂的&#xff0c;也要跳过&#xff0…

前端开发技术深度总结报告

前端开发技术深度总结报告 &#x1f4cb; 项目背景 基于 Vue 3 TypeScript Element Plus 的企业级产品管理系统&#xff0c;重点解决产品表单的数据缓存、页面导航、用户体验等核心问题。&#xfffd;&#xfffd; 遇到的问题及解决方案 1. 浏览器控制台错误处理 问题: 大量第…

Linux 单机部署 Kafka 详细教程(CentOS 7+)

系列博客专栏&#xff1a; SpringBoot与微服务实践系列博客Java互联网高级培训教程 一、环境准备 1. 操作系统要求 Kafka 可以在多种 Linux 发行版上运行&#xff0c;本文以 CentOS 7 为例&#xff0c;其他发行版步骤类似&#xff0c;只需调整包管理命令。 2. Java 环境要…

解析工业机器视觉中的飞拍技术

在工业机器视觉的领域&#xff0c;"飞拍"这个术语时常被提起&#xff0c;尤其是在高速检测和动态捕捉的场景中。但你真的了解飞拍是什么吗&#xff1f;它到底如何工作&#xff0c;能为工业应用带来哪些突破性改进呢&#xff1f;让我们一起来解密。1. 飞拍的核心概念 …

[特殊字符]企业游学 | 探秘字节,解锁AI科技新密码

宝子们&#xff0c;想知道全球科技巨头字节跳动的成功秘籍吗&#xff1f;一场企业游学&#xff0c;带你深入字节跳动创新基地&#xff0c;探索AI新科技&#xff0c;揭开规模化增长背后的神秘面纱✨字节跳动&#xff1a;全球经济价值的创造者字节跳动可太牛啦&#xff01;TikTok…

主流大数据框架深度解析:从介绍到选型实战

主流大数据框架深度解析:从介绍到选型实战 在数据驱动的时代,选择合适的大数据处理框架是构建高效、可靠数据平台的关键。 深入剖析 Hadoop MapReduce、Apache Spark、Apache Flink 和 Kafka Streams 四大主流框架,从框架介绍、具体使用场景、优缺点、选择建议到实际案例,…

座舱HMI软件开发架构:核心功能与案例解析

随着智能座舱的持续演进&#xff0c;HMI&#xff08;Human Machine Interface&#xff0c;人与机器交互界面&#xff09;系统已从单一的显示控制器演变为集多屏联动、多模态交互、车载服务集成于一体的智能系统&#xff0c;需要一个多系统、多设备协同运行的复杂架构来支撑。本…

把“思考”塞进 1 KB:我用纯 C 语言给单片机手搓了一个微型 Transformer 推理引擎

标签&#xff1a;TinyML、Transformer、单片机、Cortex-M、量化、KV-Cache、裸机编程 ---- 1. 为什么要在 64 KB SRAM 的 MCU 上跑 Transformer&#xff1f; 2024 年以前&#xff0c;TinyML ≈ CNN CMSIS-NN&#xff0c;做语音唤醒或简单分类就到头了。 但产品同事突然拍脑袋&…

什么是CLI?

什么是CLI&#xff1f;CLI&#xff08;Command Line Interface&#xff09;是命令行界面的缩写&#xff0c;是一种通过文本命令与计算机程序交互的方式。通俗比喻CLI就像是一个"智能助手"&#xff1a;你输入命令&#xff0c;它执行任务就像和机器人对话一样&#xff…

mysql基本sql语句大全

十分想念顺店杂可。。。以下是 MySQL 中常用的基本 SQL 语句大全&#xff0c;按功能分类整理&#xff0c;包含语法和示例&#xff0c;方便参考使用&#xff1a;一、数据库操作&#xff08;DDL&#xff09;用于创建、删除、切换数据库。创建数据库-- 基本语法 CREATE DATABASE […

构建响应式在线客服聊天系统的前端实践 Vue3+ElementUI + CSS3

构建响应式客服聊天系统的前端实践在当今数字化时代&#xff0c;客服系统已成为企业与客户沟通的重要桥梁。一个优秀的在线客服系统不仅需要功能完善&#xff0c;还需要在各种设备上都能提供良好的用户体验。本文将介绍如何构建一个响应式的客服聊天界面&#xff0c;确保在桌面…

C语言memcpy函数详解:高效内存复制的实用工具

目录1. memcpy函数是什么&#xff1f;函数原型2. memcpy函数的用法运行结果&#xff1a;代码解析3. memcpy函数的注意事项3.1 内存区域不重叠3.2 缓冲区大小管理3.3 指针有效性3.4 性能优势3.5 平台兼容性4. 实际应用场景4.1 数组复制4.2 动态内存复制4.3 结构体复制4.4 缓冲区…

多级缓存架构:新品咖啡上线引发的数据库压力风暴与高并发实战化解方案

一、背景&#xff1a;新品咖啡风暴与数据库之痛想象一下&#xff1a;某知名咖啡品牌推出限量版“星空冷萃”&#xff0c;通过社交媒体引爆流量。上午10点开售瞬间&#xff0c;APP与网站涌入数十万用户&#xff0c;商品详情页、库存查询请求如海啸般涌向后台。传统架构下&#x…

888. 公平的糖果交换

目录 题目链接&#xff1a; 题目&#xff1a; 解题思路&#xff1a; 代码&#xff1a; 总结&#xff1a; 题目链接&#xff1a; 888. 公平的糖果交换 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 解题思路&#xff1a; 前一个数组和sumA,后一个数组sumB,然…

Day01 项目概述,环境搭建

软件开发整体介绍 软件开发流程 需求分析&#xff1a;需求规格说明书、产品原型 设计&#xff1a;UI 设计、数据库设计&#xff0c;接口设计 编码&#xff1a;项目代码、单元测试 测试&#xff1a;测试用例、测试报告 上线运维&#xff1a;软件环境安装、配置 角色分工 项…

Perl Socket 编程

Perl Socket 编程 引言 Perl 语言作为一种强大的脚本语言,在系统管理和网络编程领域有着广泛的应用。Socket 编程是网络编程的核心,它允许程序在网络中进行数据传输。本文将详细介绍 Perl 语言中的 Socket 编程,包括 Socket 的概念、创建、通信以及一些高级应用。 Socket…