网络云相册实现--nodejs后端+vue3前端

目录

主页面

功能简介

系统简介

api

数据库表结构

代码目录

运行命令

主要代码

server

apis.js

encry.js

mysql.js

upload.js

client3

index.js

完整代码


主页面

功能简介

  • 多用户系统,用户可以在系统中注册、登录及管理自己的账号、相册及照片。

  • 每个用户都可以管理及维护相册,及相册的备注。

  • 每个用户都可以管理及维护照片,及照片的备注。

  • 相册需要可设置是否公开。

  • 照片是可评论的,只有一级评论(不需要评论分级)。

  • 分享界面(前台界面),需要图片放大预览及轮播图功能。

  • 图片删除需要回收站。

系统简介

系统采用前后端分离的方式b-s方式,后台使用nodejs技术,数据库采用MySQL系统。前端使用vue3框架搭建。

后端是负责提供接口(api)

api

用户管理
用户注册(post)/api/userlogin用户名、密码
修改密码(post)/api/userpasswordmodify原始密码、新密码
相册管理
新建相册(get)/api/addalbum相册名及简介
修改相册(get)/api/modifyalbum
移除相册(get)/api/removealbum相册必须为空才可以移除
照片管理
上传照片(*)/api/addpic加上备注
修改(备注)(post)/api/modifyps修改备注
删除照片(get)/api/removepic
评论
新增评论(get)/api/addcomment
移除评论(get) /api/removecomment

数据库表结构

users
id无序号、递增
createAt创建时间
updateAt最后更新时间
username用户名
password密码
pics
id无序号、递增
createAt创建时间
updateAt最后更新时间
url存放图片上传后相对服务器访问的地址(相对地址)
ps图片的备注
removed图片是否被移除
userid照片隶属于哪个用户
albums
id无序号、递增
createAt创建时间
updateAt最后更新时间
title相册名称
ps备注
userid相册隶属于哪个用户
comments
id无序号、递增
createAt创建时间
updateAt最后更新时间
content评论内容
userid发表评论的用户
picid被评论的照片

代码目录

运行命令

后端启动命令:npm start

前端启动命令:npm run dev

主要代码

server

apis.js

