基于HTTP、Servlet、Maven 实现的核心功能: 1.登录、注册 2.上传音乐 3.删除某一个音乐信息 4.删除选中音乐信息 5.查询音乐(包含查找指定\模糊匹配的音乐) 6.添加音乐到“喜欢列表” 7.查询喜欢的音乐(包含查找指定\模糊匹配的音乐)
重要的知识点:
- 简单的Web服务器设计能力
- Java 操作 MySQL 数据库
- 数据库设计
- JSON 的使用
- 强化 HTTP 协议的理解
- Servlet 的使用
- Java集合的使用
- 前端知识的简单使用如:HTML+CSS+JS
面试问题:一次HTTP 请求过程? TCP三次握手四次挥手,五层协议体系(应用层、传输层、网络层、数据链路层、物理层) 每一层的协议。
整体架构: 项目整体基于HTTP协议,前端使用HTML+CSS+JS构建页面整体布局,后端采用分层结构,分为Servlet层,Service(业务层)层,Dao层的设计,以达到在设计上的高内聚低耦合。
数据库设计:
三张表:
music、user、lovemusic(一个用户可以喜欢多个音乐,一个音乐可以被多个用户喜欢,所以是多对多的关系)
– 数据库 drop database if exists onlinemusic ; create database if not exists onlinemusic character set utf8; – 使用数据库
use `onlinemusic`;
DROP TABLE IF EXISTS `music`;
CREATE TABLE `music` (
`id` int PRIMARY KEY AUTO_INCREMENT,
`title` varchar(50) NOT NULL,
`singer` varchar(30) NOT NULL,
`time` varchar(13) NOT NULL,
`url` varchar(100) NOT NULL,
`userid` int(11) NOT NULL
);
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`username` varchar(20) NOT NULL,
`password` varchar(32) NOT NULL,
`age` INT NOT NULL,
`gender` varchar(2) NOT NULL,
`email` varchar(50) NOT NULL
);
DROP TABLE IF EXISTS `lovemusic`;
CREATE TABLE `lovemusic` (
`id` int PRIMARY KEY AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`music_id` int(11) NOT NULL
);
INSERT INTO user(username,password,age,gender,email)
VALUES("chensiyou","12345","21","男","1746112818@qq.com");
maven管理,Tomcat服务器配置
创建entity(实体包)
创建user、music类
服务器API设计:
-
JSON:将对象转换成字符串,JSON相比XML,文件更小。序列化与反序列化。 -
登录: 请求:POST /loginServlet data:{username,password} JSON组织数据 响应:{msg:true} 3.上传音乐:上传音乐到服务器目录
? POST /uploadServlet (不仅是给数据库写一条数据,还要给服务器目录下上传一条真正的文件)
? data:filename
? 4.删除某一条音乐信息
? POST /deleteServlet
? data:{“id”:id}
? 响应:
? {msg:true}
? 5.删除选中的音乐
? POST /deleteSelMusicServlet ? data:{“id”:id}//id为数组 ? 响应: ? {msg: true}
? 6.查询音乐(指定查找/模糊匹配)
? 请求:
? GET /findMusicServlet ? data:{musicName:musicName}
? 7.添加音乐到喜欢列表
? 请求:
? POST /loveMusicServlet ? data: {“id”: obj} ? 响应: ? {msg: true}
? 8.查询喜欢的音乐(查找指定/模糊匹配)
? 请求: ? GET /findLoveMusicServlet ? data:{musicName:musicName}
? 9.移除某个喜欢的音乐
? 请求:
? POST /removeLoveServlet ? data: {“id”: obj}
封装数据库操作
创建一个util包
创建JDBCUtils类:获取数据源(单例模式),数据库连接、关闭。
创建dao包(通过dao层来操纵MySQL)
创建UserDao类(对数据库中的user表进行操作):
? 1.实现UserDao.login功能:
? PreparedStatement和Statement 的关系和区别:
? 关系: PreparedStatement继承自Statement ,都是接口。
? 区别:PreparedStatement可以使用占位符,是预编译的,批处理比Statement效率高。
? PreparedStatement可以防止sql注入。
? 先获取数据库连接,将查询sql语句放入PreparedStatement的对象中,将?占位符通过
? PreparedStatement的对象.setString()替换成输入的用户名、密码。
?
? 2.实现UserDao.insertUser功能:
?
创建MusicDao类(对数据库中的music表进行操作):
1.实现MusicDao.upload(音乐上传)功能:
?
2.实现findMusic()(查询全部歌单)功能:
?
? 3.实现findMusicById(int id)(根据id查找音乐)功能:
?
? 4.实现findMusicByKey(String str)(根据关键字查找音乐(模糊查询))功能:
?
? 5.实现deleteMusicById(int musicId)(删除歌曲)功能:
? 在删除音乐时,要检查是否在喜欢列表里有数据记录,如果有两个表里都需要删掉。
?
? 6.实现findLoveMusicById(int musicId)(在喜欢列表查看歌曲是否存在)功能:
? 7.实现DeleteLoveMusicById(int musicId)(在喜欢列表删除)功能:
创建LoveMusicDao类(对数据库中的lovemusic表进行操作):
? 1.实现insertLoveMusic(int userId,int musicId)(添加音乐到喜欢列表)功能:
?
? 2.实现findLoveMusic(int user_id)(当前用户喜欢的音乐)功能:
? 3.实现 findLoveMusicByMusicIdAndUserId(int user_id,int musicID)(预防重复喜欢)功能:
? 4.实现findLoveMusicBykeyAndUID(String str,int user_id)(关键字查找喜欢音乐)功能:
? 5.实现removeLoveMusic(int userId,int musicId)(移除喜欢音乐)功能;
Servlet层实现
servlet是一种处于服务器和浏览器的中间层,是一种可以从前端获取http请求,并且构建一个响应回前端页面,实现生成动态页面的效果,三大周期不会
创建LoginServlet类继承自HttpServlet
1.实现登录业务:从HTTP请求中获取username和password
2.上传音乐
上传音乐分为2步: 第一步将音乐上传到服务器 第二步将音乐信息存放到数据库
知识:本质 上传签到 ——》图片 文档
1.前端 form表单 为了支持上传文件,前端采用enctype=“multipart/form-data”。
<form method="POST" enctype="multipart/form-data" action="upload">
文件上传:<input type="file" name="filename"/>
歌手名: <label>
<input type="text" name="singer" placeholder="请输入歌手名"/>
</label>
<input type="submit" value="上传"/>
</form>
-
后端注解:@MultipartConfig -
后端上传核心代码 //先上传服务器
Part part = req.getPart("filename");
//从content-disposition头中获取源文件名
String header = part.getHeader("Content-Disposition");
int start = header.lastIndexOf("=");
String fileName = header.substring(start + 1)
.replace("\"", "");
System.out.println("fileName:"+fileName);
part.write(SAVEPATH + "/" + fileName);
Part类: 使用的是Servlet 3.0 新的特征标注(Annotaion)类描述部署,一些低版本的服务器需要使用标准依赖部署描述文件 (web.xml)来部署,另外Part也是Java EE 6.0新增的类,Part是一个接口继承于javax.servlet.http,代表一部分表单项目接收来自multipart/form-data的POST的请求。 part.getHeader(“Content-Disposition”):通过此方法获取到源文件名称: Content-Disposition: form-data; name=“filename”; filename=“许巍 - 故乡.mp3” Content-Type: audio/mpeg 4.UploadMusicServlet实现 doPost方法:先要获取登录成功的用户对象,如果没有登录,则无法进行上传音乐(从session中取得user对象)。 ? 从前端的请求中获取到传输的数据,通过字符串截取获得要上传的文件名称(如果通过postman自己获取文件名会被加密,需要自己通过URLDecoder.decode()方法解密),使用part.write(存储路径+文件名)方法,将from表单中音乐文件上传到服务器中,此时文件上传成功。 接着要将数据写入数据库中,将数据库表格需要的所有信息都获取出来,通过service层将信息存入数据库。当上传的是同一首歌曲(文件相同)时,不会多次上传(因为使用的Part类的自身特点,但是还是会显示上传成功),因而避免了服务器中有多份相同的文件。 3.FindMusicServlet实现 doGet方法:从前端的请求中获取要查询的歌名,去FindMusicService中调用findMusic方法,这时如果请求中获取到的歌名不为空,则进行模糊匹配查询,如果为空,则进行全部音乐查询。将查询的结果放到musiclist中返回给FindMusicServlet,再通过输出流写到前端页面上。 4.删除音乐信息实现 4.1删除某个音乐(DeleteMusicServlet) doPost方法:首先获取前端参数id,然后在数据库中查找有没有当前id,如果没有当前id的音乐直接返回;如果有就开始删除数据库中的音乐,数据库删除完成后,检查是否删除成功,如果成功,接着删除服务器目录下对应的文件,将删除路径进行拼接,判断文件是否存在,调用delete()方法删除,将删除结果放到hashmap中,最后将map转换为json,传回给前端页面。 bug:如果先删除数据库中信息,就会获取不到url,所以先需要存一份url的值,以免出现NullPointerException,从而只是删除掉数据库中信息,但无法删除服务器中的文件。 4.2删除选中音乐(DeleteSelMusicServlet) doPost方法:删除选中与删除某个音乐逻辑一样,唯一不同的点是获取的是前端选中的id数组,进行循环遍历删除。 优化方案:在dao层写一个事务,将id数组放进去进行全部删除,这样如果中间哪个出问题了,操作回滚。(JDBC开启事务,批量删除) 5.添加喜欢音乐到喜欢列表(LoveMusicServlet) doPost方法:首先从前端获取到要添加到喜欢的音乐的id 6.查找我喜欢的音乐列表(FindLoveMusicServlet) doGet方法: 7.移除我喜欢的音乐(RemoveLoveServlet) 前端页面的设计 前端采用HTML+CSS+JS设计,JS使用jQuery,使用的不是原生的JS,而是调用的jQuery中的方法(ajax) 前端播放使用了audio标签,目前用到这个标签,不好的地方是,需要完整下载好后才能播放。本质上来说,如果可以控制,一边上传,一边播放,这样,实时性比较强,体验感会好。需要达到这样的过程,需要引入协议。而例如音视频传输的协议,一般称为流媒体协议。 1个G的音乐视频,怎么上传? 大文件的上传,可能会失败,比如网络原因等。最好的做法是采取分段上传的方式。 比如:在前端讲这1个G的数据,进行分组,假设分为10组。 前端做法,参考做法:前端分别获取 1、每次发送数据的大小 2、文件总大小(此时就可以知道分为几组了) 3、每次发送的开始位置和结束位置 4、一个二进制对象(这个对象就是你发送的区间内组成的对象) 这个二进制对象,传给后端后,后端每次存储起来。文件名字可以按照-1,-2,-3命名 5、每次发送成功一段后,后端返回成功后,继续发送下一个区间 后端做法: 1、后端可以定义一个数据结构,比如hashmap.来存储每个部分上传的进度。如<部分1,80%>类似这样的。 2、假设前端,在上传某部分失败了,和服务器进行对比,从上传失败的那个地方开始上传即可。(续传) 3、全部上传成功后,循环遍历当前目录下的所有文件,进行合并。合并需要注意,顺序不对,文件会受损。所以,这里的处理就是,文件名后面加了1,2,3,4这样顺序就不会乱了
|