圖片上傳 el+node后端+數據庫

模版部分:

鼠標懸浮到頭像的部分就出現下拉框顯示可以修改頭像,

el-upload是隱藏的,可能只是為了實現on-change函數和before-upload函數吧

這塊做的確實有點馬虎了。

 <div class="r-content"><el-dropdown><span class="el-dropdown-link"><img :src="getImageUrl" class="avatar"></span><template #dropdown><el-dropdown-menu><el-dropdown-item @click="handleUpdateAvatar">修改頭像</el-dropdown-item><el-dropdown-item @click="handleLoginOut">退出登錄</el-dropdown-item></el-dropdown-menu></template></el-dropdown><el-uploadclass="avatar-uploader"action="#":show-file-list="true":on-change="handleAvatarChange":before-upload="beforeAvatarUpload"style="display: none;"ref="uploadRef"><!-- <el-button ref="btn" size="large" type="primary">選取文件</el-button> --></el-upload></div>
import { ref, computed,nextTick } from 'vue'
import { useAllDataStore } from '../stores'
import { useRouter } from 'vue-router'
// import { ElMessage,ElUpload } from 'element-plus'
import { handleAvatarChange } from '@/services/editService.js'
const store = useAllDataStore()
import defaultAvatar from '@/assets/images/user-default.png'
const getImageUrl = computed(() => {return store.state.avatar||defaultAvatar; 
})
// 監聽狀態變化(調試用)
watch(() => store.state.avatar,(newVal, oldVal) => {console.log('頭像更新了:', newVal, oldVal)},{ immediate: true } // 立即執行一次
)
const uploadRef = ref(null)
function handleUpdateAvatar() {console.log('handleUpdateAvatar')uploadRef.value.$el.querySelector('input').click()
}// 上傳前的校驗,比如限制文件類型、大小等
function beforeAvatarUpload(file) {const isJPG = file.type === 'image/jpeg' || file.type === 'image/png';const isLt2M = file.size / 1024 / 1024 < 2; if (!isJPG) {ElMessage.error('上傳頭像只能是 JPG/PNG 格式!');}if (!isLt2M) {ElMessage.error('上傳頭像大小不能超過 2MB!');}return isJPG && isLt2M;
}//跨組件之間的傳值  
const router = useRouter()
const handleLoginOut = () => {store.clean();router.push('/login')
}

前端發送請求部分:

