nodejs图片上传/头像上传

news2024/11/20 17:38:02

项目结构

在这里插入图片描述
utils文件夹无用,没文件

前端代码

<!DOCTYPE html>
<html lang="zh-CN">
	<head>
		<meta charset="utf-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />
		<meta name="author" content="金西振" />
		<title></title>
		<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
		<style></style>
	</head>
	<body>
		<form>
			<!-- multiple 属性支持选择多个文件 -->
			<!-- accept="image/*" 只能选择图片 -->
			<input type="file" name="myfile" id="myfile" accept="image/*" multiple />
			<br />
			<input type="submit" value="上传" />
		</form>
		<script>
			const form = document.forms[0];
			const input = form.myfile;
			form.onsubmit = function (ev) {
				ev.preventDefault();
				// input.files获取选择的文件
				// console.log('文件上传 input.files', input.files);

				const file = input.files[0];
				console.log('file: ', file);
        if (!file) {
          return alert('请选择图片');
        }
				// 如果不是图片提示用户修改
				if (!file.type.startsWith('image')) {
					return alert('只能上传图片');
				}

				// 方式一:使用append;
				// const formdata = new FormData();
        // 上传一个文件
				// formdata.append('myfile', file);

        // 上传多个文件
				// formdata.append('myfile', input.files[0]);
				// formdata.append('myfile', input.files[1]);
				// formdata.append('myfile', input.files[2]);


				// 方式二:传递form对象:单个和多个文件写法一致
				const formdata = new FormData(form);

				axios({
					url: 'http://127.0.0.1:3000/upload',
					method: 'POST',
					data: formdata,
				}).then(({ data }) => {
					console.log('data: ', data);
				});
			};
		</script>
	</body>
</html>

app.js代码

const express = require('express');
const cors = require('cors');

const app = express();
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

app.use(require('./router/upload'));

app.listen(3000, () => {
	console.log('running: http://127.0.0.1:3000');
});

// 文件上传
// 服务端(听懂即可):需要使用multer模块

// 客户端(掌握):只需要使用post请求,发送formdata类型的数据即可

upload核心代码:

const router = require('express').Router();
const multer = require('multer');
const path = require('path');
// 获取文件的扩展名
console.log(path.extname('1.png'));

const storage = multer.diskStorage({
	// 用来指定文件的位置
	destination(req, file, cb) {
		cb(null, './public/img');
	},
	// 用来设置文件的名字
	filename(req, file, cb) {
		// console.log('file: ', file);
		const filename = Date.now() + path.extname(file.originalname);
		cb(null, filename);
	},
});

// {
//   fieldname: 'myfile',     上传时定义的名字
//   originalname: 'head.png',  图片名字
//   encoding: '7bit',     编码格式
//   mimetype: 'image/png'    文件类型
// }

const upload = multer({
	// 使用dest选项无法修改文件的名,文件名默认是一个长度为32位的随机值
	// dest: './public/img',
	storage: storage,
	// 文件上传限制:单位字节
	limits: { fileSize: 1024 * 1024 * 2 },
	// 文件过滤:决定哪些文件可以上传,哪些文件跳过
	// fileFilter: function (req, file, cb) {
	// 	if (file.mimetype.startsWith('image')) {
	// 		cb(null, true); // 接收这个文件
	// 	} else {
	// 		cb(null, false); // 拒绝这个文件
	// 	}
	// },
});

// multer模块是用来专门处理文件上传的,只处理multipart/form-data类型的数据
// 使用formdata可以传递文本信息,也可以传递文件数据
// 如果传递文本数据,会把参数数据保存在req.body中
// 如果传递文件数据,会把参数数据保存在req.file 或者 req.files 中

// 使用multer模块,会向req对象上添加body和 file或者files属性
// req.body 用来获取文本信息
// req.file 单个文件上传的信息  upload.single('myfile') 用上传单个文件
// req.files 一次上传多个文件的信息 upload.array('myfile', 6) 用于上传多个文件
// myfile是参数名,类似于username
router.post('/upload', upload.array('myfile'), (req, res) => {
	console.log('req.files:', req.files);

	// if (req.file) {
	res.json({ error: 0, data: '上传成功' });
	// } else {
	// 	res.json({ error: 0, data: '上传失败' });
	// }
});

module.exports = router;

头像上传数据库(这里只写后端核心代码)

const express = require('express');
const multer = require('multer');
const fs = require('fs');
const path = require('path');

let User = require('../modules/user');
let router = express.Router();
const jwt = require('../modules/jwt');

let uploadpath = './public/imgs/'; // 图头像上传地址
const imgURL = './imgs/'; // 头像网路地址
let avatarName; // 头像名字

let storage = multer.diskStorage({
	destination: function (req, file, cb) {
		cb(null, uploadpath);
	},
	filename: function (req, file, cb) {
		let extname = path.extname(file.originalname);
		// 张三-1834137249801230921.png
		avatarName = req.username + '-' + Date.now() + extname;
		cb(null, avatarName);
	},
});
let isImage = false;
let upload = multer({
	storage: storage,
	limits: {
		fileSize: 1024 * 1024 * 5,
	},
	fileFilter: function (req, file, cb) {
		if (file.mimetype.startsWith('image')) {
			isImage = true;
			cb(null, true);
		} else {
			isImage = false;
			cb(null, false);
		}
	},
});

