vue3 + fastapi 实现选择目录所有文件自定义上传到服务器
文章目录
- ⭐前言
- 💖 技术栈选择
- ⭐前端页面搭建
- 💖 调整请求content-type传递formData
- ⭐后端接口实现
- 💖 swagger文档测试接口
- ⭐前后端实现效果
- 💖 上传单个文件
- 💖 上传目录文件
- ⭐总结
- ⭐结束

⭐前言
大家好,我是yma16,本文分享关于vue3 + fastapi 实现选择目录文件上传到服务器指定位置。
vue3系列相关文章:
前端vue2、vue3去掉url路由“ # ”号——nginx配置
csdn新星计划vue3+ts+antd赛道——利用inscode搭建vue3(ts)+antd前端模板
认识vite_vue3 初始化项目到打包
python_selenuim获取csdn新星赛道选手所在城市用echarts地图显示
python系列文章:
python爬虫_基本数据类型
python爬虫_函数的使用
python爬虫_requests的使用
python爬虫_selenuim可视化质量分
python爬虫_django+vue3可视化csdn用户质量分
python爬虫_正则表达式获取天气预报并用echarts折线图显示
python爬虫_requests获取bilibili锻刀村系列的字幕并用分词划分可视化词云图展示
python爬虫_selenuim登录个人markdown博客站点
python爬虫_requests获取小黄人表情保存到文件夹
python_selenuim获取csdn新星赛道选手所在城市用echarts地图显示
💖 技术栈选择
前端:vue3 + ts + antd
后端:python + fastapi
vue3优势
Vue3相比较于Vue2有以下几个优势:
-
更快的渲染速度:Vue3通过重新设计响应式系统和虚拟DOM,可以实现更快的渲染速度。在内存使用和性能方面,Vue3比Vue2更加高效。
-
更好的TypeScript支持:Vue3更好地支持TypeScript,TypeScript在Vue3中的使用更加直接、正式、稳定,并且类型推导更加准确。
-
更好的组件化开发:Vue3可以更方便地编写组件,将模板、脚本和样式分离开来,使得代码更加易读易维护。
-
更好的开发体验:Vue3增加了很多新的特性,如Composition API、Teleport、Suspense等,这些特性使得开发过程更加简单、便捷、灵活。
-
更多的生态支持:随着Vue3的面世,越来越多的插件和库开始支持Vue3,例如Vue Router、Vuex等,这些生态工具的发展将有助于Vue3的快速发展。
fastapi优势
FastAPI的优势主要体现在以下几个方面:
-
高性能:FastAPI使用异步编程模型,使用基于事件循环的异步处理请求,可以轻松处理大量的并发请求,提高服务器性能。
-
简单易用的API开发:FastAPI能够自动生成API文档,因此开发者可以通过它来快速地编写API,而不必花费大量时间去编写文档。
-
高可靠性:FastAPI 自动进行类型检查,能够避免类型错误引起的运行时错误,提高了API的稳定性。
-
支持原生Python语法:FastAPI可以使用Python原生语法来编写代码,不需要学习新的语言,可以更方便地使用Python的生态系统。
-
兼容多种前端框架:FastAPI 可以与多种前端框架配合使用,包括React、Angular、Vue.js等,提供了更大的开发自由度。
-
广泛的社区支持:FastAPI社区非常活跃,拥有大量的开发者和用户,提供了丰富的资源和支持。
⭐前端页面搭建
布局:
上下结构
上方为选择目录
下方为选择文件夹
实现效果图如下
vue3 语法糖代码实现
<script lang="ts" setup>
import { ref,reactive,computed } from 'vue';
import { InboxOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import { uploadFile,uploadUrl } from "../../service/gpt/index";
import { UploadOutlined } from '@ant-design/icons-vue';
const state:any=reactive({fileList:[],loading:false,text:'',dirList:[],dirPath:'',customFile:null,activeKey:'1',movieUrl:''
});const upUrl=async ()=>{state.loading=truetry{const res=await uploadUrl({url:state.movieUrl})console.log('res',res)}catch (e) {message.error(JSON.stringify(e))}finally {setTimeout(()=>{state.loading=false},200)}
}const remove=(e:any)=> {console.log('drop file',e);state.fileList=[]
}const removeDir=(e:any)=>{state.dirList=state.dirList.filter((file:any)=>file.uid!==e.uid)
}const customRequesHandle=(e:any)=>{console.log(e,'custom')
}const beforeUpload = (file:any) => {console.log('file before',file)state.fileList=[file]return false;
};const beforeUploadDir = (file:any) => {state.dirList.push(file)return false;
};const uploadSingleFile= async ()=>{state.loading=trueconsole.log(typeof state.fileList[0],'file 类型')try{const formData=new FormData();formData.append('file',state.fileList[0])const res=await uploadFile(formData)console.log('res',res)}catch (e) {message.error(JSON.stringify(e))}finally {setTimeout(()=>{state.loading=false},200)}
}const upBtnDisabled=computed(()=>{return state.fileList.length===0
})const change=(e:any)=>{console.log('change e',e)}
const upDir=async ()=>{if(state.dirList.length===0){return message.warning('请选择文件夹!')}state.loading=trueconst paramsData:any={dirList:state.dirList,dirPath:state.dirPath,}try{state.dirList.forEach(async (file:any)=>{try{const formData=new FormData();formData.append('file',file)const res=await uploadFile(formData)console.log('res',res)}catch(r){message.error(JSON.stringify(r))}})}catch (e) {message.error(JSON.stringify(e))}finally {setTimeout(()=>{state.loading=false},200)}
}const previewDirFile=async (file:any)=>{return new Promise(resolve=>resolve(false))
}
</script>
<template><div><a-spin :spinning="state.loading" tip="upload..."><div class="header-tools"></div><a-tabs v-model:activeKey="state.activeKey"><a-tab-pane key="1" tab="上传文件"><div>上传文件夹<div style="margin: 5px;border: 1px dotted #1890ff;padding: 20px"><div style="margin: 10px 0;max-height: 200px;overflow: auto"><a-upload :before-upload="beforeUploadDir" v-model:file-list="state.dirList"list-type="picture"@remove="removeDir" directory><a-button><upload-outlined></upload-outlined>上传文件夹</a-button></a-upload><div ></div></div><div style="margin:10px 0"><a-button type="primary" block @click="upDir" :disabled="state.dirList.length===0" >点击开始解析文件夹</a-button></div></div>上传单文件<div style="margin: 5px;border: 1px dotted #1890ff;padding: 20px"><div><a-upload-dragger:file-list="state.fileList"list-type="picture":multiple="false":before-upload="beforeUpload"@remove="remove"@change="change"><p class="ant-upload-drag-icon"><inbox-outlined></inbox-outlined></p><p class="ant-upload-text">点击上传或者拖拽到这</p><p class="ant-upload-hint">选择文件</p></a-upload-dragger></div><div style="margin:10px 0"><a-button type="primary" block @click="uploadSingleFile" :disabled="upBtnDisabled">点击开始上传文件</a-button></div></div></div></a-tab-pane></a-tabs></a-spin></div>
</template>
<style>.header-tools{text-align: center;font-size: 24px;font-weight: bold;}.content-box{}.des{margin:20px 0;}
</style>
💖 调整请求content-type传递formData
axios封装
import axios from "axios";// 实例
const createInstance = (baseURL:string)=>{return axios.create({baseURL:baseURL,timeout: 10000,headers: {'X-Custom-Header': 'yma16'}})
};// @ts-ignore
const http:any=createInstance('');// 添加请求拦截器
http.interceptors.request.use(function (config:any) {// 在发送请求之前做些什么return config;
}, function (error:any) {// 对请求错误做些什么return Promise.reject(error);
});// 添加响应拦截器
http.interceptors.response.use(function (response:any) {// 2xx 范围内的状态码都会触发该函数。// 对响应数据做点什么return response;
}, function (error:any) {// 超出 2xx 范围的状态码都会触发该函数。// 对响应错误做点什么return Promise.reject(error);
});// 文件上传
const createUploadInstance = (baseURL:string)=>{return axios.create({baseURL:baseURL,timeout: 10000,headers: {"Content-Type": "multipart/form-data"}})
};// @ts-ignore
const uploadHttp:any=createUploadInstance('');// 添加请求拦截器
uploadHttp.interceptors.request.use(function (config:any) {// 在发送请求之前做些什么return config;
}, function (error:any) {// 对请求错误做些什么return Promise.reject(error);
});// 添加响应拦截器
uploadHttp.interceptors.response.use(function (response:any) {// 2xx 范围内的状态码都会触发该函数。// 对响应数据做点什么return response;
}, function (error:any) {// 超出 2xx 范围的状态码都会触发该函数。// 对响应错误做点什么return Promise.reject(error);
});export {http,uploadHttp};
service对接后端
import {uploadHttp} from "../../http/index";
export const uploadFile: any = (formData: any) => {return uploadHttp.post("/api/uploadFile/action", formData);
};
⭐后端接口实现
安装环境
pip install uvicorn
pip install fastapi
pip install python-multipart
上传单个文件接口实现:
from fastapi import FastAPI, status, File, Form, UploadFile
from fastapi import FastAPI, status, File, Form, UploadFile
from fastapi.middleware.cors import CORSMiddleware
import osapp = FastAPI()
# 跨域配置
origins = ["http://localhost:3000",
]
app.add_middleware(CORSMiddleware,allow_origins=origins,allow_credentials=True,allow_methods=["*"],allow_headers=["*"],
)@app.get("/api")
async def root():return {"data": "fast api!"}# 上传文件
@app.post("/api/uploadFile/action")
async def create_file(file:UploadFile
):writeBytes('./media',file)return {'code':200,"msg":'success'}# 将file写入dirs目录文件
def writeBytes(dirs,file):bytesFile=file.file.read()filename=file.filenameif not os.path.exists(dirs):os.makedirs(dirs)with open(dirs+'/'+ filename, "wb") as f:f.write(bytesFile)
uvicorn运行fastapi
uvicorn server.main:app --reload --port 7777
💖 swagger文档测试接口
swagger文档地址:
http://ip:port/docs
上传成功!
⭐前后端实现效果
💖 上传单个文件
💖 上传目录文件
上传目录文件的接口实现:
- file为二进制文件
- dir为目录名称
- name为完整的文件名称
# 上传目录文件
@app.post("/api/uploadDirFile/action")
async def uploadDirFile(file:UploadFile,dir:str=Form(),name:str=Form()
):print(dir,'dir_____________')writeBytes('./media/'+dir,name,file)return {'code':200,"msg":'success'}# 将二进制数据写入目录文件
def writeBytes(dirs,name,file):bytesFile=file.file.read()filename=nameif not os.path.exists(dirs):os.makedirs(dirs)with open(dirs+'/'+ filename, "wb") as f:f.write(bytesFile)
⭐总结
文件上传注意事项
前端:
- 请求头配置
headers: {"Content-Type": "multipart/form-data"}
- 参数传递使用
new FormData()
后端:
- 接受参数使用 Uploadfile格式
- 解析文件内容名称包括类型按格式写入文件
multipart/form-data
multipart/form-data 是一种常用的 HTTP 请求方法,通常用于上传文件或大量数据。它将请求的数据分成多个部分(part),每一部分使用一个 boundary 分隔符来分开,每个部分包含一个头部和一个内容体,头部描述了该部分的属性,如数据类型、数据编码等。在 HTTP 消息体中,每个部分之间必须以 “–boundary\r\n” 开始,以 “–boundary–\r\n” 结束,即在结尾处添加额外的 “–” 标记。在客户端使用该方法请求时,需要明确指定请求头中的 Content-Type 为 multipart/form-data。服务端接收到该请求后,需要解析出每个部分中的请求数据。
⭐结束
本文分享到这结束,如有错误或者不足之处欢迎指出!
👍 点赞,是我创作的动力!
⭐️ 收藏,是我努力的方向!
✏️ 评论,是我进步的财富!
💖 感谢你的阅读!
相关文章:

vue3 + fastapi 实现选择目录所有文件自定义上传到服务器
文章目录 ⭐前言💖 技术栈选择 ⭐前端页面搭建💖 调整请求content-type传递formData ⭐后端接口实现💖 swagger文档测试接口 ⭐前后端实现效果💖 上传单个文件💖 上传目录文件 ⭐总结⭐结束 ⭐前言 大家好,…...

html 常见兼容性问题
目录 前言: 用法: 代码: 1. 盒模型差异: 2. 表格布局问题: 3. 浏览器前缀问题: 4. 字体渲染问题: 理解: 讨论: 前言: 在Web开发中,兼容性问题是常见的挑战之一。不同的浏览器和设备可能以不同的方式解释和呈现HTML,导致网页在某些环境下出现问题…...
PCL 点云投影到圆柱(C++详细过程版)
目录 一、算法原理1、圆柱方程2、投影原理二、代码实现三、结果展示1、原始点云2、投影结果四、参考链接一、算法原理 1、圆柱方程 圆柱方程可以表示为: ( x − x...

以太网链路聚合与交换机堆叠,集群
目录 以太网链路聚合 一.链路聚合的基本概念 二.链路聚合的配置 1.手工模式 2.LACP模式 系统优先级 接口优先级 最大活动接口数 活动链路选举 负载分担 负载分担模式 三.典型使用场景 交换机之间 交换机和服务器之间 交换机和堆叠系统 防火墙双机热备心跳线 四…...

5G RedCap工业智能网关
5G RedCap工业智能网关是当前工业智能化发展领域的重要技术之一。随着物联网和工业互联网的迅速发展,企业对于实时数据传输和高速通信需求越来越迫切。在这种背景下,5G RedCap工业智能网关以其卓越的性能和功能,成为众多企业的首选。 5G RedC…...

STM32-ADC实验
AD转换包括采样阶段和转换阶段。在采样阶段才对通道数据进行;在转换阶段只是将采集的数据进行转换为数字量输出,此刻通道数据变化不会改变转换结果。 实验1:单ADC单通道中断 硬件原理图 由于PC1接到电位器上,所以我们实验选择PC1…...

05、Python -- 爬取ts文件格式视频思路
目录 第一步:爬取一段5秒视频找url代码结果 第二步:下载整个视频的所有片段代码:结果: 第三步:合成视频安装模块代码:结果 简洁代码代码:结果: 最终代码简洁前代码简洁后代码 思路&a…...

【QT】其他常用控件2
新建项目 lineEdit 什么都不显示(linux password) password textEdit和plainTextEdit spinBox和doubleSpinBox timeEdit、dateEdit、dateTimeEdit label 显示图案,导入资源:【QT】资源文件导入_复制其他项目中的文件到qt项目中_St…...
django报错--Not Found The requested URL was not found on the server.
这个问题通常是由于服务器配置或代码错误导致的。以下是解决这个问题的一些建议和步骤: 首先,请确保你的URL拼写正确。确认URL中的路径和文件名都是正确的,并且没有任何拼写错误。如果你是从浏览器中复制粘贴URL,请确保没有任何额…...

VLOOKUP函数的使用方法
VLOOKUP是一个查找函数,给定一个查找的目标,它就能从指定的查找区域中查找返回想要查找到的值。它的基本语法为: VLOOKUP(查找目标,查找范围,返回值的列数,精确OR模糊查找)下面以一个实例来介绍…...

关于前端如何下载后端接口返回content-type为application/octet-stream的文件
关于前端如何下载后端接口返回response-type为application/octet-stream的文件 问题描述 后端接口定义为直接返回一个文件,如果带认证信息可以直接通过浏览器url下载,但是接口需要传headers认证信息,url上又不支持传相关信息 解决 前端…...

报错:SSL routines:ssl3_get_record:wrong version number
一、问题描述 前后端联调的时候,连接后端本地服务器,接口一直pending调不通,控制台还报以下错误: 立马随手搜索了一下解决方案,但是emmm,不符合前端的实际情况: 二、解决方法: 实际…...

Flask后端开发(一)-基础知识和前期准备
目录 1.背景介绍1.1. 项目背景1.2. 项目难点1.3. 项目环境 2. flask后端开发实现的功能3. flask部署和前后端对接3.1. flask运行配置和服务器部署3.2. flask前后端传参 4. 后端测试工具4.1. 工具介绍4.2. 工具使用 后记 1.背景介绍 1.1. 项目背景 就是前几个月临时接手了一个…...

基于SSM的幼儿园管理系统
基于SSM的幼儿园管理系统的设计与实现~ 开发语言:Java数据库:MySQL技术:SpringSpringMVCMyBatis工具:IDEA/Ecilpse、Navicat、Maven 系统展示 登录界面 管理员界面 摘要 基于SSM(Spring、Spring MVC、MyBatis&#…...

互联网Java工程师面试题·Spring篇·第三弹
目录 编辑 4、注解 4.1、什么是基于注解的容器配置 4.2、如何在 spring 中启动注解装配? 4.3、Component, Controller, Repository,Service 有何区别? 4.4、Required 注解有什么用? 4.5、Autowired 注解有什么用? 4.6、…...

前端(二十三)——轮询和长轮询
😫博主:小猫娃来啦 😫文章核心:实现客户端与服务器实时通信的技术手段 文章目录 前言轮询技术轮询的概念轮询的实现原理轮询的优缺点轮询的使用场景 长轮询技术长轮询的概念长轮询的实现原理长轮询的优缺点长轮询的使用场景 轮询与…...

uniapp把文件中的内复制到另一个文件中
使用的是Html 5的plus.io.resolveLocalFileSystemURL方法,文档:HTML5 API Reference var soursePath file:///storage/emulated/0/a/;//用于读取var removePath file:///storage/emulated/0/w/;//用于移除w这个文件夹var targetPath file:///storage/…...

什么是蓝桥杯?什么是蓝桥STEMA考试?
第十五届蓝桥大赛赛事安排? STEMA考试11月(考试时间11月26日) STEMA考试1月(2024年1月) STEMA考试3月(2024年3月) 第十五届蓝桥杯省赛(2024年4月待定) 第十五届蓝桥杯国赛(2024年5月待定) 注:以上时间具体以组委会官方发布为准。 01.蓝桥杯 蓝桥杯全国软件和…...
快递排序Java
快速排序是在工具类常用的排序算法,快速排序的思想主要是选定一个基准元素,然后找到基准元素的位置,然后再分别排序他左边的和他右边的,快速排序是不稳定的,时间复杂度位Nlog(N),最极端的情况就是一个反向排好顺序的数组ÿ…...

Spark简单回顾
星光下的赶路人star的个人主页 大鹏一日同风起,扶摇直上九万里 文章目录 1、Spark1.1 Spark入门1.1.1 Spark部署模式1.1.2 常用端口 1.2 SparkCore1.2.1 RDD不可变和五大属性1.2.2 RDD的弹性1.2.3 cache和Checkpoint的区别1.2.4 算子 1.3 SparkSQL1.4 内核1.4.1提交…...

《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...

mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...

C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...
嵌入式常见 CPU 架构
架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集,单周期执行;低功耗、CIP 独立外设;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel(原始…...

Vue ③-生命周期 || 脚手架
生命周期 思考:什么时候可以发送初始化渲染请求?(越早越好) 什么时候可以开始操作dom?(至少dom得渲染出来) Vue生命周期: 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...