当前位置: 首页 > news >正文

图片上传 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.lryc.cn/news/602286.html

相关文章:

  • 如何用VUE实现用户发呆检测?
  • Android通知(Notification)全面解析:从基础到高级应用
  • 【前端】解决Vue3+Pinia中Tab切换与滚动加载数据状态异常问题
  • 05 OpenCV--图像预处理之图像轮廓、直方图均衡化、模板匹配、霍夫变化、图像亮度变化、形态学变化
  • 数据结构:下三角矩阵(Lower Triangular Matrix)
  • MySQL SQL性能优化与慢查询分析实战指南:新手DBA成长之路
  • Eigen 中矩阵的拼接(Concatenation)与 分块(Block Access)操作使用详解和示例演示
  • 简明量子态密度矩阵理论知识点总结
  • 搜索二维矩阵Ⅱ C++
  • 【LeetCode】算法详解#10 ---搜索二维矩阵II
  • 秩为1的矩阵的特征和性质
  • 青少年编程高阶课程介绍
  • 青少年编程中阶课
  • 『 C++ 入门到放弃 』- 哈希表
  • 攻防世界-引导-Web_php_unserialize
  • 《LeetCode 热题 100》整整 100 题量大管饱题解套餐 中
  • cacti的RCE
  • 关于“PromptPilot” 之3 -Prompt构造器核心专项能力:任务调度
  • keepalived原理及实战部署
  • MBR和GPT分区的区别
  • 电商项目DevOps一体化运维实战
  • 【Datawhale夏令营】端侧Agent开发实践
  • CodeBuddy的安装教程
  • JAVA东郊到家按摩服务同款同城家政服务按摩私教茶艺师服务系统小程序+公众号+APP+H5
  • 基于BEKK-GARCH模型的参数估计、最大似然估计以及参数标准误估计的MATLAB实现
  • openlayer根据不同的状态显示不同的图层颜色
  • Fortran实现 3维反距离加权(IDW)插值算法
  • 初识 docker [下] 项目部署
  • ETH 交易流程深度技术详解
  • 二、Linux文本处理与文件操作核心命令