JavaWeb——在线音乐播放器
文章目錄
- 效果演示
- 1. 創建SpringBoot項目
- 2. 數據庫設計
- 3. 配置數據庫和xml
- 4. 登錄模塊設計
-
- 4.1 創建User類
- 4.2 創建對應的Mapper和Controller
- 5. 實現登錄
-
- 5.1 登錄的請求和響應設計
- 5.2 請求實現
- 5.3 響應實現
-
- 5.31 設置統一的響應體類工具類
- 5.32 創建常量工具類
- 5.33 優化后完整代碼
- 6. 實現加密登錄
-
- 6.1 Bcrypt加密設計
- 6.2 加密登錄實現
-
- 6.21 創建包config,新建AppConfig類
- 6.3 加密登錄完整實現
- 7. 上傳音樂模塊設計
-
- 7.1 上傳音樂的請求和響應設計
- 7.2 新建 music 實體類
- 7.3 實現服務器上傳
-
- 創建 MusicController 類
- 7.4 如何判斷上傳的文件是mp3
- 7.5 實現數據庫上傳
-
- 實現 MusicMapper
- 實現 MusicMapper.xml
- 進行 數據庫上傳
- 完整代碼
- 8. 播放音樂模塊設計
-
- 8.1 實現 ResponseEntity 類
- 9. 刪除音樂模塊設計
-
- 9.1 刪除單個音樂
- 9.2 批量刪除
- 10. 查詢音樂模塊設計
-
- 10.1 實現 MusicMapper
- 10.2 實現 MusicMapper.xml
- 10.3 實現 MusicController
- 11. 收藏/喜歡音樂模塊設計
-
- 11.1 實現 LoveMusicMapper
- 11.2 實現 LoveMusicMapper.xml
- 11.3 實現 LoveMusicController
- 12. 查詢收藏/喜歡音樂模塊設計
-
- 12.1 實現 LoveMusicMapper
- 12.2 實現 LoveMusicMapper.xml
- 12.3 實現 LoveMusicController
- 13. 取消音樂收藏模塊設計
-
- 13.1 實現 LoveMusicMapper
- 13.2 實現 LoveMusicMapper.xml
- 13.3 實現 LoveMusicController
- 13.4 存在BUG分析
- 14. 刪除音樂完善
- 15. 注冊實現
- 前端頁面實現
- 16. 實現 登錄界面 login.html
- 17. 實現上傳音樂 upload.html
- 18. 實現音樂列表頁 list.html
- 19. 實現播放歌曲
- 20. 實現刪除單個音樂
- 21. 實現查詢音樂功能
- 22. 實現刪除選中音樂功能
- 23. 實現喜歡音樂列表頁功能
- 24. 實現收藏音樂列表功能
- 25 實現注冊功能
- 26 配置攔截器
- 項目部署
效果演示
1. 創建SpringBoot項目
2. 數據庫設計
創建 user 表:
用戶 id,用戶名 username,用戶密碼 password
創建 music 表:
音樂 id,音樂名稱 title,歌手名稱 singer,時間 time,歌曲路徑 url,對應上傳音樂的用戶 userid
創建 lovemusic 表(中間表):
音樂 id,對應的用戶id user_id,對應的音樂id music_id
-- 數據庫
drop database if exists `onlinemusic`;
create database if not exists `onlinemusic` character set utf8;-- 使用數據庫
use `onlinemusic`;-- 創建 user表
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`username` varchar(20) NOT NULL,
`password` varchar(255) NOT NULL
);-- 創建 music 表
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(1000) NOT NULL,
`userid` int(11) NOT NULL
);-- 創建 lovemusic
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
);
3. 配置數據庫和xml
打開application.properties配置如下信息:
#配置數據庫
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/onlinemusic?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=你的密碼
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver#配置xml
mybatis.mapper-locations=classpath:mybatis/**Mapper.xml#配置springboot上傳文件的大小,默認每個文件的配置最大為15Mb,單次請求的文件的總數不能大于100Mb
spring.servlet.multipart.max-file-size = 15MB
spring.servlet.multipart.max-request-size=100MB# 配置springboot日志調試模式是否開啟
debug=true# 設置打印日志的級別,及打印sql語句
#日志級別:trace,debug,info,warn,error
#基本日志
logging.level.root=INFO
logging.level.com.example.onlinemusic.mapper=debug#掃描的包:druid.sql.Statement類和frank包
logging.level.druid.sql.Statement=DEBUG
logging.level.com.example=DEBUG
4. 登錄模塊設計
4.1 創建User類
在package com.example.onlinemusic.model包中創建User類
@Data
public class User {private int id;private String username;private String password;
}
4.2 創建對應的Mapper和Controller
1.新建mapper包,在mapper包下新建UserMapper
@Mapper
public interface UserMapper {User login(User loginUser);
}
2.在resource目錄下,新建mybatis文件夾,新建UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.onlinemusic.mapper.UserMapper"></mapper>
5. 實現登錄
5.1 登錄的請求和響應設計
5.2 請求實現
@RestController // 這個注解是 @Controller 和 @RequestBody 的組合注解
@RequestMapping("/user") // 一級路由
public class UserController {@Autowiredprivate UserMapper userMapper; // 注入 UserMapper// 后面要用 UserMapper里面的方法,去進行數據查詢// 登錄功能@RequestMapping("/login")// 傳遞兩個參數 一個是 username 一個是 passwordpublic void login(@RequestParam String username,@RequestParam String password) { // @RequestParam 注解,可以H后端參數重命名,也可以制定傳參// 拿到 userLogin 對象,設置 用戶名和密碼User userLogin = new User();userLogin.setUsername(username);userLogin.setPassword(password);// 調用 userMapper的 login 方法 在數據庫中 進行查詢,返回 UserUser user = userMapper.login(userLogin);if(user != null ) {System.out.println("登錄成功!");}else {System.out.println("登錄失敗!");}}
}
UserMapper 中:
@Mapper
public interface UserMapper {// 登錄功能,查詢操作User login(User userLogin);// login 方法,返回為 User 對象
}
UserMapper.xml中:
<!-- 登錄操作,查詢 username 和 password,后面查詢那個,就目錄就是誰的,比如這里查詢是User--><select id="login" resultType="com.example.onlinemusic.model.User">select * from user where username=#{username} and password=#{password};</select>
5.3 響應實現
5.31 設置統一的響應體類工具類
@Data
// 這是統一響應體工具類
public class ResponseBodyMessage <T> {private int status; // 狀態碼private String message; // 返回的信息[出錯或者沒出錯的原因]private T data; // 返回給前端的數據,有可能是 boolean 類型,類型很多這里直接用 泛型// 構造方法public ResponseBodyMessage(int status, String message, T data) {this.status = status;this.message = message;this.data = data;}
}
5.32 創建常量工具類
5.33 優化后完整代碼
加了響應體之后,登錄成功,我們再存儲一下 Session
完整代碼如下:
@RestController // 這個注解是 @Controller 和 @RequestBody 的組合注解
@RequestMapping("/user") // 一級路由
public class UserController {@Autowiredprivate UserMapper userMapper; // 注入 UserMapper// 后面要用 UserMapper里面的方法,去進行數據查詢// 登錄功能@RequestMapping("/login")// 傳遞兩個參數 一個是 username 一個是 password// 加了統一響應體類之后 返回值 不在是 void ,而是 我們的 ResponseBodyMessagepublic ResponseBodyMessage<User> login(@RequestParam String username,@RequestParam String password,HttpServletRequest request) {// @RequestParam 注解,可以H后端參數重命名,也可以制定傳參// 拿到 userLogin 對象,設置 用戶名和密碼User userLogin = new User();userLogin.setUsername(username);userLogin.setPassword(password);// 調用 userMapper的 login 方法 在數據庫中 進行查詢,返回 UserUser user = userMapper.login(userLogin);if(user != null ) {System.out.println("登錄成功!");// 存儲 session
// request.getSession().setAttribute("USERINFO_SESSION_KEY",user);// 做出優化后的代碼,我們將 USERINFO_SESSION_KEY 寫在我們的工具包中request.getSession().setAttribute(Constant.USERINFO_SESSION_KEY,user);/*** request.getSession.setAttribute()是獲得當前會話的session* 然后再setAttribute到session里面去,有效范圍是session而不是request。*/// 返回響應體return new ResponseBodyMessage<>(0,"登錄成功老鐵!",userLogin);}else {System.out.println("登錄失敗!");// 登錄失敗,做出響應// 在這里規定 status -1 為失敗 0為成功return new ResponseBodyMessage<>(-1,"登錄失敗老鐵!",userLogin);}}
}
6. 實現加密登錄
6.1 Bcrypt加密設計
Bcrypt就是一款加密工具,可以比較方便地實現數據的加密工作。你也可以簡單理解為它內部自己實現了隨機加鹽處理 。我們使用MD5加密,每次加密后的密文其實都是一樣的,這樣就方便了MD5通過大數據的方式進行破解。Bcrypt生成的密文是60位的。而MD5的是32位的。Bcrypt破解難度更大
添加如下依賴到 pom.xml:
<!-- 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})
為什么要加 spring-boot 啟動類注解:
當啟動類,沒有加這個過濾的時候,我們發現不能進行登錄。
這是因為在SpringBoot中,默認的Spring Security生效了的,此時的接口都是被保護的,我們需要通過驗證才能正常的訪問。此時通過上述配置,即可禁用默認的登錄驗證。
實質我們并沒有用到 Security 這個框架,而是用到了里面其中的一個類,僅此而以
創建 BcryptTest 測試類:
public class BcryptTest {public static void main(String[] args) {
//模擬從前端獲得的密碼// String password = "123456";// 獲取 BCrypt 對象BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();// encode 對我們輸入的密碼進行加密處理 得到新的密碼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.2 加密登錄實現
邏輯如下:
1.根據用戶名名稱 查詢 當前是否存在這樣的用戶[用戶名:默認是唯一的]
2.取出當前用戶的密碼,進行匹配,查看密碼是否是一樣的,一樣就登錄成功
6.21 創建包config,新建AppConfig類
這里為什么要建一個 AppConfig 類,是為了方便我們后面的對象注入,比如我 我們要在 加密登錄中 注入 BCryptPasswordEncoder,我們就需要先創建一個對象,通過 @Bean 注解,將它輸入到 Spring 容器中
當然直接在 UserController 當中進行 實例化創建 BCryptPasswordEncoder 對象也是一樣的
//@Configuration:表明當前類是一個配置類,被注解的類內部包含有一個或多個被@Bean注解的方法,用于構建
//bean定義,初始化Spring容器。
//@Bean注解:用于告訴方法,產生一個Bean對象,然后這個Bean對象交給Spring管理。產生這個Bean對象的方
//法Spring只會調用一次,隨后這個Spring將會將這個Bean對象放在自己的IOC容器中。
//SpringIOC 容器管理一個或者多個bean,這些bean都需要在@Configuration注解下進行創建,在一個方法上使用
//@Bean注解就表明這個方法需要交給Spring進行管理。
@Configuration
public class AppConfig {@Beanpublic BCryptPasswordEncoder getBCryptPasswordEncoder() {return new BCryptPasswordEncoder();}
}
UserMapper 代碼如下:
UserMapper.xml 內容如下:
6.3 加密登錄完整實現
/*** 實現加密登錄*/@RequestMapping("/login")// 傳遞兩個參數 一個是 username 一個是 password// 加了統一響應體類之后 返回值 不在是 void ,而是 我們的 ResponseBodyMessagepublic ResponseBodyMessage<User> login(@RequestParam String username,@RequestParam String password,HttpServletRequest request) {// User userLogin = new User();
// userLogin.setUsername(username);
// userLogin.setPassword(password);
// User user = userMapper.login(userLogin);// 1. 先去查詢我們 用戶,是否存在User user = userMapper.selectByName(username);if(user != null ) {// 2. 如果用戶存在,找到當前用戶的密碼,通過 Bcrypt 中的 matches 方法 判斷密碼是否一致// password 為原來的密碼,user.getPassword 為加密后的密碼boolean flg = bCryptPasswordEncoder.matches(password,user.getPassword());if(!flg) {// 說明密碼不匹配return new ResponseBodyMessage<>(-1,"登錄失敗,用戶名或密碼錯誤",user);}System.out.println("登錄成功!");request.getSession().setAttribute(Constant.USERINFO_SESSION_KEY,user);return new ResponseBodyMessage<>(0,"登錄成功老鐵!",user);}else {System.out.println("登錄失敗!");return new ResponseBodyMessage<>(-1,"登錄失敗,沒找到用戶!",user);}}
7. 上傳音樂模塊設計
7.1 上傳音樂的請求和響應設計
7.2 新建 music 實體類
@Data
public class Music {private int id;private String title;private String singer;private String time;private String url;private int userId;
}
7.3 實現服務器上傳
創建 MusicController 類
@RestController // 組合注解 @Controller + @ResponseBody
@RequestMapping("/music")
public class MusicController {/*** 上傳音樂* @return*/// 從配置文件中 將 路徑 讀取出來@Value("${music.local.path}")private String SAVE_PATH;@RequestMapping("/upload")public ResponseBodyMessage<Boolean> insertMusic(@RequestParam String singer,@RequestParam("filename") MultipartFile file,HttpServletRequest request) {// 1. 檢查是否登錄// 獲取 sessionHttpSession session = request.getSession(false);// 判空if(session == null || session.getAttribute(Constant.USERINFO_SESSION_KEY) == null) {System.out.println("沒有登錄!");return new ResponseBodyMessage<>(-1,"請登錄后上傳!",false);}// 2. 上傳到了服務器 ---- 拿到完整的文件名稱 xxx.mp3String fileNameAndType = file.getOriginalFilename(); // 獲取完整文件名稱System.out.println("fileNameAndType" + fileNameAndType);String path = SAVE_PATH + fileNameAndType;File dest = new File(path); // dest 目錄if(!dest.exists()) {dest.mkdir(); // 如果 dest(目錄)不存在 創建目錄}// 如果存在,通過 file.transferTo 將文件上傳try {file.transferTo(dest);return new ResponseBodyMessage<>(0,"上傳成功!",true);} catch (IOException e) {e.printStackTrace();}return new ResponseBodyMessage<>(-1,"上傳失敗!",false);}}
7.4 如何判斷上傳的文件是mp3
每個種類的文件都要自己的格式,檢測當前你上傳的格式,判斷這個文件的格式是不是 mp3 文件
不能通過后綴名進行判斷,后綴名可以進行更改的
7.5 實現數據庫上傳
實現 MusicMapper
實現 MusicMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 這里的目錄 對于的是 mapper 所對應的目錄-->
<mapper namespace="com.example.onlinemusic.mapper.MusicMapper"><insert id="insert" >insert into music(title,singer,time,url,userid)values(#{title},#{singer},#{time},#{url},#{userid});</insert></mapper>
進行 數據庫上傳
分別獲取需要的數據:
tilte,singer,userid,url,time
/*** 進行 數據庫 上傳*/// 1. 先準備需要數據// 1.1 獲取title xxx.mp3 -- > title = xxxint index = fileNameAndType.lastIndexOf(".");String title = fileNameAndType.substring(0,index); // 拿到 xxx// 1.2 獲取 singer 已經確定,我們傳參已經傳了// 1.3 獲取 useridUser user = (User) session.getAttribute(Constant.USERINFO_SESSION_KEY);int userid = user.getId();// 1.4 獲取 url,播放音樂-》http請求String url = "/music/get?path="+title;// 1.5 獲取 年-月-日SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");// 把當前的日期格式化為 time format()格式化String time = sf.format(new Date());// 2. 調用 insertint ret = 0; // 插入影響的是行數,這里我們初始化一下,看看他等不等于1,等于1說明插入成功ret = musicMapper.insert(title,singer,time,url,userid);if(ret ==1 ) {return new ResponseBodyMessage<>(1,"數據庫上傳成功",true);}else {return new ResponseBodyMessage<>(-1,"數據庫上傳失敗",false);}}
完整代碼
@RestController // 組合注解 @Controller + @ResponseBody
@RequestMapping("/music")
public class MusicController {/*** 上傳音樂* @return*/@Autowiredprivate MusicMapper musicMapper;// 從配置文件中 將 路徑 讀取出來@Value("${music.local.path}")private String SAVE_PATH;@RequestMapping("/upload")public ResponseBodyMessage<Boolean> insertMusic(@RequestParam String singer,@RequestParam("filename") MultipartFile file,HttpServletRequest request) {// 1. 檢查是否登錄// 獲取 sessionHttpSession session = request.getSession(false);// 判空if(session == null || session.getAttribute(Constant.USERINFO_SESSION_KEY) == null) {System.out.println("沒有登錄!");return new ResponseBodyMessage<>(-1,"請登錄后上傳!",false);}// 先查詢數據庫當中是否有當前音樂[歌曲名 + 歌手]// TODD:// 2. 上傳到了服務器 ---- 拿到完整的文件名稱 xxx.mp3String fileNameAndType = file.getOriginalFilename(); // 獲取完整文件名稱System.out.println("fileNameAndType" + fileNameAndType);String path = SAVE_PATH + fileNameAndType;File dest = new File(path); // dest 目錄if(!dest.exists()) {dest.mkdir(); // 如果 dest(目錄)不存在 創建目錄}// 如果存在,通過 file.transferTo 將文件上傳try {file.transferTo(dest);} catch (IOException e) {e.printStackTrace();return new ResponseBodyMessage<>(-1,"服務器上傳失敗!",false);}/*** 進行 數據庫 上傳*/// 1. 先準備需要數據// 1.1 獲取title xxx.mp3 -- > title = xxxint index = fileNameAndType.lastIndexOf(".");String title = fileNameAndType.substring(0,index); // 拿到 xxx// 1.2 獲取 singer 已經確定,我們傳參已經傳了// 1.3 獲取 useridUser user = (User) session.getAttribute(Constant.USERINFO_SESSION_KEY);int userid = user.getId();// 1.4 獲取 url,播放音樂-》http請求String url = "/music/get?path="+title;// 1.5 獲取 年-月-日SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");// 把當前的日期格式化為 time format()格式化String time = sf.format(new Date());try {// 2. 調用 insertint ret = 0; // 插入影響的是行數,這里我們初始化一下,看看他等不等于1,等于1說明插入成功ret = musicMapper.insert(title,singer,time,url,userid);if(ret ==1 ) {return new ResponseBodyMessage<>(0,"數據庫上傳成功",true);}else {return new ResponseBodyMessage<>(-1,"數據庫上傳失敗",false);}}catch (BindingException e) {dest.delete();return new ResponseBodyMessage<>(-1,"數據庫上傳失敗",false);}// 另外一個問題: 如果重復上傳一首歌曲 能否上傳成功? 可以}}
8. 播放音樂模塊設計
8.1 實現 ResponseEntity 類
/*** 播放音樂模塊設計*/// 播放音樂的時候,路徑:/music/get?path=xxx.mp3@RequestMapping("/get")public ResponseEntity<byte[]> get(String path) {File file = new File(SAVE_PATH + path);byte[] a = null;try {a = Files.readAllBytes(file.toPath());//Files.readAllBytes(String path) :// 讀取文件中的所有字節,讀入內存 ,參數path是文件的路徑if(a == null) {// 無參ok方法返回OK狀態// 有參ok方法返回body內容和OK狀態return ResponseEntity.badRequest().build();}return ResponseEntity.ok(a);} catch (IOException e) {e.printStackTrace();}return ResponseEntity.badRequest().build();
// return ResponseEntity.internalServerError().build();
// return ResponseEntity.notFound().build();}
9. 刪除音樂模塊設計
9.1 刪除單個音樂
請求和響應設計:
data: 成功 or 失敗
1.MusicMapper 內容如下:
2.MusicMapper.xml 內容如下
3.MusicController 內容如下:
@RequestMapping("/delete")public ResponseBodyMessage<Boolean> deleteMusicById(@RequestParam String id) {// 1. 先檢查音樂是否存在int iid = Integer.parseInt(id);// 2. 如果存在進行刪除Music music = musicMapper.findMusicById(iid);if(music == null) {return new ResponseBodyMessage<>(-1,"沒有你要刪除的音樂!",false);}else {// 2.1 刪除數據庫int ret = musicMapper.deleteMusicById(iid);// 如果 ret 等于1 說明 數據庫數據刪除成功if(ret == 1) {// 2.2 刪除服務器上的數據(file)// 這里需要拿到 title,可以通過 url 去拿,也可以直接 music.getTitleint index = music.getUrl().lastIndexOf("=");String fileName = music.getUrl().substring(index+1);File file = new File(SAVE_PATH + fileName + ".mp3");System.out.println("當前的路徑: " + file.getPath());if(file.delete()){return new ResponseBodyMessage<>(0,"服務器當中的音樂刪除成功!",true);}else {return new ResponseBodyMessage<>(-1,"服務器當中的音樂刪除失敗!",false);}}else {return new ResponseBodyMessage<>(-1,"數據庫當中的音樂沒有刪除成功",false);}}}
9.2 批量刪除
約定前后端相互接口:
MusicController 內容如下:
/*** 批量刪除* id[1,3,5,7,9]*/@RequestMapping("/deleteSel")public ResponseBodyMessage<Boolean> deleteSelMusic(@RequestParam("id[]")List<Integer> id) {System.out.println("所有的id:" + id);int sum = 0;// 因為是批量刪除,這里用數組存儲我們要刪除的 音樂 idfor (int i = 0; i < id.size(); i++) {// 1. 查詢 音樂是否存在Music music = musicMapper.findMusicById(id.get(i));if (music == null) {System.out.println("沒有這個id的音樂");return new ResponseBodyMessage<>(-1, "沒有你要刪除的音樂", false);}// 2. 如果音樂存在我們進行刪除// 2.1 進行數據庫刪除int ret = musicMapper.deleteMusicById(id.get(i));if (ret == 1) {// 說明 數據庫刪除成功,進行服務器刪除// 先拿到 titleint index = music.getUrl().lastIndexOf("=");String fileName = music.getUrl().substring(index + 1);File file = new File(SAVE_PATH + fileName + ".mp3");System.out.println("當前路徑:" + file.getPath());if (file.delete()) {sum += ret;} else {return new ResponseBodyMessage<>(-1, "服務器當中的音樂刪除失敗!", false);}}else {return new ResponseBodyMessage<>(-1,"數據庫當中音樂刪除失敗",false);}}// 判斷 sum 的值是否等于 id.size 如果等于說明 刪完啦if(sum == id.size()) {System.out.println("整體刪除成功!");return new ResponseBodyMessage<>(0,"音樂刪除成功",true);}else {System.out.println("整體刪除失敗!");return new ResponseBodyMessage<>(-1,"音樂刪除失敗",false);}}
10. 查詢音樂模塊設計
此處查詢需要滿足幾個功能:
- 支持模糊查詢
- 支持傳入參數為空
請求和響應設計:
10.1 實現 MusicMapper
10.2 實現 MusicMapper.xml
模糊查詢:
select * from music where title like concat('%',#{musicName},'%');
10.3 實現 MusicController
@RequestMapping("/findmusic")// 這里有一個小細節,我們可能不傳參那么 我們的 @RequestParam 這里 需要 required = falsepublic ResponseBodyMessage<List<Music>> findMusic(@RequestParam(required = false)String musicName) {List<Music> musicList = null;if(musicName != null) {// 不為空 查詢指定音樂musicList = musicMapper.findMusicByName(musicName);}else {// 為空 查詢所有音樂musicList = musicMapper.findMusic();}return new ResponseBodyMessage<>(0,"查詢到了所有的音樂",musicList);}
11. 收藏/喜歡音樂模塊設計
請求和響應設計:
11.1 實現 LoveMusicMapper
11.2 實現 LoveMusicMapper.xml
11.3 實現 LoveMusicController
/*** 收藏音樂,需要獲取到 userId 和 musicId,musicId 傳入的參數,userId 通過 session 獲取*/@RequestMapping("/likeMusic")public ResponseBodyMessage<Boolean> likeMusic(@RequestParam String id, HttpServletRequest request) {// 字符串變為整數int musicId = Integer.parseInt(id);System.out.println("musicId:" + musicId);// 1. 檢查是否登錄HttpSession session = request.getSession(false); // 獲取 session// 判斷 session 是否為空if(session == null || session.getAttribute(Constant.USERINFO_SESSION_KEY) == null) {System.out.println("沒有登錄!");return new ResponseBodyMessage<>(-1,"請先登錄!",false);}// 如果不為空 獲取 session 信息User user = (User) session.getAttribute(Constant.USERINFO_SESSION_KEY);int userId = user.getId();System.out.println("userId:" + userId);Music music = loveMusicMapper.findLoveMusic(userId,musicId);if(music != null) {// 如果不等于null,說明之前收藏過當前的音樂,不能進行收藏 TODD:加一個取消收藏的音樂return new ResponseBodyMessage<>(-1,"之前收藏過這個音樂",false);}// 如果等于空,收藏這首音樂 (插入)boolean effect = loveMusicMapper.insertLoveMusic(userId,musicId);if(effect) {return new ResponseBodyMessage<>(0,"收藏成功",true);}else {return new ResponseBodyMessage<>(-1,"收藏失敗",false);}}
12. 查詢收藏/喜歡音樂模塊設計
此處查詢需要滿足幾個功能:
- 支持模糊查詢
- 支持傳入參數為空
請求和響應設計,和查詢音樂模塊是一樣的,這里不在做過多的闡述
12.1 實現 LoveMusicMapper
12.2 實現 LoveMusicMapper.xml
支持模糊查詢,多表聯合查詢
12.3 實現 LoveMusicController
@RequestMapping("/findlovermusic")public ResponseBodyMessage<List<Music>> findLoveMusic(@RequestParam(required = false) String musicName,HttpServletRequest request) {// 1. 檢查是否登錄HttpSession session = request.getSession(false);if(session == null || session.getAttribute(Constant.USERINFO_SESSION_KEY) == null) {System.out.println("沒有登錄");return new ResponseBodyMessage<>(-1,"沒有登錄",null);}// 如果不為空,進行查詢// 先獲取 userIdUser user = (User) session.getAttribute(Constant.USERINFO_SESSION_KEY);int userId = user.getId();List<Music> musicList = null;if(musicName == null) {// 如果等于空,查詢所有的音樂musicList = loveMusicMapper.findLoveMusicByUserId(userId);}else {// 如果不等于空,查詢 指定收藏的音樂musicList = loveMusicMapper.findLoveMusicByKeyAndUid(musicName,userId);}return new ResponseBodyMessage<>(0,"查詢到了所有的歌曲信息",musicList);}
13. 取消音樂收藏模塊設計
請求和響應設計
13.1 實現 LoveMusicMapper
13.2 實現 LoveMusicMapper.xml
13.3 實現 LoveMusicController
@RequestMapping("/deletelovemusic")public ResponseBodyMessage<Boolean> deleteLoveMusic(@RequestParam String id,HttpServletRequest request) {// 將 id 轉為 整數int musicId = Integer.parseInt(id);// 拿到 useId,先檢查是否 登錄HttpSession session = request.getSession(false);if(session == null || session.getAttribute(Constant.USERINFO_SESSION_KEY) == null) {System.out.println("沒有登錄");return new ResponseBodyMessage<>(-1,"沒有登錄",false);}// 如果 session 不等于 空 拿到 use 信息User user = (User) session.getAttribute(Constant.USERINFO_SESSION_KEY);int userId = user.getId();// 獲取到 userId 和 musicId 后調用 mapper 中的方法 去刪除 ,首先判斷是否存在int ret = loveMusicMapper.deleteLoveMusic(userId,musicId);if(ret == 1) {System.out.println("取消收藏成功!");return new ResponseBodyMessage<>(0,"取消收藏成功",true);}else {System.out.println("取消收藏失敗");return new ResponseBodyMessage<>(-1,"取消收藏失敗",false);}}
13.4 存在BUG分析
當刪除 music 表 當中的 musicId 為 5的這首音樂的時候,請問 lovemusic這張表中,是不是應該也被刪除?
music 表
lovemusic表
14. 刪除音樂完善
我們在刪除 音樂模塊中,進行刪除,需要同步刪除我們 喜歡的音樂(lovemuisc),因此,我們對 MusicController 中進行優化和完善。
在 MusicController 中,刪除 單個音樂中 添加以下內容:
在 批量刪除中 添加以下內容:
15. 注冊實現
@RequestMapping("/logon")public ResponseBodyMessage<User> logon(@RequestParam String username,@RequestParam String password,HttpServletRequest request) {// 1. 先對 輸入的 password 進行加密String newPassword = bCryptPasswordEncoder.encode(password);// 創建 User 對象User user = new User();// 設置賬號和 賬號 和 密碼user.setUsername(username);user.setPassword(newPassword);// 在數據庫中進行查詢 看用戶是否存在User user1 = userMapper.selectByName(username);if(user1 != null) {System.out.println("用戶已注冊");return new ResponseBodyMessage<>(-1,"用戶已注冊,換個其他的吧",user);}// 注冊,往數據庫中插入 新的 用戶int ret = userMapper.insertUser(user);if(ret == 1) {System.out.println("注冊成功啦,我的老baby,一起來聽音樂吧!");return new ResponseBodyMessage<>(0,"注冊成功啦,我的老baby,一起來聽音樂吧!",user);}else {System.out.println("注冊失敗!達咩~");return new ResponseBodyMessage<>(-1,"注冊失敗!達咩~",user);}}
以上,后端邏輯全部完善~~~~,接下來是后端模塊實現。
前端頁面實現
將前端頁面模板,導入到我們 resources 底下的 static 中
jquery參考手冊
16. 實現 登錄界面 login.html
JS核心代碼如下:
<script>// 1. 登錄核心業務邏輯 //原本是$(document).ready(function(){}) 表示當這個頁面的dom樹加載完成后才會執行這個,而后面的(document).ready可以省略,因此這里就是這樣寫也是可以的$(function(){//此時設置提交按鈕的click事件,通過id選擇器來獲取$("#submit").click(function(){//此時就需要獲取到用戶名和密碼的值let username = $("#user").val();let password = $("#password").val();//檢查一下用戶名和密碼是否為空,以及去掉空格使用trim方法if(username.trim() == "" || password.trim() == ""){alert("用戶名或密碼不能為空!");return;}//如果都不為空,就需要使用ajax來發送請求到后端,然后處理這個請求及返回響應$.ajax({type:"POST", // 請求url:"/user/login", // 指定路徑// 返回數據data:{"username":username,"password":password},//服務器返回數據類型dataType:"json",success:function(data){//看狀態碼if(data.status == 0){console.log(data);alert("登錄成功,點擊進行跳轉!");//登錄成功,這里就可以進行頁面的跳轉window.location.href="list.html";}else{alert("登錄失敗,用戶名或密碼錯誤!");$("#password").val("");}}});});});</script>
17. 實現上傳音樂 upload.html
- 修改后端代碼,上傳音樂成功后,跳轉到 音樂 列表頁
- 前端代碼 upload.html 實現:
<!DOCTYPE html>
<html>
<head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"><meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/></head>
<body>
<!--enctype="multipart/form-data" action 提交 url 地址-->
<form method="POST" enctype="multipart/form-data" action="/music/upload"> 文件上傳:<input type="file" name="filename"/>歌手名: <label><input type="text" name="singer" placeholder="請輸入歌手名"/></label><input type="submit" value="上傳"/>
</form>
</body>
</html>
18. 實現音樂列表頁 list.html
當我們跳轉到 list.html 以后,我們要像服務器發起請求,查詢到所有的音樂信息,動態的生成表格
之前我們前面說到,當我們 查詢音樂的時候,如果不傳參數,那么查詢到所有的音樂,傳遞參數,查詢到指定的音樂
JS代碼如下:
script type="text/javascript">// 核心代碼實現// 1. 上傳音樂// <!-- 核心代碼實現 -->$(function(){load();});// musicName 可以 默認傳參 和 不傳參// 不傳參 匹配的就是所有的 音樂function load(musicName) {$.ajax({type:"GET",url:"/music/findmusic",//數據data:{"musicName":musicName},//服務器返回數據類型dataType:"json",// 如果服務器返回成功,會返回我們的回調函數success:function(obj) {console.log(obj);// obj包含了所有的返回信息,然后其中的data就包含了所有的音樂信息,可以先獲取到var data = obj.data;var s = ''; // 最原始的拼接方式// data[i].id data[i].singer data[i].title 這種形式來獲取 obj里面的信息for(var i = 0; i < data.length;i++) {var musicUrl = data[i].url + ".mp3";s += '<tr>';s += '<th> <input id= "' + data[i].id + '" type="checkbox"> </th>';// <th> <input id="1" type="checkbox"></th>s += '<td>' + data[i].title + '</td>';s += '<td>' + data[i].singer + '</td>';//s += "<td <a href=\"\"> <audio src= \""+ musicUrl+"\" + controls=\"controls\" preload=\"none\" loop=\"loop\"> >" + "</audio> </a> </td>";s += '<td> <button class="btn btn-primary" onclick="playerSong(\''+musicUrl+'\')"> 播放歌曲 </button>' + '</td>';s += '<td> <button class="btn btn-primary" onclick="deleteInfo('+data[i].id+')"> 刪除 </button> <button class="btn btn-primary" onclick="loveInfo('+data[i].id+')"> 喜歡 </button>' + '</td>';s += '</tr>';}//然后將所有的數據放到tbody里面$('#info').html(s);}});}</script>
19. 實現播放歌曲
這里播放歌曲,我們采用開源的播放控件:
碼云地址
GitHub
將該開源項目,下載到本地,取出player文件夾,放入static文件夾下
JS 核心代碼,就是調用 播放器的 toPlay() 方法
toPlay(url,title,startTime,autoPlay) 這里的 autoPlay 設置為 true 代表 我們點擊播放的時候,它才會播放
// 實現音樂播放function playerSong(obj) {// toPlay(url,title,startTime,autoPlay)//obj: http://localhost:8080/music/get?path=xxx.mp3// 從等號下標 下一個位置開始截取 [)var title = obj.substring(obj.lastIndexOf("=") + 1);SewisePlayer.toPlay(obj,title,0,true);}
20. 實現刪除單個音樂
之前約定好的 前后端交互接口
前端傳入的數據和后端返回的響應,響應的 data 為 true or false
JS 核心代碼如下:
// 刪除音樂 傳入的參數是 idfunction deleteInfo(obj){console.log(obj);$.ajax({type:"POST",url:"/music/delete",data:{"id":obj},dataType:"json",success:function(body){console.log(body);//判斷是否刪除成功if(body.data == true){//表示刪除成功alert("刪除成功了老鐵!,重新加載當前頁面哈!");window.location.href="list.html";}else{alert("刪除失敗了老鐵!");}}});}
21. 實現查詢音樂功能
思路很簡單,我們前面實現音樂列表頁的時候 完成了 load 查詢函數,這里我們只需要點擊 按鈕,執行我們的回調函數,拿到我們的 輸入框的信息,即可,進行查詢
JS核心代碼如下:
// 查詢和刪除// 查詢~~~~~~$(function(){// 點擊 提交按鈕執行 回調函數$("#submit1").click( function(){//這里的功能就是進行查詢而框里面不傳參數就是默認的查詢所有音樂//傳了參數就進行模糊查詢var name = $("#exampleInputName2").val();load(name);});});
22. 實現刪除選中音樂功能
這里的刪除,我們的刪除邏輯為,點擊刪除,如圖:
我們需要獲取到 每一行 中的 input 標簽中的 checkbox
首先獲取到我們需要刪除的音樂id并存儲起來,其次再通過ajax和后端建立聯系,進行刪除。
JS 核心代碼如下:
// 刪除音樂/*** 1. 先拿到 需要刪除的 id* 拿到 需要刪除的 id 就存儲起來* */// when 當執行完 load函數,則執行 done 當中的回調函數$.when(load).done(function() {//刪除選中的回調事件$("#delete").click(function(){//由于這里存儲的不止一條數據,因此這里需要將這些選中的存儲下來var id = new Array();var i = 0; //然后遍歷所有的checkbox標簽$("input:checkbox").each(function(){//看有沒有被選中,被選中了就記錄下來,沒有選中就不用管//this 發生事件的 demo元素,checked表示看是否選中了if($(this).is(":checked")){id[i] = $(this).attr("id");i++;}});console.log(id);/*** 2. 找到 id以后我們通過 ajax 給后端發送請求,傳遞給后端我們需要* 刪除的音樂* */$.ajax({url:"/music/deleteSel",type:"POST",data:{"id":id},dataType:"json",// 執行成功 回調這個函數success:function(obj){// 看是否刪除成功if(obj.status == 0) {alert("刪除成功,重新加載此頁面!");window.location.href="list.html";}else{alert("刪除失敗!");}}});
23. 實現喜歡音樂列表頁功能
收藏音樂列表頁,和 list.html 基本一樣,但是需要注意我們需要將ajax中的 url 進行修改:
列入如下:
loveMusci.html 如下:
<script type="text/javascript">function load(musicName) {$.ajax({type:"GET",url:"/lovemusic/findlovermusic",//數據data:{"musicName":musicName},//服務器返回數據類型dataType:"json",// 如果服務器返回成功,會返回我們的回調函數success:function(obj) {console.log(obj);// obj包含了所有的返回信息,然后其中的data就包含了所有的音樂信息,可以先獲取到var data = obj.data;var s = ''; // 最原始的拼接方式// data[i].id data[i].singer data[i].title 這種形式來獲取 obj里面的信息for(var i = 0; i < data.length;i++) {var musicUrl = data[i].url + ".mp3";s += '<tr>';s += '<td>' + data[i].title + '</td>';s += '<td>' + data[i].singer + '</td>';s += '<td> <button class="btn btn-primary" onclick="playerSong(\''+musicUrl+'\')"> 播放歌曲 </button>' + '</td>';s += '<td> <button class="btn btn-primary" onclick="deleteInfo('+data[i].id+')"> 移除 </button>' + '</td>';s += '</tr>';}//然后將所有的數據放到tbody里面$('#info').html(s);}});}// 實現音樂播放function playerSong(obj) {// toPlay(url,title,startTime,autoPlay)//obj: http://localhost:8080/music/get?path=xxx.mp3// 從等號下標 下一個位置開始截取 [)var title = obj.substring(obj.lastIndexOf("=") + 1);SewisePlayer.toPlay(obj,title,0,true);}// 刪除喜歡音樂 傳入的參數是 idfunction deleteInfo(obj){console.log(obj);$.ajax({type:"POST",url:"/lovemusic/deletelovemusic",data:{"id":obj},dataType:"json",success:function(body){console.log(body);//判斷是否刪除成功if(body.data == true){//表示刪除成功alert("刪除成功了老鐵!,重新加載當前頁面哈!");window.location.href="list.html";}else{alert("刪除失敗了老鐵!");}}});}</script>
24. 實現收藏音樂列表功能
在 list.html 中 實現如下核心代碼:
// 添加喜歡的音樂/收藏音樂function loveInfo(obj){//直接發送請求$.ajax({type:"POST",url:"/lovemusic/likeMusic",data:{"id":obj},dataType:"json",success:function(body){//然后這里判斷看是否收藏成功了if(body.data == true){alert("收藏成功,^^,厲害了老鐵!");window.location.href="loveMusic.html";}else{alert("收藏失敗,^^,咋回事兒呢!");}}});}
25 實現注冊功能
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta name="viewport" content="width=device-width, initial-scale=1"/><meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/><title>注冊頁面</title><!-- 1. 導入CSS的全局樣式 --><link href="css/bootstrap.min.css" rel="stylesheet"><!-- 2. jQuery導入,建議使用1.9以上的版本 --><script src="js/jquery-3.1.1.min.js"></script><script src="js/md5.min.js"></script><!-- 3. 導入bootstrap的js文件 --><!--<script src="js/bootstrap.min.js"></script>--><script type="text/javascript"></script><style>#body{background-image: url("images/rose3.png");background-repeat: repeat;background-position: 0 20%;background-size: contain;}</style><script>// 登錄核心業務邏輯//原本是$(document).ready(function(){}) 表示當這個頁面的dom樹加載完成后才會執行這個,而后面的(document).ready可以省略,因此這里就是這樣寫也是可以的$(function(){//此時設置提交按鈕的click事件,通過id選擇器來獲取$("#submit").click(function(){//此時就需要獲取到用戶名和密碼的值let username = $("#user").val();let password = $("#password").val();//檢查一下用戶名和密碼是否為空,以及去掉空格使用trim方法if(username.trim() == "" || password.trim() == ""){alert("用戶名或密碼不能為空!");return;}//如果都不為空,就需要使用ajax來發送請求到后端,然后處理這個請求及返回響應$.ajax({type:"POST",url:"/user/logon",//數據data:{"username":username,"password":password},//服務器返回數據類型dataType:"json",success:function(data){//看狀態碼if(data.status == 0){console.log(data);alert("注冊成功了我的老寶貝,要跳轉了哦!");//登錄成功,這里就可以進行頁面的跳轉window.location.href="login.html";}else{alert("注冊失敗,用戶名已存在,--!");}}});});});</script>
</head>
<body id="body">
<div class="container" style="width: 400px;margin-top: 110px;background-color: rgba(255,255,255,0.9)"><h3 style="text-align: center;">回溯</h3><!-- <form action="login" method="post">--><div class="form-group" ><label for="user">用戶名:</label><input type="text" name="username" class="form-control" id="user" placeholder="請輸入用戶名"/></div><div class="form-group"><label for="password">密碼:</label><input type="password" name="password" class="form-control" id="password" placeholder="請輸入密碼"/></div><hr/><div class="form-group" style="text-align: center;"><!--class="form-group"--><input style="width: 200px;height: 40px" id="submit" class="btn btn btn-primary" type="button" value="注冊" ></div><!-- </form>--><!-- 出錯顯示的信息框 -->
</div>
</body>
</html>
26 配置攔截器
當我們直接訪問列表頁的時候,我們需要用攔截器攔截,否則 不登錄也能直接訪問,這是不行的
以上所有功能全部實現完畢。
項目部署
- 修改配置文件
修改音樂存放路徑:
在配置文件當中,修改如下:
- 將數據庫在服務器上重新進行建表等操作
輸入指令 mysql,進入數據庫:
將如下內容添加進去:
-- 數據庫
drop database if exists `onlinemusic`;
create database if not exists `onlinemusic` character set utf8;-- 使用數據庫
use `onlinemusic`;-- 創建 user表
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`username` varchar(20) NOT NULL,
`password` varchar(255) NOT NULL
);-- 創建 music 表
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(1000) NOT NULL,
`userid` int(11) NOT NULL
);-- 創建 lovemusic
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
);
輸入指令 exit 退出數據庫
- 導包
4. 上傳
-
云服務器配置防火墻端
-
啟動項目
剛開始可以使用 java -jar xxxx.jar 啟動項目【前臺運行的方式】
運行好之后沒有問題,我們可以使用下面的命令來對項目進行運行
nohup java -jar xxx.jar >> log.log &
nohup :后臺運行項目的指令
使用 >> log.log 將運行的日志記錄到 log.log 中
& 表示 一直運行
tips:重新部署
如果更新了項目,先將 jar刪除
輸入如下指令:
rm -ri xxx.jar
查看進程
ps -ef | grep java
kill [ID] 即可刪除進程
總結
以上是生活随笔為你收集整理的JavaWeb——在线音乐播放器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Jewels
- 下一篇: 手机来电通核心模块——归属地数据库设计(