vue3 + fastapi 实现选择目录所有文件自定义上传到服务器

文章目录

    • ⭐前言
      • 💖 技术栈选择
    • ⭐前端页面搭建
      • 💖 调整请求content-type传递formData
    • ⭐后端接口实现
      • 💖 swagger文档测试接口
    • ⭐前后端实现效果
      • 💖 上传单个文件
      • 💖 上传目录文件
    • ⭐总结
    • ⭐结束

yma16-logo

⭐前言

大家好,我是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有以下几个优势:

  1. 更快的渲染速度:Vue3通过重新设计响应式系统和虚拟DOM,可以实现更快的渲染速度。在内存使用和性能方面,Vue3比Vue2更加高效。

  2. 更好的TypeScript支持:Vue3更好地支持TypeScript,TypeScript在Vue3中的使用更加直接、正式、稳定,并且类型推导更加准确。

  3. 更好的组件化开发:Vue3可以更方便地编写组件,将模板、脚本和样式分离开来,使得代码更加易读易维护。

  4. 更好的开发体验:Vue3增加了很多新的特性,如Composition API、Teleport、Suspense等,这些特性使得开发过程更加简单、便捷、灵活。

  5. 更多的生态支持:随着Vue3的面世,越来越多的插件和库开始支持Vue3,例如Vue Router、Vuex等,这些生态工具的发展将有助于Vue3的快速发展。

fastapi优势
FastAPI的优势主要体现在以下几个方面:

  1. 高性能:FastAPI使用异步编程模型,使用基于事件循环的异步处理请求,可以轻松处理大量的并发请求,提高服务器性能。

  2. 简单易用的API开发:FastAPI能够自动生成API文档,因此开发者可以通过它来快速地编写API,而不必花费大量时间去编写文档。

  3. 高可靠性:FastAPI 自动进行类型检查,能够避免类型错误引起的运行时错误,提高了API的稳定性。

  4. 支持原生Python语法:FastAPI可以使用Python原生语法来编写代码,不需要学习新的语言,可以更方便地使用Python的生态系统。

  5. 兼容多种前端框架:FastAPI 可以与多种前端框架配合使用,包括React、Angular、Vue.js等,提供了更大的开发自由度。

  6. 广泛的社区支持:FastAPI社区非常活跃,拥有大量的开发者和用户,提供了丰富的资源和支持。

⭐前端页面搭建

布局:
上下结构
上方为选择目录
下方为选择文件夹
实现效果图如下
upload

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

pipi install

上传单个文件接口实现:

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

上传成功!
upload

⭐前后端实现效果

💖 上传单个文件

uploadFile

💖 上传目录文件

uploadDir
上传目录文件的接口实现:

  • 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)

⭐总结

文件上传注意事项
前端:

  1. 请求头配置 headers: {"Content-Type": "multipart/form-data"}
  2. 参数传递使用new FormData()

后端:

  1. 接受参数使用 Uploadfile格式
  2. 解析文件内容名称包括类型按格式写入文件

multipart/form-data

multipart/form-data 是一种常用的 HTTP 请求方法,通常用于上传文件或大量数据。它将请求的数据分成多个部分(part),每一部分使用一个 boundary 分隔符来分开,每个部分包含一个头部和一个内容体,头部描述了该部分的属性,如数据类型、数据编码等。在 HTTP 消息体中,每个部分之间必须以 “–boundary\r\n” 开始,以 “–boundary–\r\n” 结束,即在结尾处添加额外的 “–” 标记。在客户端使用该方法请求时,需要明确指定请求头中的 Content-Type 为 multipart/form-data。服务端接收到该请求后,需要解析出每个部分中的请求数据。

⭐结束

本文分享到这结束,如有错误或者不足之处欢迎指出!
scene

👍 点赞,是我创作的动力!
⭐️ 收藏,是我努力的方向!
✏️ 评论,是我进步的财富!
💖 感谢你的阅读!

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

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

相关文章

嵌入式养成计划-45----QT--事件机制--定时器事件--键盘事件和鼠标事件--绘制事件

一百一十五、事件机制 当这件事情发生时&#xff0c;会自动走对应的函数处理&#xff08;重写的事件函数&#xff09; 115.1 事件处理简介 什么是事件&#xff1f; (重点) 件是由窗口系统或者自身产生的&#xff0c;用以响应所发生的各类事情&#xff0c;比如用户按下并释放…

进程与线程

进程 进程锁 进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的&#xff0c;而共享带来的是竞争&#xff0c;竞争带来的结果就是错乱&#xff0c;如何控制&#xff0c;就是加锁处理 part1&#xff1a;多个进程共享同一打印终端 …

【框架源码篇 03】Spring源码手写篇-手写AOP

Spring源码手写篇-手写AOP 手写IoC和DI后已经实现的类图结构。 一、AOP分析 1.AOP是什么? AOP[Aspect Oriented Programming] 面向切面编程&#xff0c;在不改变类的代码的情况下&#xff0c;对类方法进行功能的增强。 2.我们要做什么&#xff1f; 我们需要在前面手写IoC&…

排序算法,冒泡排序算法及优化,选择排序SelectionSort,快速排序(递归-分区)

一、冒泡排序算法&#xff1a; 介绍&#xff1a; 冒泡排序&#xff08;Bubble Sort&#xff09;是一种简单直观的排序算法。它重复地走访过要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需…

关于SparkRdd和SparkSql的几个指标统计,scala语言,打包上传到spark集群,yarn模式运行