router.post('/user/avatar', jwt.verify, upload.single('avatar'), (req, res) => {
	if (!isImage) {
		res.json({ error: 1, data: '只能上传图片' });
		return;
	}
	// 如果现在的头像不是默认图片,删除现有的头像(旧头像),保存新上传的头像地址
	if (fs.existsSync(uploadpath + avatarName)) {
		User.findOne({ _id: req._id }, (error, user) => {
			// 如果用户有旧头像,则先删除旧头像文件,然后把新头像文件的url存入用户信息
			if (user.avatar != imgURL + 'timg.jpg') {
				let oldpath = user.avatar.replace(imgURL, 'public/imgs/');
				if (fs.existsSync(oldpath)) {
					fs.unlinkSync(oldpath); //删除旧头像
				}
			}
			// 保存新头像
			user.avatar = imgURL + avatarName; //新头像的地址
			user.save(error => {
				res.json({ error: 0, data: '上传成功' });
			});
		});
	} else {
		res.json({ error: 0, data: '上传失败' });
	}
});

module.exports = router;

JWZ权限验证代码

const jwt = require('jsonwebtoken');
const secretkey = 'howdoyoudo'; //密钥

// 生成token
module.exports.sign = (data = {}) => {
	return jwt.sign(data, secretkey, {
		expiresIn: 60 * 60,
	});
};

// 验证token
module.exports.verify = (req, res, next) => {
	let authorization = req.headers.authorization || req.body.token || req.query.token || '';
	let token = '';
	if (authorization.includes('Bearer')) {
		token = authorization.replace('Bearer ', '');
	} else {
		token = authorization;
	}

	jwt.verify(token, secretkey, (error, data) => {
		if (error) {
			res.json({ error: 1, data: 'token验证失败' });
    } else {
			req._id = data._id;
			req.username = data.username;
			next();
		}
	});
};

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大屏展示项目开发常用属性

图例legend前言&#xff1a;1.[配置项手册](https://echarts.apache.org/zh/option.html#title)2.[使用手册可以快速入门](https://echarts.apache.org/handbook/zh/get-started/)3.4.一、图例legend1.1 设置图例的位置 &#xff08;上下左右&#xff09;1.1.1 上下&#xff08…

【爪洼岛冒险记】第2站:带你学会:Java中三种输出语句,java中基本数据类型,学会变量,常量,类型转换知识点

&#x1f331;博主简介&#xff1a;是瑶瑶子啦&#xff0c;一名大一计科生&#xff0c;目前在努力学习C进阶、数据结构、算法、JavaSE。热爱写博客~正在努力成为一个厉害的开发程序媛&#xff01; &#x1f4dc;所属专栏&#xff1a;爪洼岛冒险记 ✈往期博文回顾&#xff1a;【…

【译】eBPF 和服务网格:还不能丢掉 Sidecar

服务网格以典型的 sidecar 模型为人熟知&#xff0c;将 sidecar 容器与应用容器部署在同一个 Pod 中。虽说 sidecar 并非很新的模型&#xff08;操作系统的 systemd、initd、cron 进程&#xff1b;Java 的多线程&#xff09;&#xff0c;但是以这种与业务逻辑分离的方式来提供服…

主库出问题了,从库怎么办?

在前面的文章中,我和你介绍了 MySQL 主备复制的基础结构,但这些都是一主一备的结构。 大多数的互联网应用场景都是读多写少,因此你负责的业务,在发展过程中很可能先会遇到读性能的问题。而在数据库层解决读性能问题,就要涉及到接下来两篇文章要讨论的架构:一主多从。 今…

Qt之标准对话框(QColorDialog、QInputDialog、QFontDialog)

文章目录QColorDialog使用方式QInputDialog使用方式QFontDialog使用方式提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 QColorDialog QDialog类用于指定颜色的。 使用方式 设置最开始的颜色 setCurrentColor(Qt::red);//其他的颜色Qt::white Qt::…

迪文串口屏(1)-DMG80480C070_03WTC

由于成本缘故&#xff0c;用迪文串口屏去替换项目里的大彩串口屏&#xff0c;样品型号为DMG80480C070_03WTC。不过说句实话&#xff0c;迪文串口屏没有大彩串口屏那么容易上手。产品命名及硬件特性10Pin_1.0mm座子&#xff0c;7.0英寸&#xff0c;800*480分辨率&#xff0c;16.…

机器学习--方差和偏差、Bagging、Boosting、Stacking

目录 一、方差和偏差 数学定义 对公式的解释 减小偏差、方差、噪声 总结 二、Bagging 代码实现 bagging什么时候会变好 不稳定的learner ​总结 三、Boosting Gradient boosting gradient boosting 的代码实现 gradient boosting的效果 ​总结 四、Stacking st…

秒杀项目总结

