文章目录
1. 项目设计
前端 :
HTML+CSS+JavaScript+JQuery
后端 :
Spring MVC+Spring Boot+MyBatis
2. 效果展示
3. 创建项目 配置文件
3.1 创建项目
3.2 配置文件
3.2.1 在 application.properties 中添加配置文件
配置数据库
spring.datasource.url=jdbc:mysql://localhost:3306/onlinemusicserver?characterEncoding=utf8&useSSL=true
spring.datasource.username=root
spring.datasource.password=0000
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
配置 Mybatis
mybatis.mapper-locations=classpath:mapper/**Mapper.xml
配置文件上传大小
spring.servlet.multipart.max-file-size = 15MB
spring.servlet.multipart.max-request-size=100MB
配置上传的路径
upload.path=E:/logs/
3.2.2 在 resources 目录下创建mapper
mapper下添加 目录 **.xml 并添加代码
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="com.example.onlinemusicserver.mapper."对应的Mapper""></mapper>
4. 数据库的设计与实现
这里设计数据库.
用户表
- 用户Id
- 用户账号
- 用户密码
音乐表
- 音乐Id
- 音乐名
- 音乐歌手
- 上传时间
- 存储地址
- 用户Id
收藏表
- 收藏Id
- 用户Id
- 音乐Id
dropdatabaseifexists`onlinemusicserver`;createdatabase`onlinemusicserver`;use`onlinemusicserver`;droptableifexists`user`;createtable`user`(`userId`intprimarykeyauto_increment,`username`varchar(20)unique,`password`varchar(255)notnull);droptableifexists`music`;createtable`music`(`musicId`intprimarykeyauto_increment,`title`varchar(100)notnull,`author`varchar(20)notnull,`uploadtime`timestampdefaultCURRENT_TIMESTAMP,`path`varchar(1000)notnull,`userId`intnotnull);droptableifexists`collect`;createtable`collect`(`collectId`intprimarykeyauto_increment,`userId`intnotnull,`musicId`intnotnull);
5. 交互接口的设计
上传音乐
请求
POST /music/upload HTTP/1.1
{singer, MultipartFile file}
响应
{
status: 1/-1 (1 为成功, -1 为失败),
message: "对应信息",
data: "内容"
}
收藏功能
请求
POST /collect/loveMusic HTTP/1.1
{musicId: 1}
响应
{
status: 1/-1,
message: "",
data: ""
}
取消收藏功能
请求
POST /collect/deleteLoveMusic HTTP/1.1
{musicId: 1}
响应
{
status: 1/-1,
message: "",
data: ""
}
收集页面 — 空查询 模糊查询
请求
POST /collect/findLoveMusic HTTP/1.1
{musicName: "可以为空可以不为空, 为空的时候,查询所有, 不为空的时候, 模糊查询"}
响应
{
status: 1/-1,
message: "",
data: {
{
musicId: "",
title: "",
author: "",
uploadtime: "",
path: "",
userId: "",
}
...
}
}
主页页面 — 空查询 模糊查询
请求
POST /music/findMusic HTTP/1.1
{musicName: "可以为空可以不为空, 为空的时候,查询所有, 不为空的时候, 模糊查询"}
响应
{
status: 1/-1,
message: "",
data: {
{
musicId: "",
title: "",
author: "",
uploadtime: "",
path: "",
userId: "",
}
...
}
}
删除单个音乐
请求
POST /music/delete HTTP/1.1
{musicId: ""}
响应
{
status: 1/-1,
message: "",
data: ""
}
删除多个音乐
请求
POST /music/deleteMore HTTP/1.1
{musicId: "1 2 3 4 5"(数组)}
响应
{
status: 1/-1,
message: "",
data: ""
}
播放音乐
请求
GET /music/play?path="..." HTTP/1.1
响应
{
音乐的字节信息
}
登录功能
请求
POST /user/login HTTP/1.1
{username: "",password: ""}
响应
{
status: 1/-1,
message: "",
data: ""
}
注销功能
请求
GET /user/logout HTTP/1.1
响应
HTTP/1.1 200
注册功能
请求
POST /user/register HTTP/1.1
{username: "",password: ""}
响应
{
status: 1/-1,
message: "",
data: ""
}
6. 工具包
6.1 设置统一响应类
这个类是用来让响应返回的格式统一的.
publicclassResponseBodyMessage<T>{privateint status;privateString message;privateT data;publicResponseBodyMessage(int status,String message,T data){this.status = status;this.message = message;this.data = data;}}
6.2 Constant类
这个类是用来存储不变的常量的. 例如设置了session对象 , 是一个字符串. 不变的字符串.将来在其他地方获取对应的session需要通过这个字符串获取 .
publicclassConstant{publicstaticfinalString USER_SESSION_KEY ="user";}
6.3 了解 MD5 加密 和 BCrypt 加密
MD5
是一个安全的散列算法,输入两个不同的明文不会得到相同的输出值,根据输出值,不能得到原始的明文,即其过程不可逆; 但是虽然不可逆,但是不是说就是安全的。因为自从出现彩虹表后,这样的密码也"不安全"。
更安全的做法是加盐或者长密码等做法,让整个加密的字符串变的更长,破解时间变慢。
Bcrypt
就是一款加密工具,可以比较方便地实现数据的加密工作。你也可以简单理解为它内部自己实现了随机加盐处理 。我们使用MD5加密,每次加密后的密文其实都是一样的,这样就方便了MD5通过大数据的方式进行破解。
Bcrypt生成的密文是60位的。而MD5的是32位的。Bcrypt破解难度更大。
MD5使用示例 (加盐)
添加依赖
<dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.9</version></dependency>
实现类
publicclassMD5Util{privatestaticfinalString salt ="1q2w3e4r5t";//可任意设置publicstaticStringmd5(String src){returnDigestUtils.md5Hex(src);}/**
* 第一次加密 :模拟前端自己加密,然后传到后端
* @param inputPass
* @return
*/publicstaticStringinputPassToFormPass(String inputPass){String str =""+salt.charAt(1)+salt.charAt(3)+ inputPass
+salt.charAt(5)+ salt.charAt(6);returnmd5(str);}/**
* 第二次加密
* @param formPass 前端加密过的密码,传给后端进行第2次加密
* @param salt 后端当中的盐值
* @return
*/publicstaticStringformPassToDBPass(String formPass,String salt){String str =""+salt.charAt(0)+salt.charAt(2)+ formPass +salt.charAt(5)+ salt.charAt(4);returnmd5(str);}/**
* 上面两个函数合到一起进行调用
* @param inputPass
* @param saltDB
* @return
*/publicstaticStringinputPassToDbPass(String inputPass,String saltDB){String formPass =inputPassToFormPass(inputPass);String dbPass =formPassToDBPass(formPass, saltDB);return dbPass;}}
BCrypt使用示例
添加依赖
<!-- security依赖包 (加密)--><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-web</artifactId></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-config</artifactId></dependency>
在springboot启动类添加:
@SpringBootApplication(exclude ={org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
创建BCryptTest测试类:
publicclassBCryptTest{publicstaticvoidmain(String[] args){//模拟从前端获得的密码String password ="123456";BCryptPasswordEncoder bCryptPasswordEncoder =newBCryptPasswordEncoder();String newPassword = bCryptPasswordEncoder.encode(password);System.out.println("加密的密码为: "+newPassword);//使用matches方法进行密码的校验boolean same_password_result = bCryptPasswordEncoder.matches(password,newPassword);//返回trueSystem.out.println("加密的密码和正确密码对比结果: "+same_password_result);boolean other_password_result = bCryptPasswordEncoder.matches("987654",newPassword);//返回falseSystem.out.println("加密的密码和错误的密码对比结果: "+ other_password_result);}
运行结果: (每次加密的密码都不同)
6.4 在Config中 注入 BCryptPasswordEncoder 对象
@ConfigurationpublicclassAppConfigimplementsWebMvcConfigurer{@BeanpublicBCryptPasswordEncodergetBCryptPasswordEncoder(){returnnewBCryptPasswordEncoder();}}
6.5 添加拦截器
6.5.1 LoginInterceptor 类
publicclassLoginInterceptorimplementsHandlerInterceptor{@OverridepublicbooleanpreHandle(HttpServletRequest request,HttpServletResponse response,Object handler)throwsException{HttpSession httpSession = request.getSession(false);if(httpSession !=null&& httpSession.getAttribute(Constant.USER_SESSION_KEY)!=null){returntrue;}
response.sendRedirect("/login.html");returnfalse;}}
6.5.2 AppConfig 类
@ConfigurationpublicclassAppConfigimplementsWebMvcConfigurer{@OverridepublicvoidaddInterceptors(InterceptorRegistry registry){LoginInterceptor loginInterceptor =newLoginInterceptor();
registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/**/login.html").excludePathPatterns("/**/css/**.css").excludePathPatterns("/**/images/**").excludePathPatterns("/**/fonts/**").excludePathPatterns("/**/js/**.js").excludePathPatterns("/**/scss/**").excludePathPatterns("/**/user/login").excludePathPatterns("/**/user/register").excludePathPatterns("/**/user/logout");}}
7. 登录模块
7.1 创建 User 实体类
创建 model 包, 然后创建 User 类
@DatapublicclassUser{privateint userId;privateString username;privateString password;}
7.2 使用 Mybatis 操作数据库
这里登录 需要进行 数据库的查询. 查询是否存在当前 username 的用户.
所以要设计, 通过用户名查找用户信息
7.2.1 在 UserServer 中添加代码
publicUserselectByName(String username){return userMapper.selectByName(username);}
7.2.2 在 UserMapper 中添加代码
/**
* 通过用户名去查找用户信息, 用来对比登录信息.
* @param username 用户名
* @return 对应用户名的用户信息
*/UserselectByName(String username);
7.2.3 在 UserMapper.xml 中添加代码
<selectid="selectByName"resultType="com.example.onlinemusicserver.model.User">
select * from user where username=#{username};
</select>
7.3 创建 UserConroller 添加代码
注意这里的登录.
- 首先去数据库根据用户名查询是否存在当前用户.
- 如果不存在, 登录失败.
- 如果存在, 用输入的密码, 和数据库中的密码进行比较, 看是否相等. (注: 数据中的密码是加密的)
- 如果不相等, 登录失败.
- 如果相等, 创建 session, 并登录成功.
@RestController@RequestMapping("/user")publicclassUserController{@AutowiredprivateUserServer userServer;@AutowiredprivateBCryptPasswordEncoder bCryptPasswordEncoder;/**
* 用户登录
* @param user
* @param req
* @return
*/@RequestMapping("/login")publicResponseBodyMessage<User>login(@RequestBodyUser user,HttpServletRequest req){User truUser = userServer.selectByName(user.getUsername());if(truUser !=null){System.out.println("登陆成功");System.out.println(user.getPassword()+" "+ truUser.getPassword());boolean flg = bCryptPasswordEncoder.matches(user.getPassword(),truUser.getPassword());if(!flg){returnnewResponseBodyMessage<>(-1,"当前账号密码错误!",user);}HttpSession session = req.getSession(true);
session.setAttribute(Constant.USER_SESSION_KEY,truUser);returnnewResponseBodyMessage<>(1,"登录成功!",truUser);}else{System.out.println("登录失败");returnnewResponseBodyMessage<>(-1,"当前账号密码错误!",user);}}}
7.4 前端代码
let loginButton = document.querySelector('#loginButton');
loginButton.onclick=function(){let username = document.querySelector('#loginUsername');let password = document.querySelector('#loginPassword');if(username.value.trim()==""){alert('请先输入用户名!');
username.focus();return;}if(password.value.trim()==""){alert('请先输入密码!');
password.focus();return;}
$.ajax({url:"user/login",method:"POST",data:JSON.stringify({username: username.value.trim(),password: password.value.trim()}),contentType:"application/json;charset=utf-8",success:function(data, status){if(data.status ==1){
location.assign("index.html");}else{alert(data.message);
username.value="";
password.value="";
username.focus();}}})}
8. 注册模块
8.1 使用 Mybatis 操作数据库
这里注册, 需要查看当前用户是否存在, 存在就不能注册, 通过用户查找, 这里已经实现.
注册一个新用户还需要 向数据库中添加一个新的用户信息.
8.1.1 在 UserServer 中添加代码
publicintaddnewUser(User newUser){return userMapper.addnewUser(newUser);}
8.1.2 在 UserMapper 中添加代码
/**
* 注册新的用户
* @param newUser 新用户信息
* @return
*/intaddnewUser(User newUser);
8.1.3 在 UserMapper.xml 中添加代码
<insertid="addnewUser">
insert into user(username,password) values (#{username},#{password});
</insert>
8.2 向 UserController 中添加代码
- 首先查看是否该用户是否存在
- 存在, 就注册失败
- 不存在, 就进行注册, 首先对当前密码进行加密.
- 加密之后对这个用户添加到数据库中.
/**
* 注册用户
* @param user 用户信息
* @return
*/@RequestMapping("/register")publicResponseBodyMessage<Boolean>register(@RequestBodyUser user){User user1 = userServer.selectByName(user.getUsername());if(user1 !=null){returnnewResponseBodyMessage<>(-1,"当前用户已经存在",false);}else{User newUser =newUser();
newUser.setUsername(user.getUsername());String newPassword = bCryptPasswordEncoder.encode(user.getPassword());
newUser.setPassword(newPassword);
userServer.addnewUser(newUser);returnnewResponseBodyMessage<>(1,"注册成功",true);}}
8.3 前端代码
let Reg = document.querySelector('#Reg');
Reg.onclick=function(){let username = document.querySelector('#RegUsername');let password1 = document.querySelector('#RegPassword1');let password2 = document.querySelector('#RegPassword2');if(!$('#checkbox').is(':checked')){alert("请勾选条款");return;}if(username.value.trim()==""){alert("请先输入用户名!");
username.focus();return;}if(password1.value.trim()==""){alert('请先输入密码!');
password1.focus();return;}if(password2.value.trim()==""){alert('请再次输入密码!');
password2.focus();return;}if(password1.value.trim()!= password2.value.trim()){alert('两次输入的密码不同!');
passwrod1.value="";
password2.value="";return;}
$.ajax({url:"user/register",method:"POST",data:JSON.stringify({username: username.value.trim(),password: password1.value.trim()}),contentType:"application/json;charset=utf-8",success:function(data,status){if(data.status ==1){alert(data.message);
location.assign("login.html");}else{alert(data.message);
username.value="";
password1.value="";
password2.value="";
username.focus();}}})}
9. 退出功能
这里点击退出之后, 直接删除 对应 的 session 即可
9.1 向 UserController 中添加代码
直接删除对应session 为
Constant.USER_SESSION_KEY
, 然后跳转到
login.html
@RequestMapping("/logout")publicvoiduserLogout(HttpServletRequest request,HttpServletResponse response)throwsIOException{HttpSession session = request.getSession(false);// 拦截器的拦截, 所以不可能出现session为空的情况
session.removeAttribute(Constant.USER_SESSION_KEY);
response.sendRedirect("login.html");}
9.2 登录注册测试.
10. 上传音乐模块
10.1 创建 Music 实体类
@DatapublicclassMusic{privateint musicId;privateString title;privateString author;privateTimestamp uploadtime;privateString path;privateint userId;privateString srcPath;}
10.2 使用 Mybatis 操作数据库
上传音乐, 要上传 音乐名, 音乐歌手, 音乐地址, 上传作者Id. (音乐上传时间, 已经默认设置了. 不需要传也可以)
通过音乐名去查找歌曲, 这里用来对当前歌曲判断, 是否出现歌曲和歌手都相同的情况.
10.2.1 在 MusicServer 中添加代码
publicintinsert(String title,String author,String path,int userId){return musicMapper.insert(title,author,path,userId);}publicList<Music>selectByTitle(String title){return musicMapper.selectByTitle(title);}
10.2.2 在 MusicMapper 中添加代码
/**
* 上传音乐
* @param title 音乐名
* @param author 歌手
* @param path 对应的地址
* @param userId 上传的用户Id
* @return 返回影响行数
*/intinsert(String title,String author,String path,int userId);/**
* 通过音乐名去查找歌曲.
* @param title 音乐名
* @return 对应音乐名的所有歌曲
*/List<Music>selectByTitle(String title);
10.2.3 在 MusicMapper.xml 中添加代码
<insertid="insert">
insert into music(title,author,path,userId) values (#{title},#{author},#{path},#{userId});
</insert><selectid="selectByTitle"resultType="com.example.onlinemusicserver.model.Music">
select * from music where title = #{title};
</select>
10.3 向 MusicController 中添加代码
- 这里首先对session判断, 判断是否存在session. (配置拦截器之后就不需要判断了)
- 去数据库中查询所有title相同的歌曲. 如果歌曲名相同,歌手也相同, 那么就上传失败.
- 创建文件夹. 将文件上传到文件夹中.(文件名是以歌手-歌名创建, 为了防止重名无法读取)
- 然后对该文件, 进行判断, 判断是不是 MP3 文件, 注意MP3文件, 字节码中有 字符"TAG"
- 在数据库中上传数据. 注意这里的path.
@RestController@RequestMapping("/music")publicclassMusicController{@AutowiredprivateMusicServer musicServer;@Value("${upload.path}")publicString SAVE_PATH;/**
* 上传音乐
* @param singer
* @param file
* @param request
* @return
*/@RequestMapping("/upload")publicResponseBodyMessage<Boolean>insertMusic(@RequestParamString singer,@RequestPart("filename")MultipartFile file,HttpServletRequest request,HttpServletResponse response){// 检测登录HttpSession session = request.getSession(false);if(session ==null|| session.getAttribute(Constant.USER_SESSION_KEY)==null){System.out.println("当前未登录!");returnnewResponseBodyMessage<>(-1,"请登录后上传",false);}// 文件的类型String fileNameAndType = file.getOriginalFilename();// 防止出现重复的相同歌曲和相同歌手.可以出现相同歌曲不同歌手String title = fileNameAndType.substring(0,fileNameAndType.lastIndexOf('.'));// 可能出现多首名称相同的歌曲, 所以用 ListList<Music> list = musicServer.selectByTitle(title);if(list !=null){for(Music music : list){if(music.getAuthor().equals(singer)){returnnewResponseBodyMessage<>(-1,"当前歌手的歌曲已经存在!",false);}}}// 创建文件String path = SAVE_PATH +singer+"-"+fileNameAndType;File dest =newFile(path);if(!dest.exists()){
dest.mkdirs();}try{
file.transferTo(dest);//return new ResponseBodyMessage<>(1,"上传成功!",true);}catch(IOException e){
e.printStackTrace();returnnewResponseBodyMessage<>(-1,"服务器上传失败!",false);}// 这里对是不是 MP3 文件进行判断. 主要是判断是否存在 TAG 这个字符File file1 =newFile(path);byte[] res =null;try{
res =Files.readAllBytes(file1.toPath());if(res ==null){returnnewResponseBodyMessage<>(-1,"当前文件不存在",false);}String str =newString(res);if(!str.contains("TAG")){
file1.delete();returnnewResponseBodyMessage<>(-1,"当前不是mp3文件",false);}}catch(IOException e){
e.printStackTrace();returnnewResponseBodyMessage<>(-1,"服务器出现问题",false);}// 在数据库中上传数据User user =(User) session.getAttribute(Constant.USER_SESSION_KEY);// 这里传递的 path 没有带 `.MP3` 后期在前端进行设置String uploadPath ="/music/play?path="+singer+"-"+title;try{int ret = musicServer.insert(title,singer,uploadPath,user.getUserId());if(ret ==1){
response.sendRedirect("/index.html");returnnewResponseBodyMessage<>(1,"上传成功",true);}else{returnnewResponseBodyMessage<>(-1,"数据库上传失败",false);}}catch(BindingException|IOException e){
dest.delete();returnnewResponseBodyMessage<>(-1,"数据库上传失败",false);}}}
10.4 前端代码
<formmethod="post"enctype="multipart/form-data"action="music/upload">
文件上传: <inputtype="file"name="filename"id="filename"/>
歌手名: <label><inputtype="text"name="singer"placeholder="请输入歌手名"id="singer"/></label><inputtype="submit"value="上传"id="submit"/></form>
10.5 测试代码
11. 播放音乐模块
11.1 向 MusicController 中添加代码
- 获取存储路径的文件.
- 读取文件中的所有字节,读入内存, 如果不为空, 返回字节码回去.
/**
* 播放音乐
* @param path
* @return
*/@RequestMapping("/play")publicResponseEntity<byte[]>playMusic(@RequestParamString path){File file =newFile(SAVE_PATH + path);byte[] res =null;try{
res =Files.readAllBytes(file.toPath());if(res ==null){returnResponseEntity.badRequest().build();}returnResponseEntity.ok(res);}catch(IOException e){
e.printStackTrace();returnResponseEntity.badRequest().build();}}
11.2 测试代码
观察字节码可以看出, 有 TAG 这个字符
12. 删除音乐模块
12.1 使用 Mybatis 操作数据库
删除音乐, 主要是两个删除, 一个是删除单个, 根据单个musicId 删除. 另一个是删除多个, 根据多个 musicId 删除.
这里根据 musicId 删除, 需要去数据库里查找 是否存在当前 musicId 的歌曲. 存在删成功, 不存在删失败.
注意, 删除的时候, 不仅要删除 music表里的歌曲. 也要删除 collect 表里的歌曲.
12.1.1 在 MusicServer 和 CollectServer 中添加代码
musicServer
publicMusicselectById(int musicId){return musicMapper.selectById(musicId);}publicintdeleteById(int musicId){return musicMapper.deleteById(musicId);}
collectServer
publicintdeleteLoveMusicById(int musicId){return collectMapper.deleteLoveMusicById(musicId);}
12.1.2 在 MusicMapper 和 CollectMapper 中添加代码
musicMapper
/**
* 通过音乐Id去查找歌曲
* @param musicId 音乐Id
* @return 查找到的音乐Id
*/MusicselectById(int musicId);/**
* 删除对应音乐Id的歌曲
* @param musicId 音乐Id
* @return 返回影响行数
*/intdeleteById(int musicId);
collectMapper
/**
* 删除收藏表中音乐Id为musicId的
* @param musicId 音乐Id
* @return 返回受影响行数
*/intdeleteLoveMusicById(int musicId);
12.1.3 在 MusicMapper.xml 和 CollectMapper.xml 中添加代码
MusicMapper.xml
<selectid="selectById"resultType="com.example.onlinemusicserver.model.Music">
select * from music where musicId = #{musicId};
</select><deleteid="deleteById">
delete from music where musicId = #{musicId};
</delete>
CollectMapper.xml
<deleteid="deleteLoveMusicById">
delete from collect where musicId = #{musicId};
</delete>
12.2 删除单一音乐功能
12.2.1 向 MusicController 中添加代码
- 首先查看要删除的 musicId 的音乐是否存在
- 如果不存在就直接返回删除失败
- 如果存在, 就删除, 首先删除数据库中的记录, 再删除服务器上的数据
- 同时删除 collect 表中的 musicId 的数据
/**
* 删除音乐
* @param musicId
* @return
*/@RequestMapping("/delete")publicResponseBodyMessage<Boolean>deleteMusic(@RequestParamString musicId){// 1. 检测音乐是不是存在Music music = musicServer.selectById(Integer.parseInt(musicId));// 2. 不存在直接返回, 存在就删除if(music ==null){System.out.println("该音乐不存在");returnnewResponseBodyMessage<>(-1,"没有你要删除的音乐",false);}// 2.1 删除数据库中的记录int ret = musicServer.deleteById(Integer.parseInt(musicId));if(ret ==1){// 2.2 删除服务器上的数据int index = music.getPath().lastIndexOf("=");StringPathName= music.getPath().substring(index+1);File file =newFile(SAVE_PATH +PathName+".mp3");if(file.delete()){
collectServer.deleteLoveMusicById(Integer.parseInt(musicId));returnnewResponseBodyMessage<>(1,"删除成功!",true);}else{returnnewResponseBodyMessage<>(-1,"服务器删除失败!",false);}}else{returnnewResponseBodyMessage<>(-1,"数据库删除失败!",false);}}
12.2.2 前端代码
functiondeleteMusic(musicId){
$.ajax({url:"music/delete",method:"post",data:{"musicId":musicId},dataType:"json",success:function(data,status){if(data.status ==1){alert(data.message);
location.assign("index.html");}else{alert(data.message);}}})}
12.2.3 测试代码
12.3 删除多个音乐功能
12.3.1 向 MusicConroller 中添加代码
- 遍历传过来的 musicId的集合. 查询是否存在当前musicId 的音乐
- 存在就删除数据库中的数据, 然后删除服务器上的数据, 再删除 collect 表中的数据
- 都删除成功就计数. 如果和传来的集合的数据总数和计数的总数一样, 就返回删除成功.
/**
* 删除多选音乐
* @param musicId
* @return
*/@RequestMapping("/deleteMore")publicResponseBodyMessage<Boolean>deleteMoreMusic(@RequestParam("musicId[]")List<Integer> musicId){int sum =0;for(int i =0; i < musicId.size(); i++){Music music = musicServer.selectById(musicId.get(i));if(music ==null){returnnewResponseBodyMessage<>(-1,"没有你要删除的音乐",false);}int ret = musicServer.deleteById(musicId.get(i));if(ret ==1){int index = music.getPath().lastIndexOf("=");StringPathName= music.getPath().substring(index+1);File file =newFile(SAVE_PATH +PathName+".mp3");if(file.delete()){
collectServer.deleteLoveMusicById(musicId.get(i));
sum += ret;}else{returnnewResponseBodyMessage<>(-1,"服务器删除失败!",false);}}else{returnnewResponseBodyMessage<>(-1,"数据库删除失败!",false);}}if(sum == musicId.size()){returnnewResponseBodyMessage<>(1,"音乐删除成功!",true);}else{returnnewResponseBodyMessage<>(-1,"音乐删除失败!",false);}}
12.3.2 前端代码
$(function(){
$.when(load).done(function(){$("#deleteMore").click(function(){let musicId =newArray();let i =0;$("input:checkbox").each(function(){if($(this).is(":checked")){
musicId[i]=$(this).attr("id");
i++;}});
$.ajax({url:"music/deleteMore",method:"post",data:{"musicId":musicId},dataType:"json",success:function(data,status){if(data.status ==1){alert(data.message);
location.assign("index.html");}else{alert(data.message);}}})})})})
12.3.3 测试代码
13. 收藏音乐模块
13.1 创建 Collect 实体类
@DatapublicclassCollect{privateint collectId;privateint userId;privateint musicId;}
13.2 使用 Mybatis 操作数据库
- 首先要通过 musicId 和 userId去查找当前是否存在 collect 表中
- 在通过 musicId 和 userId 去添加歌曲
13.2.1 在 CollectServer 中添加代码
publicCollectfindCollectMusic(int userId,int musicId){return collectMapper.findCollectMusic(userId,musicId);}publicintinsertLoveMusic(int userId,int musicId){return collectMapper.insertLoveMusic(userId, musicId);}
13.2.2 在 CollectMapper 中添加代码
/**
* 查看对应用户是否已经收藏了该音乐
* @param userId 用户Id
* @param musicId 音乐Id
* @return 收藏歌单
*/CollectfindCollectMusic(int userId,int musicId);/**
* 收藏音乐
* @param userId 用户Id
* @param musicId 音乐Id
* @return 返回影响行数
*/intinsertLoveMusic(int userId,int musicId);
13.2.3 在 CollectMapper.xml 中添加代码
<selectid="findCollectMusic"resultType="com.example.onlinemusicserver.model.Collect">
select * from collect where userId = #{userId} and musicId = #{musicId};
</select><insertid="insertLoveMusic">
insert into collect(userId,musicId) values(#{userId},#{musicId});
</insert>
13.3 向 CollectControll 中添加代码
- 通过用户Id 和 musicId查看是否存在歌曲
- 如果存在就返回收藏失败
- 如果不存在, 就根据用户id和musicId 添加收藏
/**
* 点击收藏的时候, 收藏音乐
* @param musicId
* @param request
* @return
*/@RequestMapping("/loveMusic")publicResponseBodyMessage<Boolean>AddLoveMusic(@RequestParamString musicId,HttpServletRequest request){int music_Id =Integer.parseInt(musicId);HttpSession session = request.getSession(false);User user =(User) session.getAttribute(Constant.USER_SESSION_KEY);int userId = user.getUserId();Collect collect = collectServer.findCollectMusic(userId,music_Id);if(collect !=null){returnnewResponseBodyMessage<>(-1,"当前已经收藏了",false);}else{int ret = collectServer.insertLoveMusic(userId,music_Id);if(ret ==1){returnnewResponseBodyMessage<>(1,"收藏成功!",true);}else{returnnewResponseBodyMessage<>(-1,"收藏失败",false);}}}
13.4 前端代码
functioncollectMusic(musicId){
$.ajax({url:"collect/loveMusic",method:"post",data:{"musicId":musicId},dataType:"json",success:function(data,status){if(data.status ==1){alert(data.message);
location.assign("collect.html");}else{alert(data.message);}}})}
13.5 测试代码
14. 取消收藏音乐模块
14.1 使用 Mybatis 操作数据库
- 根据userId 和 musicId 删除歌曲
14.1.1 在 CollectServer 中添加代码
publicintdeleteLoveMusic(int userId,int musicId){return collectMapper.deleteLoveMusic(userId,musicId);}
14.1.2 在 CollectMapper 中添加代码
/**
* 删除用户收藏的对应的音乐Id
* @param userId 用户Id
* @param musicId 音乐Id
* @return 受影响行数
*/intdeleteLoveMusic(int userId,int musicId);
14.1.3 在 CollectMapper.xml 中添加代码
<deleteid="deleteLoveMusic">
delete from collect where userId = #{userId} and musicId = #{musicId}
</delete>
14.2 向 CollectControll 中添加代码
- 这里登录之后去收藏页面,去删除歌曲.
- 通过 musicId 和 userId 去删除歌曲
/**
* 删除收藏的音乐
* @param musicId
* @param request
* @return
*/@RequestMapping("/deleteLoveMusic")publicResponseBodyMessage<Boolean>deleteLoveMusic(@RequestParamString musicId,HttpServletRequest request){HttpSession session = request.getSession(false);if(session ==null){returnnewResponseBodyMessage<>(-1,"当前未登录",false);}User user =(User) session.getAttribute(Constant.USER_SESSION_KEY);int userId = user.getUserId();int ret = collectServer.deleteLoveMusic(userId,Integer.parseInt(musicId));if(ret ==1){returnnewResponseBodyMessage<>(1,"取消收藏成功!",true);}else{returnnewResponseBodyMessage<>(-1,"取消收藏失败!",false);}}
14.3 前端代码
functiondeleteLoveMusic(musicId){
$.ajax({url:"collect/deleteLoveMusic",method:"post",data:{"musicId":musicId},dataType:"json",success:function(data,status){if(data.status ==1){alert(data.message);
location.assign("collect.html");}else{alert(data.message);}}})}
14.4 测试代码
15. 主页面 - 查询模块
15.1 使用 Mybatis 操作数据库
- 这里有空查询和模糊查询两种数据库操作
- 空查询 不带 name
- 模糊查询带name
15.1.1 在 MusicServer 中添加代码
publicList<Music>findMusic(){return musicMapper.findMusic();}publicList<Music>findMusicByName(String name){return musicMapper.findMusicByName(name);}
15.1.2 在 MusicMapper 中添加代码
/**
* 查找所有的歌曲
* @return 所有的歌曲
*/List<Music>findMusic();/**
* 支持模糊查询的歌曲.
* @param name 部分歌曲名
* @return 对应所有的歌曲
*/List<Music>findMusicByName(String name);
15.1.3 在 MusicMapper.xml 中添加代码
<selectid="findMusic"resultType="com.example.onlinemusicserver.model.Music">
select * from music;
</select><selectid="findMusicByName"resultType="com.example.onlinemusicserver.model.Music">
select * from music where title like concat('%',#{name},'%');
</select>
15.2 向 MusicController 中添加代码
这里判断前端传来的 name是否为空
- 不为空, 进入模糊查询
- 为空, 进入空查询
/**
* 支持模糊查询, 支持空查询
* @param name
* @return
*/@RequestMapping("/findMusic")publicResponseBodyMessage<List<Music>>findMusic(@RequestParam(required =false)String name){List<Music> list =null;if(name !=null){
list = musicServer.findMusicByName(name);}else{
list = musicServer.findMusic();}returnnewResponseBodyMessage<>(1,"查询完毕!",list);}
15.3 前端代码
$(function(){load()});functionload(musicName){
$.ajax({url:'music/findMusic',method:'POST',data:{"name":musicName},dataType:"json",success:function(data,status){if(data.data!=null){createMusic1(data.data);let audios = document.querySelectorAll('#player2');for(let audio of audios){newMediaElementPlayer(audio,{pluginPath:'https://cdn.jsdelivr.net/npm/[email protected]/build/',shimScriptAccess:'always',success:function(){let play = document.querySelector('.player');
play.style ="visibility: visible;";}});}}}})}functioncreateMusic1(lists){let s ='';for(let list of lists){
s+='<div class="d-block d-md-flex podcast-entry bg-white mb-5" data-aos="fade-up">';
s+='<img src="images/img_2.jpg" class="image">';
s+='<div class="text">';
s+='<input id="'+list.musicId+'" type="checkbox" class="checkbox">';
s+='<h3 class="font-weight-light">'+list.title+'</h3>';
s+='<div class="text-white mb-3"><span class="text-black-opacity-05"><small>'+list.author+'</small><span class="sep">/</span><small>'+DateFormat(list.uploadtime)+'</small></span></div>';
s+='<input type="button" class="btn btn-primary" style="margin: 5px;" value="收藏音乐" οnclick="collectMusic(\''+list.musicId+'\')">';
s+='<input type="button" class="btn btn-primary" style="margin: 5px;" value="删除音乐" οnclick="deleteMusic(\''+list.musicId+'\')">';
s+='<div class="player">';
s+='<audio id="player2" preload="none" controls style="max-width: 100%">';
s+='<source src="'+ list.path+'.mp3'+'" type="audio/mp3">';
s+='</audio></div></div></div>';}$("#list23").html(s);}// 把毫秒级时间戳转化成格式化日期functionDateFormat(timeStampMS){var date =newDate(timeStampMS);var year = date.getFullYear(),
month = date.getMonth()+1,//月份是从0开始的
day = date.getDate(),
hour = date.getHours(),
min = date.getMinutes(),
sec = date.getSeconds();var newTime = year +'-'+(month <10?'0'+ month : month)+'-'+(day <10?'0'+ day : day)+' '+(hour <10?'0'+ hour : hour)+':'+(min <10?'0'+ min : min)+':'+(sec <10?'0'+ sec : sec);return newTime;}$(function(){$("#submit1").click(function(){var name =$("#exampleInputName2").val();load(name);});});
15.4 测试代码
16. 收藏页面 - 查询模块
16.1 使用 Mybatis 操作数据库
- 这里有空查询和模糊查询两种数据库操作
- 空查询 不带 name
- 模糊查询带name
16.1.1 在 CollectServer 中添加代码
publicList<Music>findLoveMusicByUserId(int userId){return collectMapper.findLoveMusicByUserId(userId);}publicList<Music>findLoveMusicByNameAndUserId(String name,int userId){return collectMapper.findLoveMusicByNameAndUserId(name,userId);}
16.1.2 在 CollectMapper 中添加代码
/**
* 查找用户收藏的所有音乐
* @param userId 用户Id
* @return 返回查询到的所有音乐
*/List<Music>findLoveMusicByUserId(int userId);/**
* 查找用户收藏音乐中名字带有 name的音乐
* @param name 部分名字
* @param userId 用户Id
* @return 返回查询到的所有音乐
*/List<Music>findLoveMusicByNameAndUserId(String name,int userId);
16.1.3 在 CollectMapper.xml 中添加代码
<selectid="findLoveMusicByUserId"resultType="com.example.onlinemusicserver.model.Music">
select m.* from collect c,music m where m.musicId = c.musicId and c.userId = #{userId};
</select><selectid="findLoveMusicByNameAndUserId"resultType="com.example.onlinemusicserver.model.Music">
select m.* from collect c,music m where m.musicId = c.musicId and c.userId = #{userId} and m.title like concat('%',#{name},'%');
</select>
16.2 向 CollectController 中添加代码
这里判断前端传来的 name是否为空
- 不为空, 进入模糊查询
- 为空, 进入空查询
/**
* 1. 空查询, 查找所有的收藏音乐
* 2. 模糊查询, 查询包含部分 musicName 的所有收藏音乐
* @param musicName
* @param request
* @return
*/@RequestMapping("findLoveMusic")publicResponseBodyMessage<List<Music>>findLoveMusic(@RequestParam(required =false)String musicName,HttpServletRequest request){HttpSession session = request.getSession(false);if(session ==null){returnnewResponseBodyMessage<>(-1,"当前未登录",null);}User user =(User) session.getAttribute(Constant.USER_SESSION_KEY);int userId = user.getUserId();List<Music> list =null;if(musicName ==null){
list = collectServer.findLoveMusicByUserId(userId);}else{
list = collectServer.findLoveMusicByNameAndUserId(musicName,userId);}returnnewResponseBodyMessage<>(1,"查询成功!",list);}
16.3 前端代码
$(function(){load()});functionload(musicName){
$.ajax({url:'collect/findLoveMusic',method:'POST',data:{"musicName":musicName},dataType:"json",success:function(data,status){if(data.data!=null){createMusic1(data.data);let audios = document.querySelectorAll('#player2');for(let audio of audios){newMediaElementPlayer(audio,{pluginPath:'https://cdn.jsdelivr.net/npm/[email protected]/build/',shimScriptAccess:'always',success:function(){let play = document.querySelector('.player');
play.style ="visibility: visible;";}});}}}})}$(function(){$("#submit1").click(function(){var name =$("#exampleInputName2").val();load(name);});});functioncreateMusic1(lists){let s ='';for(let list of lists){
s+='<div class="d-block d-md-flex podcast-entry bg-white mb-5" data-aos="fade-up">';
s+='<img src="images/img_1.jpg" class="image">';
s+='<div class="text">';
s+='<h3 class="font-weight-light">'+list.title+'</h3>';
s+='<div class="text-white mb-3"><span class="text-black-opacity-05"><small>'+list.author+'</small><span class="sep">/</span><small>'+DateFormat(list.uploadtime)+'</small></span></div>';
s+='<input type="button" class="btn btn-primary" style="margin: 5px;" value="取消收藏" οnclick="deleteLoveMusic(\''+list.musicId+'\')">';
s+='<div class="player">';
s+='<audio id="player2" preload="none" controls style="max-width: 100%">';
s+='<source src="'+ list.path+'.mp3'+'" type="audio/mp3">';
s+='</audio></div></div></div>';}$("#list23").html(s);}// 把毫秒级时间戳转化成格式化日期functionDateFormat(timeStampMS){var date =newDate(timeStampMS);var year = date.getFullYear(),
month = date.getMonth()+1,//月份是从0开始的
day = date.getDate(),
hour = date.getHours(),
min = date.getMinutes(),
sec = date.getSeconds();var newTime = year +'-'+(month <10?'0'+ month : month)+'-'+(day <10?'0'+ day : day)+' '+(hour <10?'0'+ hour : hour)+':'+(min <10?'0'+ min : min)+':'+(sec <10?'0'+ sec : sec);return newTime;}
16.4 测试代码
版权归原作者 独一无二的哈密瓜 所有, 如有侵权,请联系我们删除。