
?前言
大家好,我是yma16,本文分享關于vue3 + fastapi 實現(xiàn)選擇目錄文件上傳到服務器指定位置。
vue3系列相關文章:
前端vue2、vue3去掉url路由“ # ”號——nginx配置
csdn新星計劃vue3+ts+antd賽道——利用inscode搭建vue3(ts)+antd前端模板
認識vite_vue3 初始化項目到打包
python_selenuim獲取csdn新星賽道選手所在城市用echarts地圖顯示
python系列文章:
python爬蟲_基本數(shù)據(jù)類型
python爬蟲_函數(shù)的使用
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優(yōu)勢
Vue3相比較于Vue2有以下幾個優(yōu)勢:
-
更快的渲染速度:Vue3通過重新設計響應式系統(tǒng)和虛擬DOM,可以實現(xiàn)更快的渲染速度。在內存使用和性能方面,Vue3比Vue2更加高效。
-
更好的TypeScript支持:Vue3更好地支持TypeScript,TypeScript在Vue3中的使用更加直接、正式、穩(wěn)定,并且類型推導更加準確。
-
更好的組件化開發(fā):Vue3可以更方便地編寫組件,將模板、腳本和樣式分離開來,使得代碼更加易讀易維護。
-
更好的開發(fā)體驗:Vue3增加了很多新的特性,如Composition API、Teleport、Suspense等,這些特性使得開發(fā)過程更加簡單、便捷、靈活。
-
更多的生態(tài)支持:隨著Vue3的面世,越來越多的插件和庫開始支持Vue3,例如Vue Router、Vuex等,這些生態(tài)工具的發(fā)展將有助于Vue3的快速發(fā)展。
fastapi優(yōu)勢
FastAPI的優(yōu)勢主要體現(xiàn)在以下幾個方面:
-
高性能:FastAPI使用異步編程模型,使用基于事件循環(huán)的異步處理請求,可以輕松處理大量的并發(fā)請求,提高服務器性能。
-
簡單易用的API開發(fā):FastAPI能夠自動生成API文檔,因此開發(fā)者可以通過它來快速地編寫API,而不必花費大量時間去編寫文檔。
-
高可靠性:FastAPI 自動進行類型檢查,能夠避免類型錯誤引起的運行時錯誤,提高了API的穩(wěn)定性。
-
支持原生Python語法:FastAPI可以使用Python原生語法來編寫代碼,不需要學習新的語言,可以更方便地使用Python的生態(tài)系統(tǒng)。
-
兼容多種前端框架:FastAPI 可以與多種前端框架配合使用,包括React、Angular、Vue.js等,提供了更大的開發(fā)自由度。
-
廣泛的社區(qū)支持:FastAPI社區(qū)非?;钴S,擁有大量的開發(fā)者和用戶,提供了豐富的資源和支持。
?前端頁面搭建
布局:
上下結構
上方為選擇目錄
下方為選擇文件夾
實現(xiàn)效果圖如下
vue3 語法糖代碼實現(xiàn)
<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=true
try{
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=true
console.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=true
const 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) {
// 在發(fā)送請求之前做些什么
return config;
}, function (error:any) {
// 對請求錯誤做些什么
return Promise.reject(error);
});
// 添加響應攔截器
http.interceptors.response.use(function (response:any) {
// 2xx 范圍內的狀態(tài)碼都會觸發(fā)該函數(shù)。
// 對響應數(shù)據(jù)做點什么
return response;
}, function (error:any) {
// 超出 2xx 范圍的狀態(tài)碼都會觸發(fā)該函數(shù)。
// 對響應錯誤做點什么
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) {
// 在發(fā)送請求之前做些什么
return config;
}, function (error:any) {
// 對請求錯誤做些什么
return Promise.reject(error);
});
// 添加響應攔截器
uploadHttp.interceptors.response.use(function (response:any) {
// 2xx 范圍內的狀態(tài)碼都會觸發(fā)該函數(shù)。
// 對響應數(shù)據(jù)做點什么
return response;
}, function (error:any) {
// 超出 2xx 范圍的狀態(tài)碼都會觸發(fā)該函數(shù)。
// 對響應錯誤做點什么
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);
};
?后端接口實現(xiàn)
安裝環(huán)境
pip install uvicorn
pip install fastapi
pip install python-multipart
上傳單個文件接口實現(xiàn):
from fastapi import FastAPI, status, File, Form, UploadFile
from fastapi import FastAPI, status, File, Form, UploadFile
from fastapi.middleware.cors import CORSMiddleware
import os
app = 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.filename
if 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
上傳成功!
?前后端實現(xiàn)效果
?? 上傳單個文件
?? 上傳目錄文件
上傳目錄文件的接口實現(xiàn):
- 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'
}
# 將二進制數(shù)據(jù)寫入目錄文件
def writeBytes(dirs,name,file):
bytesFile=file.file.read()
filename=name
if not os.path.exists(dirs):
os.makedirs(dirs)
with open(dirs+'/'+ filename, "wb") as f:
f.write(bytesFile)
?總結
文件上傳注意事項
前端:
- 請求頭配置
headers: {"Content-Type": "multipart/form-data"}
- 參數(shù)傳遞使用
new FormData()
后端:
- 接受參數(shù)使用 Uploadfile格式
- 解析文件內容名稱包括類型按格式寫入文件
multipart/form-data
multipart/form-data 是一種常用的 HTTP 請求方法,通常用于上傳文件或大量數(shù)據(jù)。它將請求的數(shù)據(jù)分成多個部分(part),每一部分使用一個 boundary 分隔符來分開,每個部分包含一個頭部和一個內容體,頭部描述了該部分的屬性,如數(shù)據(jù)類型、數(shù)據(jù)編碼等。在 HTTP 消息體中,每個部分之間必須以 “–boundary\r\n” 開始,以 “–boundary–\r\n” 結束,即在結尾處添加額外的 “–” 標記。在客戶端使用該方法請求時,需要明確指定請求頭中的 Content-Type 為 multipart/form-data。服務端接收到該請求后,需要解析出每個部分中的請求數(shù)據(jù)。
?結束
本文分享到這結束,如有錯誤或者不足之處歡迎指出!文章來源:http://www.zghlxwxcb.cn/news/detail-713681.html
?? 點贊,是我創(chuàng)作的動力!
?? 收藏,是我努力的方向!
?? 評論,是我進步的財富!
?? 感謝你的閱讀!文章來源地址http://www.zghlxwxcb.cn/news/detail-713681.html
到了這里,關于vue3 + fastapi 實現(xiàn)選擇目錄所有文件自定義上傳到服務器的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!