var express = require('express')
var router = express.Router()
//引入封装的mysql访问函数
const query = require('../utils/mysql')
const encry = require('../utils/encry')
const jwt = require('jsonwebtoken')
const { expressjwt } = require('express-jwt')
const key = 'yuaner'
//引入上传对象
const upload = require('../utils/upload')//用户注册的api
router.post('/userreg', async (req, res) => {//来自url的参数,使用req.query来获取//来自请求体的参数,使用req.body来获取const { username, password } = req.body//判断用户名是否重复const result = await query('select * from users where username=?', [username,])if (result.length > 0) {res.json({flag: false,msg: '用户名已存在',})} else {//插入await query('insert into users (username,password,createAt,updateAt) values (?,?,?,?)',[username, encry(password), new Date(), new Date()])res.json({flag: true,msg: '用户注册成功',})}
})//用户登录
router.post('/userlogin', async (req, res) => {//获取参数const { username, password } = req.bodyconst result = await query('select * from users where username=? and password=?',[username, encry(password)])if (result.length > 0) {res.json({flag: true,msg: '登录成功',token: jwt.sign({ userid: result[0].id }, key, {expiresIn: '10h',}),})} else {res.json({flag: false,msg: '用户名或密码错误',})}
})router.all('*',expressjwt({ secret: key, algorithms: ['HS256'] }),function (req, res, next) {next()}
)/*** 新建相册*/
router.post('/addalbum', async (req, res) => {//获取数据const { title, ps } = req.bodyconst userid = req.auth.useridconst result = await query('select * from albums where title=? and userid=?',[title, userid])if (result.length > 0) {res.json({flag: false,msg: '同名相册已存在',})} else {await query('insert into albums (title,ps,userid,createAt,updateAt) Values(?,?,?,?,?)',[title, ps, userid, new Date(), new Date()])res.json({flag: true,})}
})/*** 修改相册* /modifyalbum* 参数 title ,ps,albumid*/
router.post('/modifyalbum', async (req, res) => {const { title, ps, albumid } = req.body//从token中获取useridconst userid = req.auth.useridconst result = await query('select * from albums where title=? and userid=? and id<>?',[title, userid, Number(albumid)])if (result.length > 0) {res.json({flag: true,msg: '相册名已存在',})} else {//进行修改的查询await query('update albums set title=?,ps=? where id=?', [title,ps,albumid,])res.json({flag: true,msg: '修改成功',})}
})/*** 移除相册* /removealbum* 参数 albumid,userid*//**
router.get('/removealbum', async (req, res) => {//获取参数const { albumid } = req.body //判断当前相册是否为空const result = await query('select COUNT(*) as count from pics where albumid = ?',[albumid])if (result[0].count > 0) {res.json({flag: false,msg: '相册不为空,请先移除所有照片再删除相册',})}await query('delete from albums where id = ?', [albumid])res.json({flag: true,msg: '相册已删除',})
})*/router.get('/removealbum', async (req, res) => {//获取参数let { albumid } = req.queryalbumid = Number(albumid)//获取useridconst userid = req.auth.userid//判断当前相册是否为空const result = await query(//可以限制为1,不用查询很多,此处用'limit 1'进行优化'select * from pics where albumid=? limit 1',[albumid])if (result.length == 0) {//如果为空则删除//删除工作不需要赋值,直接waitawait query('delete from albums where id=? and userid=?', [albumid,userid,])res.json({flag: true,msg: '删除成功!',})}//如果不为空则不能删除else {res.json({flag: false,msg: '相册不为空',})}
})/*** 分页查看相册内的图片列表* /getPiclist* 参数:albumid、pageIndex、pageRecord、、* 需要每一页记录数* 当前页数*/
router.get('/getPicList', async (req, res) => {//获取参数let { albumid, pageIndex, pageRecord } = req.queryconst result = await query('select * from pics where albumid=? and removed =0 ORDER BY updateAt desc limit ?,? ',[Number(albumid),(pageIndex - 1) * pageRecord,Number(pageRecord),])const result2 = await query('select count(*) as t from pics where albumid=? and removed =0 ',[Number(albumid)])res.json({flag: true,result: result,pageCount: Math.ceil(result2[0].t / pageRecord),})
})/*** 获取当前用户的相册列表* /getAlbumList*/ router.get('/getAlbumList', async (req, res) => {//获取参数const { userid } = req.authlet result = await query('select a.id,a.title,a.ps,count(a.id) as t,max(b.url) as url from albums a ' +'left join pics b on a.id = b.albumid ' +'where a.userid=? ' +'group by a.id,a.title,a.ps,a.userid',[Number(userid)])result = result.map(item => {if (!item.url) {item.t = 0 //'/default.jpg' 是 public作为根 // item.url='/default.jpg'}return item})res.json({flag: true,result: result,})
})
/*** 上传图片*/
router.post('/addpic', upload.single('pic'), async (req, res) => {// 图片上传的路径由multer写入req.file对象中const path = req.file.path.split('\\').join('/').slice(6)//除了上传的文件之外,其他的表单数据也被multer存放在req.body中const ps = req.body.ps//userid由token解析得到const albumid = req.body.albumid//存储到数据库中await query('insert into pics (url,ps,removed,albumid,createAt,updateAt) values (?,?,?,?,?,?)',[path, ps, 0, Number(albumid), new Date(), new Date()])res.json({flag: true,msg: '照片添加成功! very good!',})
})/*** 照片删除* 接口地址:deletepic* 客户端参数:picid* 接受客户端参数,将数据库中的记录删除,并返回客户端删除成功* /api/removepic*/
router.get('/deletepic', async (req, res) => {const { picid } = req.query //or const picid =req.query.picidawait query('delete  from pics where id=?',[Number(picid)],res.json({flag: true,msg: '照片删除成功',}))
})/*** 照片放入回收站* 接口地址:removepic* 客户端参数:picid* 接受客户端参数,将数据库中的记录删除,并返回客户端删除成功* /api/removepic*/
router.get('/removepic', async (req, res) => {const { picid } = req.query //or const picid =req.query.picidawait query('update pics set removed=1 where id=?',[picid],res.json({flag: true,msg: '照片已放入回收站',}))
})/*** 修改照片备注* modifyps,ps* 参数picid*/
router.post('/modifyps', async (req, res) => {//获取参数const { picid, ps } = req.body//修改,调用数据库await query('update pics set ps=?,updateAt=? where id=?', [ps,new Date(),Number(picid),])res.json({flag: true,msg: '备注修改成功!',})
})/*** 新增评论* addComment* 参数:content,userid(req.auth)、picid* 类型:post*/router.post('/addComment', async (req, res) => {//获取参数const { picid, content } = req.bodyconst { userid } = req.authawait query('insert into comments (content,createAt,updateAt,userid,picid) values (?,?,?,?,?)',[content, new Date(), new Date(), Number(userid), Number(picid)])res.json({flag: true,msg: '评论成功',})
})/*** 删除评论* deleteComment* 参数:commitid,content,userid(req.auth)* 类型:get* 删除前需保证当前用户是这条评论的发表人才能删除*/
router.get('/deleteComment', async (req, res) => {//获取参数const { commentid } = req.queryconst { userid } = req.authconst result = await query('select a.id from comments a' +' left join pics b ON a.picid=b.id' +' left join albums c ON b.albumid=c.id' +' where a.id=? and (a.userid=? or c.userid=?)',[Number(commentid), Number(userid), Number(userid)])if (result.length > 0) {await query('delete from comments where id=?', [Number(commentid),])res.json({flag: true,msg: '删除成功',})} else {res.json({flag: false,msg: '权限不足',})}// let key=false// const result = await query(//   'delete * from comments where id=? and userid =? limit 1)',//   [Number(userid),Number(commentid)]// )// if(result.length>0){//   key=true// }// if(key==false){//   const result=await query('select *from comment where id=?',[Number(commentid)])//   const picid=result[0].picid// }// res.json({//   flag: true,//   msg: '删除成功',// })
})/*** 评论列表* /getCommentList* 参数:picid、pageIndex、pageRecord* get*/
router.get('/getCommentList', async (req, res) => {//获取参数,需要后续修改,不使用constlet { picid, pageIndex, pageRecord } = req.queryconst result = await query('select * from comments a' +' left join pics b ON a.picid=b.id' +' where picid=? and b.removed=0 order by a.updateAt desc limit ?,? ',[Number(picid),Number(pageIndex - 1) * pageRecord,Number(pageRecord),])res.json({flag: true,result: result,})
})module.exports = router

