当前位置: 首页 > news >正文

uniApp上传文件踩坑日记

       最近在做移动端app,开始接触uniapp。想着直接用PC端的前后端API去做文件上传,但是uniapp的底层把请求拆成了普通请求和文件上传请求,所以不能用一个axios去做所有请求的处理,拆成uni.request和uni.uploadFile去分别处理两种情况。

H5

       所以要封装两个请求接口,一个request.js普通请求封装和PC大致相同。另一个是文件请求封装 ,用upload.js去封装uni.uploadFile做请求拦截器

import store from "@/store";
import config from "@/config";
import { getToken } from "@/utils/auth";
import errorCode from "@/utils/errorCode";
import { toast, showConfirm, tansParams } from "@/utils/common";let timeout = 10000;
const baseUrl = config.baseUrl;const upload = (config) => {// 是否需要设置 tokenconst isToken = (config.headers || {}).isToken === false;config.header = config.header || {};if (getToken() && !isToken) {config.header["Authorization"] = getToken();}// get请求映射params参数if (config.params) {let url = config.url + "?" + tansParams(config.params);url = url.slice(0, -1);config.url = url;}return new Promise((resolve, reject) => {uni.uploadFile({timeout: config.timeout || timeout,url: baseUrl + config.url,filePath: config.filePath,name: config.name || "file",header: config.header,formData: config.formData,success: (res) => {let result = JSON.parse(res.data);const code = result.code || 200;const msg = errorCode[code] || result.msg || errorCode["default"];if (code === 200) {resolve(result);} else if (code == 401) {showConfirm("登录状态已过期,您可以继续留在该页面,或者重新登录?").then((res) => {if (res.confirm) {store.dispatch("LogOut").then((res) => {uni.reLaunch({ url: "/pages/login/login" });});}});reject("无效的会话,或者会话已过期,请重新登录。");} else if (code === 500) {toast(msg);reject("500");} else if (code !== 200) {toast(msg);reject(code);}},fail: (error) => {let { message } = error;if (message == "Network Error") {message = "后端接口连接异常";} else if (message.includes("timeout")) {message = "系统接口请求超时";} else if (message.includes("Request failed with status code")) {message = "系统接口" + message.substr(message.length - 3) + "异常";}toast(message);reject(error);},});});
};export default upload;

然后修改之前的前端API为

import upload from "@/utils/upload";export function fileUpload(data) {return upload({url: "/common/app/upload/" + data.fileType,name: data.name,filePath: data.filePath,});
}

        这里api解释一下,因为现在uniapp上传文件接口的差别,是创建一个临时url,我们去上传这个临时文件url到后端去保存的。

        所以前后端的接口都需要做调整,后端是拿到前端的文件然后转存到指定位置,再传回文件的路径。

