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提交…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...

(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...