encry.js

//导入包
const crypto = require('crypto')//导出一个函数(方法)
module.exports = password => {//创建一个加密对象sha1、md5const encry = crypto.createHash('sha1')//将要加密的字符串存入加密对象中encry.update(password)//将解密后的结果以hex的方式输出,hex就是将数字以字符+数字的方式进行输出return encry.digest('hex')
}

mysql.js

//封装mysql的连接,导出一个执行sql语句并返回结果的函数//先引入mysql的驱动--也就是mysql2      //const用来替代var
const mysql = require(`mysql2`)//创建连接池   {}是存对象的
const pool = mysql.createPool({//极限值connectionLimit: 10, //默认最大的连接数量host: `127.0.0.1`,user: `root`,password: '123456', //填自己的密码database: 'album',
})
//query函数用来执行sql语句
//参数是sql语句及参数    //nodejs是一个弱类型    //lamada表达式
const query = (sql, params) =>new Promise(//promise对象将异步操作包装成可控的结果//resolve,reject两个参数,一个成功,一个失败(resolve, reject) => {//先从连接池中取出一条连接  //异步操作pool.getConnection((err, connection) => {//如果失败。执行reject,表示失败if (err) {reject(err) //拿取连接失败,则直接返回失败} else {connection.query(sql, params, (err, result) => {//查询完成后,无论结果是什么,都不再需要连接//将连接换回连接池connection.release()if (err) {reject(err)} else {resolve(result)}})}})})//导出query函数
module.exports = query

upload.js

//引入multer、fs(读写操作模块)、path(路径模块)、
const multer = require('multer')
const fs = require('fs')
const path = require('path')//创建磁盘存储引擎
const storage = multer.diskStorage({ destination, filename })//创建上传对象
const upload = multer({ storage })//它是上传时目录的生成函数
function destination(req, res, callback) {const date = new Date()//创建动态目录const path = `public/upload/${date.getFullYear()}/${date.getMonth() + 1}`//生成路径fs.mkdirSync(path, { recursive: true })callback(null, path)
}
//用来自动生成随机的不重复的文件名
function filename(req, file, callback) {//file.originalname 是上传文件的原始名//a.jpg a.b.c.jpg//a.b.c.jpg   >['a','b','c','jpg']   扩展名console.log(file)const arr = file.originalname.split('.')const extname = arr[arr.length - 1]const date = new Date()const filename = date.getTime() + Math.random() + '.' + extnamecallback(null, filename)
}module.exports = upload

client3

index.js

import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes: [{path: '/',//有人访问根,我导向/home(重定向)redirect: '/home',},{path: '/home',name: 'home',component: HomeView,children: [{path: 'albummanage',component: () => import('../views/AlbumManage.vue'),},{path: 'album/:id',component: () => import('../views/PicsView.vue'),},],},{path: '/login',name: 'login',component: () => import('../views/LoginView.vue'),},{path: '/reg',name: 'reg',component: () => import('../views/RegView.vue'),},],
})//全局路由前置守卫
router.beforeEach(async (to, from) => {//从本地存储中尝试获取tokenif (to.name == 'home' && !localStorage.getItem('token')) {return { name: 'login' }}
})
export default router

完整代码

更新ing~

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

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

相关文章

众人帮蚂蚁帮任务平台修复版源码,含搭建教程。

全修复运营版本的任务平台&#xff0c;支持垂直领域细分&#xff0c;定向导流&#xff0c;带有排行榜功能&#xff0c;任务发布上传审核&#xff0c;用户信用等级&#xff0c;充值接口等等均完美可用。支付对接Z支付免签接口&#xff0c;环境配置及安装教程都已经打包。 搭建环…

C语言调试宏全面总结(六大板块)

C语言调试宏进阶篇&#xff1a;实用指南与案例解析C语言调试宏高级技巧与最佳实践C语言调试宏的深度探索与性能考量C语言调试宏在嵌入式系统中的应用与挑战C语言调试宏在多线程环境中的应用与策略C语言调试宏在并发编程中的高级应用 C语言调试宏进阶篇&#xff1a;实用指南与案…

【Linux】网络基础_4

文章目录 十、网络基础5. socket编程网络翻译服务 未完待续 十、网络基础 5. socket编程 网络翻译服务 基于UDP&#xff0c;我们实现一个简单的翻译。 我们导入之前写的代码&#xff1a; InetAddr.hpp&#xff1a; #pragma once#include <iostream> #include <sys…

开源智能低代码自动化助手:Obsei

**Obsei&#xff1a;**低代码&#xff0c;高效率&#xff0c;Obsei让AI自动化触手可及。- 精选真开源&#xff0c;释放新价值。 概览 Obsei是一款开源的低代码人工智能自动化工具&#xff0c;它为企业提供了一套灵活的解决方案&#xff0c;以应对日益增长的数据处理需求。该工…

uvm_config_db 和 uvm_resource_db :

uvm_config_db class my_driver extends uvm_driver;int my_param;function new(string name, uvm_component parent);super.new(name, parent);endfunctionvirtual task run_phase(uvm_phase phase);// 在组件内部获取配置值if (!uvm_config_db#(int)::get(this, ""…

[Git][远程操作]详细讲解

1.理解分布式版本控制系统 形象理解&#xff1a;每个⼈的电脑上都是⼀个完整的版本库 这样⼯作的时候&#xff0c;就不需要联⽹了&#xff0c; 因为版本库就在⾃⼰的电脑上 既然每个⼈电脑上都有⼀个完整的版本库&#xff0c;那多个⼈如何协作呢&#xff1f; 例如&#xff1a;…

ajax图书管理项目

bootstrap弹框 不离开当前页面&#xff0c;显示单独内容&#xff0c;让用户操作 功能&#xff1a;不离开当前页面&#xff0c;显示单独内容&#xff0c;供用户操作步骤&#xff1a; 1.引入bootstrap.css和bootstrap.js …

Stegdetect教程:如何用Stegdetect检测和破解JPG图像隐写信息

一、Stegdetect简介 Stegdetect 是一个开源工具&#xff0c;专门设计用于检测图像文件&#xff08;JPG格式&#xff09;中的隐写信息。Stegdetect 可以检测多种常见的隐写方法&#xff0c;比如 JSteg、JPHide 和 OutGuess 等。 二、使用Stegdetect检测图像隐写 官方描述&#…

NSS [SWPUCTF 2022 新生赛]file_master

NSS [SWPUCTF 2022 新生赛]file_master 开题&#xff0c;一眼文件上传。 network看看返回包。后端语言是PHP。 除了文件上传还有个查看文件功能。 起手式查询/etc/passwd&#xff0c;发现查询方法是GET提交参数&#xff0c;后端使用file_get_contents()函数包含文件。同时有op…

企业级业务架构设计探讨

引言 在数字化转型的浪潮中&#xff0c;企业业务架构的设计成为了连接企业战略与技术实现的桥梁&#xff0c;其重要性日益凸显。本文探讨企业级业务架构的设计原则、流程、工具和技术实现&#xff0c;并结合具体案例&#xff0c;为读者提供参考。 一、设计原则&#xff1a;奠…

KubeSphere 部署的 Kubernetes 集群使用 GlusterFS 存储实战入门

转载&#xff1a;KubeSphere 部署的 Kubernetes 集群使用 GlusterFS 存储实战入门 知识点 定级&#xff1a;入门级 GlusterFS 和 Heketi 简介 GlusterFS 安装部署 Heketi 安装部署 Kubernetes 命令行对接 GlusterFS 实战服务器配置(架构1:1复刻小规模生产环境&#xff0c;…

新手学习Gazebo+ros仿真控制小车-----易错和自己理解

赵虚左老师讲的很详细&#xff0c;这里只是理一下思路&#xff0c;说下突然出现“新”概念之间的关系。 urdf文件:里面是配置模型的&#xff0c;既有模型的位置、尺寸、颜色&#xff0c;也包含复杂的物理模型信息比如&#xff1a;转动惯量&#xff0c;碰撞box大小等等&#xff…

黑马Java零基础视频教程精华部分_11_面向对象进阶(3)_抽象类、接口、适配器

《黑马Java零基础视频教程精华部分》系列文章目录 黑马Java零基础视频教程精华部分_1_JDK、JRE、字面量、JAVA运算符 黑马Java零基础视频教程精华部分_2_顺序结构、分支结构、循环结构 黑马Java零基础视频教程精华部分_3_无限循环、跳转控制语句、数组、方法 黑马Java零基础视…

书生大模型基础岛-第二关:8G 显存玩转书生大模型 Demo

1.来源 https://github.com/InternLM/Tutorial/blob/camp3/docs/L1/Demo/task.md 2.过程 在 /root/share/pre_envs 中配置好了预置环境 icamp3_demo conda activate /root/share/pre_envs/icamp3_demo创建一个目录&#xff0c;用于存放我们的代码。并创建一个 cli_demo.py …

【hive】HiveSQL中两个json解析函数的使用json路径定位小工具

文章目录 1.HiveSQL中两个json解析函数1&#xff09;get_json_object2&#xff09;json_tuple 2.json中key所在层级路径定位小工具 关于json&#xff1a; https://blog.csdn.net/atwdy/article/details/124668815 1.HiveSQL中两个json解析函数 1&#xff09;get_json_object …

C语言程序设计-[3] 运算符和表达式

C语言的运算符也存在优先级和结合性的概念&#xff0c;在同一表达式中&#xff0c;优先级高的先结合&#xff0c;优先级相同时&#xff0c;就需要考虑结合性(分为左结合性和右结合性——对于单目、三目和赋值运算符表达式&#xff0c;从右至左运算&#xff1b;其他运算符表达式…

【Mind+】掌控板入门教程04 迷你动画片

还记得小时候每天放学必看的动画片吗&#xff1f;还记得那些年陪伴我一起长大的卡通人物吗&#xff1f;勇救爷爷的葫芦娃&#xff0c;我们的朋友小哪吒&#xff0c;相信这些经典的动画形象已经成为了一代人童年的美好回忆。今天就让我们用掌控板来制作一部迷你动画片吧。 项目示…

什么是云原生?

1. 前言 停下手头的工作&#xff0c;让你的同事定义“云原生”一词。你很可能会得到几个不同的答案。 1.1 让我们从一个简单的定义开始&#xff1a; 云原生架构和技术是一种设计、构建和操作在云中构建并充分利用云计算模型的工作负载的方法。 1.2 云原生计算基金会给出了官方…

Godot的节点与场景

要深入的理解节点与场景&#xff0c;我们需要跳出这两个概念来看他。说的再直白一些godot本质就是一个场景编辑器&#xff01; 场景的概念应该在我们平时看电影看电视时会经常提到&#xff0c;比如某一个打斗的场景&#xff0c;这个场景可能会被设在某一个街道&#xff0c;那么…

数据湖之Hudi

Apache Hudi&#xff08;Hadoop Upserts Deletes and Incrementals&#xff09;是一个用于管理大规模数据湖的开源框架&#xff0c;旨在高效地进行数据的插入、更新和删除操作&#xff0c;并支持流式数据的处理。Hudi 的设计目标是解决传统数据湖在数据管理和查询性能上的不足&…