- 首页音乐列表加载
- 添加音乐
- 删除音乐
- 修改音乐
"dependencies": {
"art-template": "^3.0.3", // 浏览器端使用的模板引擎
"body-parser": "^1.15.2", // 解析处理表单 post 请求体
"bootstrap": "^3.3.7", // UI 框架
"ejs": "^2.5.2", // 和 Express 结合使用的模板引擎
"express": "^4.14.0", // 基于node的快速 Web 开发框架
"formidable": "^1.0.17", // 处理表单文件上传
"jquery": "^3.1.1", // 前端DOM操作库
"moment": "^2.15.2", // 处理时间的一个js库
"mysql": "^2.11.1" // 操作 MySQL 数据库的一个驱动包
}
- 装node,node版本6.0+
- 开wampserver,wampserver是一个web开发环境,wampserver自带mysql
- 装navicat,连接mysql,wampserver默认mysql用户名root,端口3306,密码为空。 navicat是一个数据库管理工具。
- 新建数据库,数据库名称是『codepackage』
- 在数据库下新建一张表,其中id字段设置自动替增、设为主键
- 保存表,表的名字是『music』 6.『music』文件夹下打开命令窗口,敲 『node app.js』,浏览器地址『http://localhost:3000/』下查看效果
GET / 渲染首页index.html
GET /musicList 发送音乐列表数据
GET /add 渲染添加音乐页面add.html
POST /add 处理添加音乐请求
GET /edit?id=xxx 渲染编辑音乐页面edit.html
POST /edit?id=xxx 处理编辑音乐请求
GET /remove?id=xxx 处理删除音乐请求
GET /musicList 从数据库获取数据,渲染页面index.html
GET /add 渲染添加音乐页面add.html
POST /add 处理添加音乐请求
GET /edit?id=xxx 渲染编辑音乐页面edit.html
POST /edit?id=xxx 处理编辑音乐请求
GET /remove?id=xxx 处理删除音乐请求
播放音乐
// router.js文件代码
router
// 设置路由
.get('/', handler.showIndex)
// handler.js文件代码,后台处理代码,具体的路由处理函数
exports.showIndex = (req, res) => {
res.render('index')
}
// router.js文件代码
router
.get('/musicList', handler.getMusicList)
// db.js文件代码。用到了node-mysql中间件。封装了一个数据库处理方法
const mysql = require('mysql')
// 1. 创建一个连接池
// 数据库的名字是hello
const pool = mysql.createPool({
// 这里可以配置连接池中的连接对象数量
connectionLimit: 500,
host: 'localhost',
user: 'root',
password: '',
database: 'codepackage'
})
exports.query = function(sql, params = [], callback) {
// 2. 从连接池拿一个可用的连接
pool.getConnection((err, connection) => {
if (err) {
// 这里一般就是拿不到连接对象了才会报错
// 就是池子里的连接对象用完了
// 数据库本身也默认有一个最大连接数
return callback(err)
}
// 3. 操作数据库
connection.query(sql, params, (err, rows) => {
if (err) {
return callback(err)
}
callback(null, rows)
// 4. 将连接释放回连接池
connection.release()
})
})
}
// handler.js文件代码
exports.getMusicList = (req, res) => {
db.query('SELECT * FROM music', (err, rows) => {
if (err) {
throw err
}
console.log(rows)
// 返回一个 JSON字符串
res.json({
list: rows
})
})
}
// index.html文件代码
// 模板文件,arttemplate库渲染模板
<script type="text/template" id="tpl">
{{ each list as value index }}
<tr>
<td>{{index+1}}</td>
<td>{{value.title}}</td>
<td>{{value.singer}}</td>
<td>
<img width="100" src="uploads/{{value.img}}" alt="">
</td>
<td>
<span data-music="{{value.music}}" class="glyphicon glyphicon-play">
</span>
</td>
<td>
<a href="/edit?id={{value.id}}">修改</a>
<a data-id="{{value.id}}" class="remove" href="#">删除</a>
</td>
</tr>
{{/each}}
</script>
// index.html文件代码,ajax请求,根据服务器响应的数据渲染模板
function loadData() {
$.ajax({
url: '/musicList',
type: 'get',
dataType: 'json',
success: function(data) {
// {list: Array[{},{}]}
// console.log(data)
// template(id, data)
// 根据 id 渲染模板。内部会根据document.getElementById(id)查找模板。
console.log(data)
$('#tbody').html(template('tpl', data))
}
})
}
// index.html文件代码
// http://localhost:3000/add
<a class="btn btn-success" href="/add">添加歌曲</a>
// router.js文件代码
router
// 设置路由
.get('/add', handler.showAdd)
// handler.js文件代码,具体路由处理函数
exports.showAdd = (req, res) => {
res.render('add')
}
// add.html文件代码
<!--
复杂表单提交:
method: post
enctype: multipart/form-data
-->
<form action="/add" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="title">标题</label>
<input type="text" class="form-control" id="title" name="title" placeholder="请输入音乐标题">
</div>
<div class="form-group">
<label for="artist">歌手</label>
<input type="text" class="form-control" id="singer" name="singer" placeholder="请输入歌手名称">
</div>
<div class="form-group">
<label for="music_file">音乐</label>
<!-- accept: List of types the server accepts, typically a file type. IE10+-->
<input type="file" id="music" name="music" accept="audio/*">
<p class="help-block">请选择要上传的音乐文件.</p>
</div>
<div class="form-group">
<label for="image_file">海报</label>
<input type="file" id="poster" name="img" accept="image/*">
<p class="help-block">请选择要上传的音乐海报.</p>
</div>
<button type="submit" class="btn btn-success">点击添加</button>
</form>
// router.js文件代码
router
.post('/add', handler.doAdd)
// handler.js文件代码
// 用到了formidable插件
exports.doAdd = (req, res) => {
const form = new formidable.IncomingForm()
form.uploadDir = config.uploadDir // 配置上传文件的路径
form.keepExtensions = true // 保持扩展名
form.maxFieldsSize = 20 * 1024 * 1024 // 配置上传文件的大小
form.parse(req, (err, fields, files) => {
if (err) {
throw err
}
// 回调函数中的 fields 就是表单中的普通字段
// files 就是文件信息
// { title: 'dg', singer: 'hf' }
// {{music},{img}}
// console.log(fields)
// console.log(files)
const title = fields.title
const singer = fields.singer
// F:\05-work\02-codePackage\10BasicalNode\07-Node基础-第7天-2016年10月25日-{Mysql基本使用、sql语句、
//数据增删改、留言本、网页
// 版音乐播放器}\musicMyAnalysis\uploads\upload_8aab3ac004b7290f6bccc04953283409.mp3
// console.log(files.music.path)
const music = path.basename(files.music.path)
// upload_8aab3ac004b7290f6bccc04953283409.mp3
// console.log(music)
const img = path.basename(files.img.path)
// 向mysql数据库插入一条数据,引入了db.js文件
db.query('INSERT INTO music(title, singer, music, img) VALUES(?, ?, ?, ?)', [
title,
singer,
music,
img
], (err, rows) => {
if (err) {
throw err
}
res.redirect('/')
})
})
}
// index.html文件代码
// http://localhost:3000/edit?id=6
<td>
<a href="/edit?id={{value.id}}">修改</a>
<a data-id="{{value.id}}" class="remove" href="#">删除</a>
</td>
// router.js文件代码
router
.get('/edit', handler.showEdit)
// handler.js文件代码
exports.showEdit = (req, res) => {
const id = req.query.id
// console.log(id)
// 按id查询数据库
db.query('SELECT * FROM music WHERE id=?', [id], (err, rows) => {
if (err) {
throw err
}
/*[ RowDataPacket {
id: '1',
title: '女儿情',
singer: '万晓利',
music: 'upload_3c7892ecbd4d0a34945f75f92cc4a9d9.mp3',
img: 'upload_ae4f4136e27689b81be6d326b7703dc9.jpg' } ]*/
// console.log(rows)
res.render('edit', {
music: rows[0]
})
})
}
// edit.html文件代码
// ejs模板引擎渲染
<form id="form" action="/edit?id=<%= music.id %>" method="post">
<div class="form-group">
<label for="title">标题</label>
<input type="text" class="form-control" id="title" name="title"
placeholder="请输入音乐标题" value="<%= music.title %>">
</div>
<div class="form-group">
<label for="artist">歌手</label>
<input type="text" class="form-control" id="singer" name="singer" placeholder="请输入歌手名称"
value="<%= music.singer %>">
</div>
<!-- <div class="form-group">
<label for="music_file">音乐</label>
<input type="file" id="music_file">
<p class="help-block">请选择要上传的音乐文件.</p>
</div>
<div class="form-group">
<label for="image_file">海报</label>
<input type="file" id="image_file">
<p class="help-block">请选择要上传的音乐海报.</p>
</div> -->
<button type="submit" class="btn btn-success">确定修改</button>
</form>
// edit.html 文件代码
<form id="form" action="/edit?id=<%= music.id %>" method="post">
<div class="form-group">
<label for="title">标题</label>
<input type="text" class="form-control" id="title" name="title" placeholder="请输入音乐标题"
value="<%= music.title %>">
</div>
<div class="form-group">
<label for="artist">歌手</label>
<input type="text" class="form-control" id="singer" name="singer" placeholder="请输入歌手名称"
value="<%= music.singer %>">
</div>
<!-- <div class="form-group">
<label for="music_file">音乐</label>
<input type="file" id="music_file">
<p class="help-block">请选择要上传的音乐文件.</p>
</div>
<div class="form-group">
<label for="image_file">海报</label>
<input type="file" id="image_file">
<p class="help-block">请选择要上传的音乐海报.</p>
</div> -->
<button type="submit" class="btn btn-success">确定修改</button>
</form>
// edit.html文件代码
$('#form').on('submit', function (e) {
// 把表单的默认行为干掉,因为提交后要根据服务器返回的数据进行判断后,再进行页面跳转。单纯使用表单默认提交,无法做后续操作。
e.preventDefault()
var url = $(this).attr('action')
var type = $(this).attr('method')
// 3
// console.log($(this).serialize())
$.ajax({
url: url,
type: type,
data: $(this).serialize(),
dataType: 'json',
success: function (data) {
// Object {code: 2000, msg: "uploads success"}
// 打印了,但是页面又立即跳转了,所以看不到
// console.log(data)
var code = data.code
if (code === 2000) {
// 删除成功,跳转到首页
window.location.href = '/'
} else if (code === 2001) {
window.alert('更新失败,请重试')
}
}
})
})
// router.js文件代码
router
.post('/edit', handler.doEdit)
// handler.js文件代码
exports.doEdit = (req, res) => {
// { id: '5' }
// console.log(req.query)
// { title: 'dgf', singer: '电话' }
// console.log(req.body)
const id = req.query.id
// 用到了body-parser中间件
const title = req.body.title
const singer = req.body.singer
// 数据存入数据库之前,一定要做数据安全校验
// 更新数据库中根据id找到的那一条数据
db.query('UPDATE music SET title=?, singer=? WHERE id=?', [
title, singer, id
], (err, rows) => {
if (err) {
throw err
}
if (rows.affectedRows !== 1) {
return res.json({
code: 2001,
msg: 'update failed'
})
}
res.json({
code: 2000,
msg: 'uploads success'
})
})
}
// index.html文件代码
<a data-id="{{value.id}}" class="remove" href="#">删除</a>
// index.html文件代码
$('body').on('click', '.remove', function(e) {
if (window.confirm('确定要删除?')) {
var id = $(this).data('id')
$.ajax({
url: '/remove',
type: 'get',
data: {
id: id
},
dataType: 'json',
success: function(data) {
if (data.code === 1000) {
loadData()
} else if (data.code === 1001) {
window.alert('删除失败')
}
}
})
}
})
// router.js文件代码
router
.get('/remove', handler.doRemove)
// handler.js文件代码
exports.doRemove = (req, res) => {
const id = req.query.id
db.query('DELETE FROM music WHERE id=?', [id], (err, rows) => {
if (err) {
throw err
}
if (rows.affectedRows !== 1) {
// 删除歌曲
// 1000
// 1001
return res.json({
code: 1001,
msg: 'remove failed'
})
}
res.json({
code: 1000,
msg: 'remove success'
})
})
}
// index.html文件代码
<audio id="audio" src="uploads/万晓利 - 女儿情.mp3" controls="true"></audio>
// index.html文件代码
// 对于动态加载的内容,记得要使用事件代理的形式注册事件
// 或者将添加事件的行为放到渲染 DOM 之后
$('body').on('click', '.glyphicon-play', function(e) {
var music = $(this).data('music')
// upload_b3db892ab168d7a007c597db80102981.mp3
// console.log(music)
$('#audio').attr('src', 'uploads/' + music).attr('autoplay', true)
})
- 数据库id存什么? 数据库开发过程中的一个原则是不应该使用主键来储存“内容”。URL地址并不唯一,可能出现“?”或者“&”甚至于引号等非常规字符,有些字符在改写为URL时还会被转译(例如人民→“%E4%BA%BA%E6%B0%91”),并且有些网站会在URL中添加无意义的随机字符。这些情况使得储存URL的字段会发生重复、不可索引、空值的情况,应当极力避免。应该使用主键来储存“索引”,例如学生的学号,职员的工号,居民身份证号码等等唯一的字符串。如果在设计数据表的过程中并没有符合作为索引的字段,可以专门设置一个自增字段用来作为主键类型int,勾选Auto Increase选项
- 大部分情况下,修改代码后,要在命令行重新输入node app.js重启服务,才能看到修改代码后的效果。命令行按上下键可以快速切换历史命令。
- 点击上传图片按钮,浏览器反应很慢,原因不知道
- 首页点击播放,音乐已经开始播放了,但是页面图标并没有改变