网络云相册实现--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.body

  const 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.body
  const userid = req.auth.userid
  const 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中获取userid
  const userid = req.auth.userid
  const 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.query
  albumid = Number(albumid)
  //获取userid
  const userid = req.auth.userid
  //判断当前相册是否为空
  const result = await query(
    //可以限制为1,不用查询很多,此处用'limit 1'进行优化
    'select * from pics where albumid=? limit 1',
    [albumid]
  )
  if (result.length == 0) {
    //如果为空则删除
    //删除工作不需要赋值,直接wait
    await 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.query

  const 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.auth

  let 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.picid
  await 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.picid
  await 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.body
  const { userid } = req.auth

  await 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.query
  const { userid } = req.auth

  const 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) => {
  //获取参数,需要后续修改,不使用const
  let { picid, pageIndex, pageRecord } = req.query
  const 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、md5
  const 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() + '.' + extname

  callback(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) => {
  //从本地存储中尝试获取token
  if (to.name == 'home' && !localStorage.getItem('token')) {
    return { name: 'login' }
  }
})
export default router

完整代码

更新ing~

  • 13
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要将Node.js + Express + Vue3 + Vite应用程序部署到阿里上,您可以按照以下步骤进行操作: 1. 在阿里上创建一个ECS实例,选择适合您需求的操作系统和配置。确保您在安全组中打开了HTTP(80端口)和HTTPS(443端口)的访问权限。 2. 在本地开发环境中构建Vue3应用程序。在项目根目录下打开终端或命令提示符,运行以下命令: ```bash npm run build ``` 这将使用Vite构建工具编译和打包Vue3应用程序,并将生成的静态文件保存在`dist`目录中。 3. 将Vue3应用程序的静态文件上传到阿里ECS实例。您可以使用FTP工具(如FileZilla)将`dist`目录中的文件上传到ECS实例的`/var/www/html`目录(或您自定义的web目录)。 4. 在ECS实例上安装Node.js和npm。您可以通过SSH远程连接到ECS实例并使用以下命令安装它们: ```bash # 安装Node.js curl -fsSL https://deb.nodesource.com/setup_14.x | sudo -E bash - sudo apt-get install -y nodejs # 安装npm sudo apt-get install -y npm ``` 5. 在ECS实例上创建一个名为`server.js`的文件,并添加以下内容: ```javascript const express = require('express'); const path = require('path'); const app = express(); // 静态文件托管 app.use(express.static(path.join(__dirname, 'html'))); // 所有路由重定向到index.html app.get('*', (req, res) => { res.sendFile(path.join(__dirname, 'html', 'index.html')); }); // 启动服务器 const port = process.env.PORT || 80; app.listen(port, () => { console.log(`Server is running on port ${port}`); }); ``` 在上述示例中,我们使用Express创建了一个简单的服务器。我们通过`express.static`中间件来托管上传的Vue3应用程序的静态文件,然后将所有路由重定向到`index.html`以支持Vue的单页应用。最后,我们使用`app.listen`方法来启动服务器,并指定端口号为80。 6. 在ECS实例上运行以下命令来安装Express和其他项目依赖: ```bash npm install express ``` 7. 在ECS实例上运行以下命令启动Express服务器: ```bash node server.js ``` 这将在ECS实例上启动Express服务器,并监听端口80。 8. 现在,您的Node.js + Express + Vue3 + Vite应用程序已经在阿里ECS实例上运行起来了。您可以在浏览器中访问ECS实例的公网IP地址来查看应用程序。 请注意,上述示例仅提供了一个简单的部署流程,并且假设您已经完成了阿里ECS实例的设置和配置。在实际部署过程中,您可能还需要考虑安全性、性能优化、域名绑定等方面的问题。另外,您可能还需要使用Nginx等工具来配置反向代理或实现负载均衡等功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值