秒杀就是同一个时刻有大量的请求争抢购买同一个商品&#xff0c;并且完成交易的过程 也就是大量的并发读和并发写 先制作一个增删改查的秒杀系统&#xff0c;但是想让这个系统支持高并发访问就没那么容易了&#xff0c; 如何让这个秒杀系统面对百万级的请求流量不出故障&…

02-final、finally、finalize的区别?

1.final final是java中的修饰符&#xff0c;用于修饰属性&#xff08;变量&#xff09;、方法、类。 1.被final修饰的变量不可以被改变,如果修饰引用,那么表示引用不可变,引用指向的内容可变. 被修饰的常量,在编译阶段会存入常量池中. 2.被final修饰的方法不可以被重写, 被修…

图片播放器的实现1——利用Image2LCD提取图片数据并显示

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 参考内容 &#xff08;1&#xff09;https://xiefor100.blog.csdn.net/article/details/71941527 &#xff08;2&#xff09;https://xiefor100.blog.csdn.net/article/details/78529519 内容总结 …

2022. 12 青少年软件编程(图形化) 等级考试试卷(四级)

2022年12月 青少年软件编程&#xff08;图形化&#xff09; scratch等级考试试卷&#xff08;四级&#xff09; 分数&#xff1a; 100 题数&#xff1a; 28 一、单选题(共 15题&#xff0c;共 30分) 1.运行下列程序&#xff0c; 变量“结果”的值为&#xff1f; &#xff08; &…

WXSS 如何进行编译?

过往中小企业或技术团队开发一个 App 的时间成本和人力成本居高难下&#xff0c;但是随着微信上线小程序&#xff0c;更像是为这部分群体打开了一扇天窗&#xff0c;此后小程序呈现出井喷式发展的状态&#xff0c;不仅微信&#xff0c;支付宝、百度、抖音等超级 App 都跟上步伐…

【C++核心编程】C++全栈体系(十)

C核心编程 第四章 类和对象 六、继承 继承是面向对象三大特性之一 有些类与类之间存在特殊的关系&#xff0c;例如下图中&#xff1a; 我们发现&#xff0c;定义这些类时&#xff0c;下级别的成员除了拥有上一级的共性&#xff0c;还有自己的特性。 这个时候我们就可以考…

华为DHCPv6实验配置

目录 配置AR1作为DHCPv6服务器为PC1分配IPv6地址 配置AR2作为DHCPv6服务器&#xff0c;AR1作为DHCPv6中继器为PC2分配IPv6地址 配置AR3作为DHCPv6 PD服务器为AR1分配地址前缀 什么是DHCP PD 配置AR1作为DHCPv6服务器为PC1分配IPv6地址 AR1 DHCPv6服务器端配置 ipv6 …

解决N+1问题的另一种方法 - 关联的多结果集ResultSet

如果我的博客对你有帮助&#xff0c;欢迎进行评论✏️✏️、点赞&#x1f44d;&#x1f44d;、收藏⭐️⭐️&#xff0c;满足一下我的虚荣心&#x1f496;&#x1f64f;&#x1f64f;&#x1f64f; 。 从版本 3.2.3 开始&#xff0c;MyBatis 提供了另一种解决 N1 查询问题的方…

C语言——位段

文章目录思维导图&#xff1a;一. 什么是位段二.位段的内存分配三.位段的跨平台问题四.位段的应用结语:思维导图&#xff1a; 一. 什么是位段 位段的声明和结构体类似&#xff0c;但是有2个不同&#xff1a; 位段的成员必须是int、unsigned int 或 signed int(在很多平台上cha…

python自学之《21天学通Python》(9)——基于tkinter的GUI编程

第12章 基于tkinter的GUI编程 Windows的图形用户界面非常方便用户操作&#xff0c;因此&#xff0c;Windows操作系统得到了广大个人计算机用户的欢迎。在Python中&#xff0c;也可以编写美观的GUI界面应用程序与项目。tkinter是Python自带的用于GUI编程的模块&#xff0c;tkin…

【论文速递】CVPR2022 - 学习 什么不能分割:小样本分割的新视角

【论文速递】CVPR2022 - 学习 什么不能分割:小样本分割的新视角 【论文原文】&#xff1a;Learning What Not to Segment: A New Perspective on Few-Shot Segmentation 获取地址&#xff1a;https://openaccess.thecvf.com/content/CVPR2022/papers/Lang_Learning_What_Not_…

Linux--线程控制--线程相关函数--tid--0109 10

1.如何理解线程 定义&#xff1a;在一个程序里的一个执行路线就叫做线程&#xff08;thread&#xff09;。 更准确的定义是&#xff1a;线程是“一个进程内部的控制序列”。 每个进程都有自己的进程地址空间和task_struct结构体&#xff0c;如果我们通过一定的方式在创建进程…

【记录】ChatGPT|近期两次更新一览(更新至2023年1月12日)

如果你还没有使用过ChatGPT&#xff0c;可以先看看我的上一篇文章&#xff1a;【记录】ChatGPT&#xff5c;注册流程、使用技巧与应用推荐&#xff08;更新至2022年12月14日&#xff09;。   昨天晚上&#xff0c;ChatGPT突然很多人都无法登录&#xff0c;包括我。我当时以为…