后端API

	@PostMapping("/app/upload/{fileType}")public R appUpload(MultipartFile file,@PathVariable String fileType) {// 检查文件是否存在if (file.isEmpty()) {log.info("文件不存在,请检查路径:");throw new CustomException("文件上传失败");}String fileFolder = basePath + File.separator + "file";if (File.separator.equals("\\")) {// 本地windows测试fileFolder = "C:\\Users\\A\\Desktop\\rm-mes\\data\\file";}File imgFolder = new File(fileFolder);if (!imgFolder.exists()) {imgFolder.mkdirs();log.info("创建指定目录文件夹:" + fileFolder);}// 获取文件大小(字节)long fileSize = file.getSize();// 转换为MBdouble fileSizeInMB = fileSize / (1024.0 * 1024.0); // 使用1024.0确保结果为浮点数// UUID重新生成文件,防止重复覆盖String fileName = UUID.randomUUID() + "." + fileType;// xxx.jpg  xxx.mp4String filePath = fileFolder + File.separator + fileName;try {// 文件转存位置file.transferTo(new File(filePath));} catch (IOException e) {e.printStackTrace();}return R.ok().data("filePath", filePath).data("fileType", fileType).data("fileSize", fileSizeInMB);}

       文件的后缀是通过前端传过来的类型去保存的,按照之前PC端动态获取文件后缀的方式会失败,导致

然后到前端的签名图片上传

 async uploadSignature() {const blob = this.base64ToBlob(this.signature);const filePath = URL.createObjectURL(blob); // 创建一个临时的文件路径let data = { name: "file", filePath: filePath, fileType: "png" };const res = await fileUpload(data);this.editForm.responsibleSign = res.data.filePath;},base64ToBlob(b64) {const byteCharacters = atob(b64.replace("data:image/png;base64,", "")); // 把前缀去掉const byteNumbers = new Array(byteCharacters.length);for (let i = 0; i < byteCharacters.length; i++) {byteNumbers[i] = byteCharacters.charCodeAt(i);}const byteArray = new Uint8Array(byteNumbers);const blob = new Blob([byteArray], { type: "image/jpeg" });return blob;},

       在我们签完名后,一般会拿到签名的base64编码,现在要根据接口的需求,创建一个临时URL放到请求中,所以第一步先调用base64ToBlob转成Blob后,再创建临时地址。最后创建请求体的数据,把文件的路径和文件类型即可,最后调用接口保存到服务器中~

uniApp

        如果不是H5的环境下上传文件会比较麻烦,需要考虑是app还是哪个第三方的小程序。因为在app中创建临时文件需要获取当前运行环境,还要获取一些设备权限。

         所以想要避免这种前端差异和麻烦,我们把后端文件上传接口,改成接收base64编码,在后端去转码成文件保存到服务器中。那么现在我们在前端需要写一个base64编码转换的方法。如果能直接拿到则跳过,直接调用请求api即可。

        现在不在H5环境下,签名会拿到一个图片的临时地址,但是这个地址不能通过第一种创建Blob方式临时http的形式传到后端。因为app不支持http的创建。下面写个方法去转码

export function pathToBase64(path) {return new Promise(function (resolve, reject) {// appif (typeof plus === "object") {plus.io.resolveLocalFileSystemURL(path,function (entry) {entry.file(function (file) {var fileReader = new plus.io.FileReader();fileReader.onload = function (evt) {resolve(evt.target.result);};fileReader.onerror = function (error) {reject(error);};fileReader.readAsDataURL(file);},function (error) {reject(error);});},function (error) {reject(error);});return;}// 微信小程序if (typeof wx === "object" && wx.canIUse("getFileSystemManager")) {wx.getFileSystemManager().readFile({filePath: path,encoding: "base64",success: function (res) {resolve("data:image/png;base64," + res.data);},fail: function (error) {reject(error);},});return;}reject(new Error("not support"));});
}

       现在我们拿到base64编码,那么其实就是一个字符串,我们不需要使用uni.uploadFile(),直接用uni.request()去上传,当做普通请求去处理,放到post请求的请求体中即可。

修改前端api

import upload from "@/utils/upload";
import request from "@/utils/request";export function fileUpload(data) {return request({url: "/common/app/upload",method: "post",data: data,});
}

修改后端api(用png图片做示例)

	@PostMapping("/app/upload")public R uploadBase64(@RequestBody FlierInfo flierInfo) {String base64 = flierInfo.getBase64();// 确定文件类型String fileType = "png";// 去掉前缀String base64Image = base64.replace("data:image/png;base64,", "");String fileFolder = basePath + File.separator + "file";if (File.separator.equals("\\")) {// 本地windows测试fileFolder = "C:\\Users\\A\\Desktop\\rm-mes\\data\\file";}File imgFolder = new File(fileFolder);if (!imgFolder.exists()) {imgFolder.mkdirs();log.info("创建指定目录文件夹:" + fileFolder);}// UUID重新生成文件,防止重复覆盖String fileName = UUID.randomUUID() + "." + fileType;// xxx.jpg  xxx.mp4String filePath = fileFolder + File.separator + fileName;Base64Util.GenerateImage(base64Image, filePath); // 转码成图片保存到指定路径return R.ok().data("filePath", filePath).data("fileType", fileType).data("fileSize", "");}

前端调用

import { fileUpload } from "@/api/mes/system/common";
import { pathToBase64 } from "@/utils/pathToBase64";async submit(ref) {// await this.uploadSignature();pathToBase64(this.signature).then(async (base64) => {// 保存base64图片await this.uploadSign(base64);}).catch((error) => {console.error(error);});},
async uploadSign(base64) {let data = {base64: base64,};const res = await fileUpload(data);this.editForm.responsibleSign = res.data.filePath;},

       H5和app的处理文件上传的记录到此,第一种方法在H5环境下拿到的base64编码其实可以直接发送到第二种情况的后端接口去做保存,万变不离其宗。

相关文章:

uniApp上传文件踩坑日记

最近在做移动端app&#xff0c;开始接触uniapp。想着直接用PC端的前后端API去做文件上传&#xff0c;但是uniapp的底层把请求拆成了普通请求和文件上传请求&#xff0c;所以不能用一个axios去做所有请求的处理&#xff0c;拆成uni.request和uni.uploadFile去分别处理两种情况。…...

Webhook 是什么?详解其工作原理

在现代技术中&#xff0c;一切都相互连接&#xff0c;每个应用程序通过许多服务的组合和协调实现无缝工作。这种协调是通过 webhooks 实现的。 Webhooks 是基于 HTTP 的回调函数&#xff0c;其中一个服务使用 API 立即通知另一个服务发生的事件。这就是简单的版本。从技术上讲…...

log4j2漏洞复现(CVE-2021-44228)

靶场环境 步骤一&#xff1a;设置出战规则 步骤二&#xff1a;开启靶场 cd vulhub cd log4j cd CVE-2021-44228 docker-compose up -d docker ps 访问端口 靶机开启 步骤三&#xff1a;外带注入 获得dnslog 靶机访问dnslog 得到dnslog的二级域名信息 步骤四&#xff1a;构造…...

tcpdump抓包分析

使用tcpdump进行抓包分析是一个常见的网络诊断和分析任务。以下是如何使用tcpdump进行抓包和分析的一些基本步骤和技巧&#xff1a; 1. 基本抓包 首先&#xff0c;你需要确定要抓取数据包的网络接口。可以使用ifconfig或ip addr命令查看网络接口。然后&#xff0c;使用以下命…...

LearnOpenGL学习(碰撞检测,粒子)

完整代码见&#xff1a;zaizai77/OpenGLTo2DGame: 基于OpenGL制作2D游戏 物体本身的数据来检测碰撞会很复杂&#xff0c;一半使用重叠在物体上的更简单的外形来检测。 AABB - AABB 碰撞 AABB代表的是轴对齐碰撞箱(Axis-aligned Bounding Box)&#xff0c;碰撞箱是指与场景基…...

操作系统(24)提高磁盘I/O速度的途径

前言 操作系统提高磁盘I/O速度的途径多种多样&#xff0c;这些途径旨在减少磁盘访问的延迟和开销&#xff0c;提高数据传输的效率。 一、磁盘高速缓存&#xff08;Disk Cache&#xff09; 磁盘高速缓存是一种在内存中为磁盘数据设置的缓冲区&#xff0c;用于存储磁盘中某些盘块…...

C/C++基础知识复习(45)

1) C 中面向对象编程如何实现数据隐藏&#xff1f; 在 C 中&#xff0c;数据隐藏是通过将类的成员变量和方法的访问权限控制起来实现的。通常&#xff0c;数据隐藏是通过使用 访问控制 机制来实现的&#xff0c;C 提供了三种访问控制修饰符&#xff1a; private: 使成员变量和…...

现代C++锁介绍

文章目录 场景描述&#x1f41e; 初始实现: 非线程安全版本互斥锁: std::mutex使用mutex保护共享资源使用std::lock_guard简化锁的管理 优化读操作: std::shared_mutex多个锁的管理: std::scoped_lock使用std::scoped_lock避免死锁 其他高级锁⏳ 带超时的锁: std::timed_mutex使…...

Squid代理服务器的安装使用

1.简介 Squid代理服务器是一种高效的中间服务器&#xff0c;位于客户端和目标服务器之间&#xff0c;起到了重要的网络中介作用。以下是对Squid代理服务器的详细介绍&#xff1a; 一、功能特点 缓存功能&#xff1a; Squid可以缓存经过它的请求和响应数据。当客户端发起请求时…...

爬虫学习案例8

爬取京东评论信息 采用DrissionPage自动化工具采集&#xff0c;感觉比Selenium工具好&#xff0c;真香。 安装第三方库 pip install DrissionPage pip install pandas pip install pyecharts pip install jieba pip install wordcloud1.安装DrissionPage库 DrissionPage安装…...

深入了解 CouchDB 的 Mango 查询:操作符和限制

CouchDB 是一个基于文档的数据库管理系统,支持 HTTP 协议,拥有强大的同步机制和灵活的数据模型。Mango 查询是 CouchDB 中用于数据检索的现代化查询接口,灵感来自 MongoDB 的查询语法。本文将深入探讨 Mango 查询中的各种操作符和限制,并提供详细的例子和说明,帮助你更好地…...

基于SSM(Spring + Spring MVC + MyBatis)框架搭建一个病人跟踪信息管理系统

基于SSM&#xff08;Spring Spring MVC MyBatis&#xff09;框架搭建一个病人治疗跟踪信息系统是一个相对复杂的项目&#xff0c;涉及到多个模块和功能。以下是一个简要的指导步骤。 1. 环境准备 开发环境&#xff1a;确保安装了Java Development Kit (JDK)&#xff0c;建议…...

U盘文件名变乱码:原因、恢复与预防全解析

一、U盘文件名变乱码现象描述 在日常使用U盘进行数据传输和存储时&#xff0c;我们有时会遇到一个令人头疼的问题&#xff1a;U盘中的文件名突然变成了乱码&#xff0c;无法正常识别或访问。这些乱码文件名可能包含各种奇怪的字符和符号&#xff0c;使得原本有序的文件管理变得…...

EasyGBS国标GB28181公网平台P2P远程访问故障诊断:云端服务端排查指南

随着信息技术的飞速发展&#xff0c;视频监控领域正经历从传统安防向智能化、网络化安防的深刻转变。EasyGBS平台&#xff0c;作为基于国标GB28181协议的视频流媒体平台&#xff0c;为用户提供了强大的视频监控直播功能。然而&#xff0c;在实际应用中&#xff0c;P2P远程访问可…...

一网多平面

“一网多平面”是一种网络架构概念&#xff0c;具体指的是在一张物理网络之上&#xff0c;逻辑划分出“1N”个平面。以下是对“一网多平面”的详细解释&#xff1a; 定义与构成 01一网多平面 指的是在统一的物理网络基础设施上&#xff0c;通过逻辑划分形成多个独立的网络平面…...

animatediff 模型网盘分享

网盘 一、123网盘&#xff0c;不限速 https://www.123pan.com/s/ueQ8jv-OlzPh.html 网盘 网址 animatediff 国外网址https://huggingface.co/guoyww/animatediff/tree/cd71ae134a27ec6008b968d6419952b0c0494cf2 国内镜像在 https://hf-mirror.com/guoyww/animatediff/t…...

ansible play-book玩法

使用ansible-playbook实现安装nginx_ansible 安装nginx-CSDN博客文章浏览阅读1.5k次&#xff0c;点赞14次&#xff0c;收藏19次。本文详细介绍了如何在Linux环境中准备Ansible环境&#xff0c;包括配置主机、下载和安装Ansible&#xff0c;以及使用yum模块和tar包源码安装Nginx…...

MySQL索引-索引的分类和创建

索引类型 数据类型 B树索引Hash索引FullText全文索引 物理存储 聚簇索引二级索引 字段特性 主键索引唯一索引普通索引前缀索引 字段个数 单列索引联合索引 创建索引 创建表时一同创建创建表后单独创建创建表后通过修改表结构创建 可以通过 SHOW INDEX FROM test_table;查看…...

如何给负载均衡平台做好安全防御

在现代网络架构中&#xff0c;负载均衡&#xff08;Load Balancing&#xff09;扮演着至关重要的角色。它不仅负责将流量分配到多个服务器以确保高效的服务交付&#xff0c;还作为第一道防线来抵御外部攻击。为了保护您的应用程序和服务免受潜在威胁&#xff0c;必须对负载均衡…...

HR/TA/HRBP的关系

HR&#xff08;人力资源&#xff09;领域包含 TA&#xff08;人才获取&#xff09;和 HRBP&#xff08;人力资源业务伙伴&#xff09;这两个重要的角色&#xff0c;但它们只是 HR 工作的一部分分支&#xff0c;一般我们说的HR指TA。 1. 人才获取&#xff08;TA&#xff09; 定…...

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中&#xff0c;结构体可以嵌套使用&#xff0c;形成更复杂的数据结构。例如&#xff0c;可以通过嵌套结构体描述多层级数据关系&#xff1a; struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

《基于Apache Flink的流处理》笔记

思维导图 1-3 章 4-7章 8-11 章 参考资料 源码&#xff1a; https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

C# 表达式和运算符(求值顺序)

求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如&#xff0c;已知表达式3*52&#xff0c;依照子表达式的求值顺序&#xff0c;有两种可能的结果&#xff0c;如图9-3所示。 如果乘法先执行&#xff0c;结果是17。如果5…...

深入理解Optional:处理空指针异常

1. 使用Optional处理可能为空的集合 在Java开发中&#xff0c;集合判空是一个常见但容易出错的场景。传统方式虽然可行&#xff0c;但存在一些潜在问题&#xff1a; // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...