需求&#xff1a; ❖ 要求:分别用SparkRDD, SparkSQL两种编程方式完成下列数据分析,结合webUI监控比较性能优劣并给出结果的合理化解释. 1、分别统计用户&#xff0c;性别&#xff0c;职业的个数&#xff1a; 2、查看统计年龄分布情况&#xff08;按照年龄分段为7段&#xff0…

初识树结构和二叉树

一&#xff0c;树概念及结构 1.1树结构的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的。 注意&a…

AI全栈大模型工程师(九)Function Calling 的机制

文章目录 Function Calling 的机制Function Calling 示例 1:加法计算器Function Calling 实例 2:四则混合运算计算器后记Function Calling 的机制 Function Calling 示例 1:加法计算器 需求:用户输入任意可以用加法解决的问题,都能得到计算结果。 # 加载环境变量import o…

人工智能发展与结构科学

人工智能&#xff08;AI&#xff09;在各种应用中的影响力不断增强&#xff0c;从简单的计算任务到复杂的决策支持。但在这背后&#xff0c;AI的发展其实是一个关于结构演变的故事。从最早的线性结构&#xff0c;到今天的复杂网络结构&#xff0c;结构的演变对AI的能力和效率产…

【linux】查看下载应用在服务器的日志

查看日志路径 一般在配置文件中logback.xml 账号密码xshell连接服务器&#xff0c;进入日志路径 根据搜索关键字查看xxx.log文件内容 cat xxx.log | grep 关键字 下载 xxx.log 到本地&#xff0c;一般可以下载当天的日志文件到本地查看比较方便 sz xxx.log 参考文章&#xff…

Adobe 推出 Photoshop Elements 2024 新版

&#x1f989; AI新闻 &#x1f680; Adobe 推出 Photoshop Elements 2024 新版 摘要:Adobe 最新发布 Photoshop Elements 2024 版本,新增引入 AI 功能,提供匹配颜色、创建照片卷、一键选择照片天空或背景等新功能,界面也进行了优化更新。本次发布重点加强了 AI 支持,简化复杂…

【软考-中级】系统集成项目管理工程师 【19 项目收尾管理】

持续更新。。。。。。。。。。。。。。。 【第十九章】收尾管理 &#xff08;选择题1分&#xff09; 19.1 项目验收19.2 项目总结19.3系统维护19.3.1软件项目的后续工作19.3.2系统集成项目的后续工作 19.4 项目后评价1. 信息系统目标评价2. 信息系统过程评价3. 信息系统效益评价…

嵌入式学习笔记(60)内存管理之堆

1.7.1.什么是堆&#xff08;heap&#xff09; 内存管理对OS来说是一件非常复杂的事&#xff0c;因为首先内存容量大&#xff0c;其次内存需求在时间和大小块上没有规律&#xff08;OS上运行着几十、几百、几千个进程随时都会申请或者释放内存&#xff0c;申请或者释放的内存块…

leetcode:2678. 老人的数目(python3解法)

难度&#xff1a;简单 给你一个下标从 0 开始的字符串 details 。details 中每个元素都是一位乘客的信息&#xff0c;信息用长度为 15 的字符串表示&#xff0c;表示方式如下&#xff1a; 前十个字符是乘客的手机号码。接下来的一个字符是乘客的性别。接下来两个字符是乘客的年…

100天掌握网络安全知识点!

1.网络安全是什么 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高&#xff1b; 二、则是发展相对成熟…

Microsoft Edge中使用开源的ChatGPT

一、双击打开浏览器 找到&#xff1a;扩展&#xff0c;打开 二、打开Microsoft Edge加载项 三、Move tab新标签 获取免费ChatGPT 四、启用Move tab。启用ChatGPT。 扩展 管理扩展 启用 五、新建标签页&#xff0c;使用GPT 六、使用举例 提问 GPT回复

学习git博客

git新建分支并且提交代码过程 1. git pull <codeBaseAddress> [分支名(默认是master)] 2. cd <projectName> 3. git branch <newBranchName> // 创建分支 4. git checkout <newBranchName> // 切换到新分支 // 开始写你的新代码 5. git add . //…

idea2023配置maven

看过【黑马程序员Maven全套教程&#xff0c;maven项目管理从基础到高级&#xff0c;Java项目开发必会管理工具maven】https://www.bilibili.com/video/BV1Ah411S7ZE?p9&vd_sourceedf9d91e5a0a27db51e3d6d4b9400637 配置的&#xff0c;前提要素配置也在这个课程里有啦&…

linux 下的java gate服务断掉的原因及解决思路

一.查询断掉的原因 1.查看gate日志&#xff0c;发现没有报错信息&#xff0c;突然就断了 2.查看是不是OOM导致 dmesg | grep java 发现确实Out of Memory了 3.发生问题的原因&#xff1a; 默认情况下, Linux kernels(内核)允许进程申请的量超过系统可用内存. 这是因为,在大多数…

微机原理:汇编语言语句类型与格式

文章目录 壹、语句类型1、语句分类2、常用伪代码和运算符2.1数据定义伪指令2.1.1字节定义伪指令DB&#xff08;8位&#xff09;2.1.2字定义伪指令DW&#xff08;16位&#xff09;2.1.3双字节伪指令DD2.1.4 多字节定义DF/DQ/DT&#xff08;了解&#xff09; 2.2 常用运算符2.2.1…

SLAM从入门到精通(a*搜路算法)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 目前机器人常用的搜路算法主要有这么几种&#xff0c;迪杰斯特拉算法、a*算法、贪心算法。每一种算法都有自己的场景和优势&#xff0c;可以灵活选…