export const handleAvatarChange=async (file)=> {try {// 創建 FormData 對象,用于上傳文件const formData = new FormData();formData.append('file', file.raw); // 調用后端接口上傳頭像,這里的接口地址根據實際后端定義填寫const res = await axios.post(`${API_URL}/updateAvatar`, formData, {headers: {'Content-Type': 'multipart/form-data', 'Authorization':`Bearer ${localStorage.getItem('token')}`,},withCredentials: true});if (res.data.code === 200) { // 假設后端返回 code 為 200 表示成功ElMessage.success('頭像修改成功');// 更新 store 中的頭像地址store.updateImg(res.data.data.avatar);}} catch (error) {// console.error('上傳頭像出錯!:', error);ElMessage.error('網絡異常,頭像修改失敗!');}
}

后端處理請求部分:

import { Router } from 'express';
import multer from 'multer';
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs/promises';
import pool from '../config/db.js';
import jwt from 'jsonwebtoken';
import  { JWT_SECRET }  from '../config/config.js';
import bodyParser from 'body-parser';
const router = Router();// 1. 直接通過 import.meta.url 計算上傳目錄路徑(不使用 __dirname)
const currentFileUrl = new URL(import.meta.url); //當前文件完整的地址
const currentDirPath = path.dirname(fileURLToPath(currentFileUrl)); // 當前文件所在目錄routes的地址
const uploadDir = path.join(currentDirPath, '../public/avatars'); // 拼接上傳目錄路徑
// console.log('uploadDir:',uploadDir);
// 初始化上傳目錄
try {await fs.access(uploadDir);
} catch {await fs.mkdir(uploadDir, { recursive: true });
}// 2. 配置 multer 存儲規則
const storage = multer.diskStorage({destination: (req, file, cb) => {cb(null, uploadDir);},filename: (req, file, cb) => {const safeName = file.originalname.replace(/[^a-zA-Z0-9_.-]/g, '');const uniqueName = `${Date.now()}-${safeName}`;cb(null, uniqueName);//生成唯一文件名}
});// 3. 創建 multer 實例
const upload = multer({limits: { fileSize: 2 * 1024 * 1024 },fileFilter: (req, file, cb) => {if (file.mimetype.startsWith('image/')) {cb(null, true);} else {cb(new Error('只允許上傳圖片文件!'), false);}},storage: storage
});// 4.添加認證中間件  
const authenticate = (req, res, next) => {const token = req.headers.authorization?.split(' ')[1];console.log(req.headers);if (!token) {return res.status(401).json({ code: 401, message: '未提供Token認證信息!' });}try {// 獲取發送請求方的token信息,驗證發送人   同時在post請求中順利的修改請求人的數據庫字段const decoded = jwt.verify(token, JWT_SECRET);req.user = decoded;console.log('req.user:',req.user);next();} catch (error) {res.status(401).json({ code: 401, message: '這是無效或者過期的Token!' });
}
}
// 5. 處理頭像上傳請求
router.post('/updateAvatar',authenticate, upload.single('file'), async (req, res) => {try {if (!req.file) {return res.status(400).json({ code: 400, message: '文件為空,請選擇要上傳的頭像!' });}console.log('req.file.filename:',req.file.filename);// 生成圖片訪問 URLconst avatarUrl = `http://localhost:3000/avatars/${req.file.filename}`;// 驗證登錄態if (!req.user?.id) {return res.status(401).json({ code: 401, message: '未登錄,無法修改頭像' });}const userId = req.user.id;// 更新數據庫const [results] = await pool.query('UPDATE users SET avatarUrl = ? WHERE id = ?',[avatarUrl, userId]);if (results.affectedRows === 0) {return res.status(404).json({ code: 404, message: '用戶不存在,更新失敗' });}res.status(200).json({code: 200,message: '頭像修改成功',data: { avatar: avatarUrl }});} catch (error) {console.error('頭像上傳失敗:', error);res.status(500).json({ code: 500, message: '服務器錯誤,上傳失敗' });}
});

因為有文件處理中間件這些的吧所以確實麻煩了些,還有認證中間件,可以自行刪減,注意頭像要上傳原圖片,然后對應的格式就是multipart/form-data了 這塊content-type我這個項目里

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/93335.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/93335.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/93335.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

[java 常用類API] 新手小白的編程字典

目錄 1.API 1.1定義: 2.Object類 2.1 toString() 方法 2.2 equals() 方法 3. Arrays 類 3.1 equals() 方法 3.2 sort() 方法 3.2.1排序 3.2.2 自定義對象排序 3.3 binarySearch() 方法 3.4 copyOf() 方法 3.5 fill() 方法 3.6 toString() 方法 4.基本數據類型包裝類 4.…

去除視頻字幕 2, 使用 PaddleOCR 選取圖片中的字幕區域, 根據像素大小 + 形狀輪廓

有人問我在搞什么&#xff1a;就是做這里的第2步。問題描述這里誤導&#xff0c;誤判&#xff0c;太嚴重了。如果我把這個區域當做是 mask ,那么真正的目標會被去除掉還有什么建議嗎&#xff1f;比如我能否根據這個mask 的大致形狀來判斷它是不是字幕&#xff0c; 如果不是細長…

Ubuntu 連接Visual SVN

Windows服務器上的svn倉庫為&#xff1a; https://ldw_online:8443/svn/OnlineRepository/LVC IP地址為192.168.8.8 4. 從 Ubuntu 測試連通性 在 Ubuntu 上可以用&#xff1a; bash 復制編輯 curl -vk https://192.168.8.8:8443/ 如果返回 HTTP 頭或 SSL 握手成功&#xff…

JAVA:Spring Boot 集成 Protobuf 的技術指南

?? 1、簡述 在分布式服務通信中,數據序列化與反序列化的效率對系統性能影響極大。Protocol Buffers(Protobuf) 是由 Google 提出的一種高效的結構化數據序列化協議,具有: ?? 高性能(遠優于 JSON/XML) ?? 跨語言支持 ?? 較小的體積 本篇將帶你了解如何在 Spring…

SQLServer內存釋放工具介紹:一款實用的數據庫性能優化助手

SQLServer內存釋放工具介紹&#xff1a;一款實用的數據庫性能優化助手 去發現同類優質開源項目:https://gitcode.com/ 在數據庫管理中&#xff0c;內存釋放是優化服務器性能的重要環節。本文將為您詳細介紹一款名為SQLServer內存釋放工具的開源項目&#xff0c;幫助您輕松管理…

《藍耘容器全棧技術指南:企業級云原生與異構計算實戰大全》

&#x1f31f; 嗨&#xff0c;我是Lethehong&#xff01;&#x1f31f; &#x1f30d; 立志在堅不欲說&#xff0c;成功在久不在速&#x1f30d; &#x1f680; 歡迎關注&#xff1a;&#x1f44d;點贊??留言收藏&#x1f680; &#x1f340;歡迎使用&#xff1a;小智初學計…

計算器3.0:實現用戶自定義組件

前言&#xff1a; 馬總給我提出計算器3.0新需求&#xff1a;可以在頁面上輸入一個組件&#xff0c;用戶的組件庫里面就多一個組件&#xff0c;用戶就可以使用 一、解決方法&#xff1a; 1. 新增成員變量和初始化 // 新增的輸入框 private InputBox newInputBox; // 新增的組…

PIG AI 全新升級:全新 MCP 能力加持,讓企業級 AI 開發效率翻倍!

你是否曾為 AI 應用的開發門檻而頭疼?調試代碼耗費數小時、集成外部工具需要復雜配置、想要快速構建智能系統卻不知從何下手…別擔心!PIG AI 最新版本帶來的 MCP(Model Context Protocol)能力,正為這些問題提供一站式解決方案。本文將帶你深入淺出地了解這一重磅升級,手把…

Springboot+vue超市管理系統的設計與實現

文章目錄前言詳細視頻演示具體實現截圖后端框架SpringBoot前端框架Vue持久層框架MyBaits成功系統案例&#xff1a;代碼參考數據庫源碼獲取前言 博主介紹:CSDN特邀作者、985高校計算機專業畢業、現任某互聯網大廠高級全棧開發工程師、Gitee/掘金/華為云/阿里云/GitHub等平臺持續…

一文快速了解Docker和命令詳解

本文讓你快速了解Docker是什么的東西&#xff0c;在我們程序開發的時候到底有什么作用&#xff0c;為什么需要去學習它。本文章只是做一個簡單的概述配套黑馬課程讓你快速了解、使用Docker。 一、什么是Docker&#xff1f; Docker是一個開源的容器化平臺&#xff0c;允許開發者…

【GaussDB】如何從GaussDB發布包中提取出內核二進制文件

【GaussDB】如何從GaussDB發布包中提取出內核二進制文件 背景 GaussDB 從505和506版本起&#xff08;前面的版本不清楚&#xff09;&#xff0c;華為官方不再提供用腳本安裝GaussDB的方式&#xff08;應該是基于運維交付標準化的角度考慮&#xff09;&#xff0c;僅支持使用T…

ETH 交易流程深度技術詳解

概述在前面對 PolkaVM 和 Revive 的文章中&#xff0c;我們介紹了很多技術細節&#xff0c;開發工具。還對比 EVM&#xff0c;知道了 PolkaVM 的優勢。很多同學還是對 Polkadot SDK 為什么可以運行 EVM 兼容的智能合約&#xff0c;以及交易處理的整個流程不太清楚。這篇文章將會…

【算法訓練營Day17】二叉樹part7

文章目錄二叉樹的最近公共祖先二叉搜索樹的最近公共祖先二叉搜索樹中的插入操作刪除二叉搜索樹中的節點二叉樹的最近公共祖先 題目鏈接&#xff1a;236. 二叉樹的最近公共祖先 解題邏輯&#xff1a; 最近公共祖先的定義為&#xff1a;對于有根樹 T 的兩個節點 p、q&#xff0c…

Vue插件與組件核心區別詳解

在 Vue 中&#xff0c;插件&#xff08;Plugin&#xff09; 和 組件&#xff08;Component&#xff09; 是兩種不同層次的概念&#xff0c;它們的主要區別如下&#xff1a;1. 組件 (Component) 定義&#xff1a; Vue 應用的基本構建單元&#xff0c;是可復用的 Vue 實例&#x…

基礎NLP | 02 深度學習基本原理

文章目錄 深度學習基本原理 數學基礎 線代 numpy 常用操作 導數, 梯度 梯度下降法 梯度下降代碼 GradientDescent.py 反向傳播 完整的反向傳播過程 權重更新方式 pytorch 網絡結構 全連接層 (線性層) 例子 - 手動實現模擬一個線性層 DNNforward.py 激活函數 激活函數-Sigmoid…

MySQL面試題及詳細答案 155道(001-020)

《前后端面試題》專欄集合了前后端各個知識模塊的面試題&#xff0c;包括html&#xff0c;javascript&#xff0c;css&#xff0c;vue&#xff0c;react&#xff0c;java&#xff0c;Openlayers&#xff0c;leaflet&#xff0c;cesium&#xff0c;mapboxGL&#xff0c;threejs&…

Ansible安裝與入門

目錄 Ansible ansible任務執行模式 ansible執行流程 ansible命令執行過程&#xff08;背會&#xff09; ansible的安裝方式 ansible的程序結構&#xff08;yum安裝為例&#xff09; ansible的配置文件查找順序&#xff08;背會&#xff09; 核心配置文件 ansible的配置…

【Spring】Spring Boot啟動過程源碼解析

目錄 一、啟動入口 二、SpringApplication的構造過程 2.1 設置應用類型 2.2 設置初始化器&#xff08;Initializer&#xff09; 2.2.1 獲取BootstrapRegistryInitializer對象 2.2.2 獲取ApplicationContextInitializer對象 2.3 設置監聽器&#xff08;Listener&#xff…

CDN架構全景圖

CDN架構全景圖 CDN&#xff08;內容分發網絡&#xff09;是一種通過在全球范圍內部署邊緣節點服務器&#xff0c;將內容緩存至離用戶最近的位置&#xff0c;從而加速內容分發、降低延遲并減輕源站壓力的分布式網絡架構。其核心設計目標是優化互聯網內容傳輸效率&#xff0c;提升…

【pytest高階】源碼的走讀方法及插件hook

一、pytest源碼走讀方法 依賴庫認知篇 &#x1f4e6;這是理解 pytest 源碼的 “前菜”&#xff0c;先認識 3 個超重要的小伙伴&#xff1a;iniconfig &#x1f4c4;&#xff1a;像個 “文件小管家”&#xff0c;專門負責讀取 ini 配置文件&#xff08;比如 pytest 的配置&#…