4、选择项目存放路径,就可以创建出一个 SpringBoot 项目
1、创建 config 包,在 config 包中创建 LoginInterceptor 类
4、在 UserMapper 接口中新增 selectByName 方法
4、在 UserController 类中添加 register 方法
2、在 MusicController 类中添加 playMusic 方法
4. 在 MusicController 类中添加 deleteByMusicId 方法
2. 在 MusicController 类中添加 deleteSelMusic 方法
4、在 MusicController 类中添加 findMusic 方法
4、在 LoveMusicController 类中添加 findLoveMusic 方法
4、在 LoveMusicController 类中添加 removeLoveMusic 方法
3、调整 MusicController 类中的 deleteMusicByMusicId 和 deleteSelMusic 方法










- #配置数据库
- spring.datasource.url=jdbc:mysql://127.0.0.1:3306/onlinemusic?characterEncoding=utf8&serverTimezone=UTC
- spring.datasource.username=你的用户名
- spring.datasource.password=你的密码
- spring.datasource.driver-class-name=com.mysql.jdbc.Driver
-
- #配置xml
- mybatis.mapper-locations=classpath:mybatis/**Mapper.xml
-
- # 音乐上传后的路径
- music.local.path=E:/SaveMusic/(填写存放歌曲的路径)
-
- #配置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

- -- 创建数据库
- drop database if exists `onlinemusic`;
- create database if not exists `onlinemusic` character set utf8;
- -- 使用数据库
- use `onlinemusic`;
-
- -- 用户表
- DROP TABLE IF EXISTS `user`;
- CREATE TABLE `user` (
- `userid` INT PRIMARY KEY AUTO_INCREMENT comment '用户id',
- `username` varchar(20) NOT NULL comment '用户名',
- `password` varchar(255) NOT NULL comment '密码'
- );
-
- -- 歌曲表
- DROP TABLE IF EXISTS `music`;
- CREATE TABLE `music` (
- `musicid` int PRIMARY KEY AUTO_INCREMENT comment '歌曲id',
- `title` varchar(50) NOT NULL comment '歌曲名称',
- `singer` varchar(30) NOT NULL comment '歌手',
- `time` varchar(13) NOT NULL comment '上传歌曲时间',
- `url` varchar(1000) NOT NULL comment '存放歌曲的路径',
- `userid` int(11) NOT NULL comment '上传歌曲的用户'
- );
-
- -- 歌曲收藏表
- DROP TABLE IF EXISTS `lovemusic`;
- CREATE TABLE `lovemusic` (
- `loveid` int PRIMARY KEY AUTO_INCREMENT comment '收藏歌曲的id',
- `user_id` int(11) NOT NULL comment '收藏歌曲的用户id',
- `music_id` int(11) NOT NULL comment '歌曲id'
- );
在 package com.example.musicserver 目录下创建一个 tools 包(工具包),在这个包中存放整个项目要使用的工具类。
- 设计统一的响应体工具类,因为做任何操作时都需要响应,所以封装一个通用的响应工具类,这个工具类设计成一个泛型类。
- package com.example.onlinemusic.tools;
-
- import lombok.Data;
-
- @Data
- public class ResponseBodyMessage
{ - private int status; //状态码
-
- private String message; // 返回的信息(出错的原因等)
-
- private T data; // 返回给前端的数据(因为返回的数据类型不确定,可能是 String,boolea,int ...,因此使用泛型)
-
- public ResponseBodyMessage(int status, String message, T data) {
- this.status = status;
- this.message = message;
- this.data = data;
- }
- }
- 这个类用来存储不变的常量。 例如:设置 session 对象中的 key 值,key 是一个不变的字符串。
- 如果在其他地方获取对应的 session 就可以通过这个类中的字符串进行获取。
- package com.example.onlinemusic.tools;
-
- public class Constant {
- public static final String USER_SESSION_KEY= "USERINFO_SESSION_KEY"; // 设置 session 中的 key 值
- }
MD5是一个安全的散列算法,输入两个不同的明文不会得到相同的输出值,根据输出值,不能得到原始的明文,即其过程不可逆; 但是虽然不可逆,但是不是说就是安全的。因为自从出现彩虹表后,这样的密码也"不安全"。
- 彩虹表:彩虹表就是一个庞大的、针对各种可能的字母组合预先计算好的哈希值的集合,不一定是针对MD5算法的,各种算法的都有,有了它可以快速的破解各类密码。越是复杂的密码,需要的彩虹表就越大,现在主流的彩虹表都是100G以上。
更安全的做法是加盐或者长密码等做法,让整个加密的字符串变的更长,破解时间变慢。密码学的应用安全,是建立在破解所要付出的成本远超出能得到的利益上的。
- 加盐的做法:盐是在每个密码中加入一些单词来变成一个新的密码,存入数据库当中。
(1)在 pom.xml 文件中添加依赖(添加到
- <dependency>
- <groupId>commons-codecgroupId>
- <artifactId>commons-codecartifactId>
- dependency>
- <dependency>
- <groupId>org.apache.commonsgroupId>
- <artifactId>commons-lang3artifactId>
- <version>3.9version>
- dependency>
(2)在 tools 包中创建 MD5Util 类
- package com.example.onlinemusic.tools;
-
- import org.apache.commons.codec.digest.DigestUtils;
-
- public class MD5Util {
- // 定义一个固定的盐值
- private static final String salt = "1j2a3v4a5"; // 盐值可以自定义
-
- public static String md5(String src) {
- return DigestUtils.md5Hex(src);
- }
-
- /**
- * 第一次加密 :模拟前端自己加密,然后传到后端
- *
- * @param inputPass
- * @return
- */
- public static String inputPassToFormPass(String inputPass) {
- String str = "" + salt.charAt(1) + salt.charAt(3) + inputPass
- + salt.charAt(5) + salt.charAt(6);
- return md5(str);
- }
-
- /**
- * 第2次MD5加密
- *
- * @param formPass 前端加密过的密码,传给后端进行第2次加密
- * @param salt 用户数据库当中的盐值
- * @return
- */
- public static String formPassToDBPass(String formPass, String salt) {
- String str = "" + salt.charAt(0) + salt.charAt(2) + formPass + salt.charAt(5)
- + salt.charAt(4);
- return md5(str);
- }
-
- /**
- * 上面两个函数合到一起进行调用
- *
- * @param saltDB
- * @return
- * @paraminputPass
- */
- public static String inputPassToDbPass(String inputPass, String saltDB) {
- String formPass = inputPassToFormPass(inputPass);
- String dbPass = formPassToDBPass(formPass, saltDB);
- return dbPass;
- }
-
- public static void main(String[] args) {
- System.out.println("对用户输入密码进行第1次加密:" + inputPassToFormPass("123456"));
- System.out.println("对用户输入密码进行第2次加密:" + formPassToDBPass(inputPassToFormPass("123456"), salt));
- System.out.println("对用户输入密码进行第2次加密:" + inputPassToDbPass("123456", salt));
- }
- }
运行结果

不管运行多少次,这个密码是规定的。因为这里没有用随机盐值。当密码长度很大,盐值也是随机的情况下,密码的强度也加大了。破解成本也增加了。
- Bcrypt 就是一款加密工具,可以比较方便地实现数据的加密工作。也可以简单理解为它内部自己实现了随机加盐处理 。
- 使用MD5加密,每次加密后的密文其实都是一样的,这样就方便了MD5通过大数据的方式进行破解。
- Bcrypt生成的密文是60位的,而MD5的是32位的,因此 Bcrypt 破解难度更大。
(1)在 pom.xml 文件中添加依赖(添加到
- <dependency>
- <groupId>org.springframework.securitygroupId>
- <artifactId>spring-security-webartifactId>
- dependency>
- <dependency>
- <groupId>org.springframework.securitygroupId>
- <artifactId>spring-security-configartifactId>
- dependency>
(2)在springboot启动类添加下面的内容
- @SpringBootApplication(exclude =
- {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
(3)在 tools 包中创建 BCryptTest 测试类
- package com.example.onlinemusic.tools;
-
- import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
-
- public class BCryptTest {
- public static void main(String[] args) {
- //模拟从前端获得的密码
- String password = "123456";
- BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
- String newPassword = bCryptPasswordEncoder.encode(password);
- System.out.println("加密的密码为: "+newPassword);
-
- //使用matches方法进行密码的校验
- boolean same_password_result = bCryptPasswordEncoder.matches(password,newPassword);
-
- //返回true
- System.out.println("加密的密码和正确密码对比结果: "+same_password_result);
- boolean other_password_result = bCryptPasswordEncoder.matches("987654",newPassword);
-
- //返回false
- System.out.println("加密的密码和错误的密码对比结果: " + other_password_result);
- }
- }
运行结果(每次运行的生成的密码都不一样)

- encode方法:对用户密码进行加密。
- matches方法:参数一,待检验的未加密的密码 。参数二:从数据库中查询出的加密后密码 。
- BCrypt加密: 一种加盐的单向Hash,不可逆的加密算法,同一种明文(plaintext),每次加密后的密文都不一样,而且不可反向破解生成明文,破解难度很大。
- MD5加密: 是不加盐的单向Hash,不可逆的加密算法,同一个密码经过hash的时候生成的是同一个hash值,在大多数的情况下,有些经过md5加密的方法将会被破解。
- Bcrypt生成的密文是60位的。而MD5的是32位的。
- 目前,MD5和BCrypt比较流行。相对来说,BCrypt比MD5更安全,但加密更慢。
- 虽然BCrpyt也是输入的字符串+盐,但是与MD5+盐的主要区别是:每次加的盐不同(BCrpyt 中的盐值是随即生成的),导致每次生成的结果也不相同。无法比对!
- package com.example.onlinemusic.config;
-
- import com.example.onlinemusic.tools.Constant;
- import com.example.onlinemusic.tools.ResponseBodyMessage;
- import org.springframework.web.servlet.HandlerInterceptor;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import javax.servlet.http.HttpSession;
-
- public class LoginInterceptor implements HandlerInterceptor {
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- HttpSession session = request.getSession(false);
- if(session == null || session.getAttribute(Constant.USER_SESSION_KEY)==null){
- return false;
- }
- return true;
- }
- }
- package com.example.onlinemusic.config;
-
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
- import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
- import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-
- @Configuration
- public class AppConfig implements WebMvcConfigurer {
-
- @Bean
- public BCryptPasswordEncoder getBCryptPasswordEncoder(){
- return new BCryptPasswordEncoder();
- }
-
- /**
- * 添加拦截器
- * @param registry
- */
- @Override
- public void addInterceptors(InterceptorRegistry registry) {
- // 登录之后才可以访问其他页面
- LoginInterceptor loginInterceptor = new LoginInterceptor();
- registry.addInterceptor(loginInterceptor).
- // 拦截所有的
- addPathPatterns("/**")
- //排除所有的JS
- .excludePathPatterns("/js/**.js")
- //排除images下所有的元素
- .excludePathPatterns("/images/**")
- .excludePathPatterns("/css/**.css")
- .excludePathPatterns("/fronts/**")
- .excludePathPatterns("/player/**")
- .excludePathPatterns("/login.html")
- .excludePathPatterns("/register.html")
- //排除登录和注册接口
- .excludePathPatterns("/user/login")
- .excludePathPatterns("/user/register");
- }
- }
- 请求:
- {
- post, // 使用 post 请求
- /user/login // 请求路径
- data:{ username, password } // 传入的数据
- }
-
- 响应:
- {
- "status": 200,
- "message": "登录成功",
- "data": {
- "id": xxxxx,
- "username": xxxxxx,
- "password": xxxxxxxx
- }
-
- }
-
- 响应设计字段解释:
- {
- 状态码为 200 表示成功,-200表示失败
- 状态描述信息,描述此次请求成功或者失败的原因
- 返回的数据,请求成功后,需要给前端的数据信息(返回用户id,用户名)
- }
- package com.example.onlinemusic.model;
-
- import lombok.Data;
-
- @Data
- public class User {
- private int userId; // 用户id
- private String username; // 用户名
- private String password; // 密码
- }
在 package com.example.musicserver.mapper 包中创建 UserMapper 接口
- package com.example.onlinemusic.mapper;
-
- import com.example.onlinemusic.model.User;
- import org.apache.ibatis.annotations.Mapper;
-
- @Mapper
- public interface UserMapper {
- User login(User loginUser);
- }
- "1.0" encoding="UTF-8"?>
- mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="com.example.onlinemusic.mapper.UserMapper">
- <select id="login" resultType="com.example.onlinemusic.model.User">
- select * from user where username=#{username} and password=#{password}
- select>
-
- <select id="selectByName" resultType="com.example.onlinemusic.model.User">
- select * from user where username=#{username};
- select>
-
- mapper>
- // 通过用户名查询用户是否存在(用户名是唯一的)
- User selectByName(String username);
- package com.example.onlinemusic.controller;
-
- import com.example.onlinemusic.mapper.UserMapper;
- import com.example.onlinemusic.model.User;
- import com.example.onlinemusic.tools.Constant;
- import com.example.onlinemusic.tools.ResponseBodyMessage;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestParam;
- import org.springframework.web.bind.annotation.RestController;
-
- import javax.servlet.http.HttpServletRequest;
-
- @RestController
- @RequestMapping("/user")
- public class UserController {
- @Autowired
- private UserMapper userMapper;
-
- // 使用 BCrypt 对密码进行加密
- @Autowired
- private BCryptPasswordEncoder bCryptPasswordEncoder;
-
- @RequestMapping("/login")
- // 传入用户名和密码
- public ResponseBodyMessage
login(@RequestParam String username, @RequestParam String password, HttpServletRequest request){ -
- // 调用 UserMapper 接口
- User user = userMapper.selectByName(username);
-
- // 判断是否登录成功
- if(user != null){
- System.out.println("登录成功"); // 可以在控制台中打印登录信息
-
- // 判断当前用户输入的密码(password) 与 数据库中查询到的密码(加密的密码,getPassword())是否匹配
- boolean flag = bCryptPasswordEncoder.matches(password,user.getPassword());
-
- if(!flag){
- // 密码不匹配,登录失败
- return new ResponseBodyMessage<>(-200,"用户名或密码错误",user);
- }
-
- // 如果登录成功就将信息写入到 session 中(在 session 中存储了一个用户信息对象,此后可以随时从 session 中将这个对象取出来进行一些操作)
- request.getSession().setAttribute(Constant.USER_SESSION_KEY,user);
- // 状态码为200,表示登录成功,并返回用户信息
- return new ResponseBodyMessage<>(200,"登录成功",user);
- }else{
- System.out.println("登录失败");
- // 状态码为500,表示登录失败,并返回用户信息
- return new ResponseBodyMessage<>(-200,"用户名或密码错误",user);
-
- }
- }
- }
在数据库中插入一条数据,启动项目,使用 postman 进行测试。

-
- $(function(){
- $("#submit").click(function(){
- // 点击登录按钮,获取用户名和密码
- var username = $("#username").val();
- var 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){
- console.log(data);
- // 如果状态码为 200,表示登录成功
- if(data.status == 200){
- alert("登录成功");
- // 跳转到指定页面
- window.location.href="list.html";
- }else{
- alert("登录失败,账号或密码错误");
- // 登录失败,将用户名或密码置空
- $("#username").val("");
- $("#password").val("");
- }
- }
- });
- });
- });
-
- $(function () {
- $("#register").click(function () {
- window.location.href="register.html";
- });
- });
-
-
- 请求:
- {
- post, // 使用 post 请求
- /user/register // 请求路径
- data:{ username, password } // 传入的数据
- }
-
- 响应:
- {
- "status": 200,
- "message": "注册成功",
- "data": {
- "id": xxxxx,
- "username": xxxxxx,
- "password": xxxxxxxx
- }
-
- }
-
- 响应设计字段解释:
- {
- 状态码为 200 表示成功,-200表示失败
- 状态描述信息,描述此次请求成功或者失败的原因
- 返回的数据,请求成功后,需要给前端的数据信息(返回用户id,用户名)
- }
- // 输入用户名和密码,注册账号
- boolean insertInToValues(String username,String password);
- <insert id="insertInToValues" >
- insert into user(username,password)
- values(#{username},#{password});
- insert>
- /**
- * 用户注册
- * @param username
- * @param password
- * @return
- */
- @RequestMapping("/register")
- public ResponseBodyMessage
register(@RequestParam String username,@RequestParam String password) { - User user1 = userMapper.selectByName(username);
- if(user1 != null) {
- return new ResponseBodyMessage<>(-1,"当前用户已经存在",false);
- }else {
- String newPassword = bCryptPasswordEncoder.encode(password);
- boolean flag = userMapper.insertInToValues(username,newPassword);
- if(flag == true){
- return new ResponseBodyMessage<>(200,"注册成功",true);
- }else{
- return new ResponseBodyMessage<>(-200,"注册失败",false);
- }
- }
- }

-
- $(function(){
- $("#register").click(function(){
- var username = $("#username").val();
- var password = $("#password").val();
- $.ajax({
- url: "/user/register",
- type: "POST",
- data:{
- "username":username,
- "password":password
- },
- dataType:"json",
- success: function(data){
- console.log(data);
- if(data.status == 200) {
- alert("注册成功");
- window.location.href="login.html";
- }else{
- alert("注册失败");
- $("#username").val("");
- $("#password").val("");
- $("#repassword").val("");
- }
- }
- })
- })
- });
-
- 请求:
- {
- post, // 使用 post 请求
- /music/upload // 请求路径
- {singer,MultipartFile file},//上传歌手,歌曲文件
- }
-
- 响应:
- {
- "status": 200,
- "message": "上传成功!",
- "data": true
- }
-
- 响应设计字段解释:
- {
- 状态码为 200 表示成功,-200 表示失败
- 状态描述信息,描述此次请求成功或者失败的原因
- 返回的数据,请求成功后,需要给前端的数据信息,true 表示上传成功,false 表示上传失败
- }
- package com.example.onlinemusic.model;
-
- import lombok.Data;
-
- @Data
- public class Music {
- private int musicId; // 歌曲id
- private String title; // 歌曲名称
- private String singer; //歌手
- private String time; // 上传歌曲的时间
- private String url; // 上传歌曲的路径
- private int userId; // 上传歌曲的用户
- }
- package com.example.onlinemusic.mapper;
-
- import org.apache.ibatis.annotations.Mapper;
-
- @Mapper
- public interface MusicMapper {
- /**
- * 插入音乐
- * @param title
- * @param singer
- * @param time
- * @param url
- * @param userId
- * @return
- */
- int insert(String title,String singer,String time,String url,int userId);
-
- /**
- * 查询歌曲名
- * @param title
- * @return
- */
- List
selectBytitle(String title); - }
- "1.0" encoding="UTF-8"?>
- mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="com.example.onlinemusic.mapper.UserMapper">
-
- <insert id="insert">
- insert into music(title,singer,time,url,userId)
- values(#(title),#(singer),#(time),#(url),#(userId));
- insert>
-
- <select id="selectBytitle" resultType="com.example.onlinemusic.model.Music">
- select * from music where title = #{title};
- select>
-
- mapper>
- package com.example.onlinemusic.controller;
-
- import com.example.onlinemusic.mapper.LoveMusicMapper;
- import com.example.onlinemusic.mapper.MusicMapper;
- import com.example.onlinemusic.model.Music;
- import com.example.onlinemusic.model.User;
- import com.example.onlinemusic.tools.Constant;
- import com.example.onlinemusic.tools.ResponseBodyMessage;
- import org.apache.ibatis.binding.BindingException;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.http.ResponseEntity;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestParam;
- import org.springframework.web.bind.annotation.RestController;
- import org.springframework.web.multipart.MultipartFile;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import javax.servlet.http.HttpSession;
- import java.io.File;
- import java.io.IOException;
- import java.nio.file.Files;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.List;
-
-
- @RestController
- @RequestMapping("/music")
- public class MusicController {
-
- // 在配置文件中添加歌曲路径
- @Value("${music.local.path}")
- private String SAVE_PATH;
-
- @Autowired
- private MusicMapper musicMapper;
-
- @Autowired
- private LoveMusicMapper loveMusicMapper;
-
- /**
- * 上传音乐
- * 请求路径:/music/upload
- * @param singer 上传歌手
- * @param file 上传歌曲
- * @param request 请求,验证是否登录
- * @return 返回true表示上传成功,返回false表示上传失败
- */
- @RequestMapping("/upload")
- public ResponseBodyMessage
insertMusic(@RequestParam String singer, @RequestParam ("filename")MultipartFile file, HttpServletRequest request, HttpServletResponse response){ -
- // 1. 检查是否登录
- HttpSession session = request.getSession(false);
- if(session == null || session.getAttribute(Constant.USER_SESSION_KEY)==null){
- System.out.println("没有登录");
- return new ResponseBodyMessage<>(-200,"请登录后再进行上传",false);
- }
-
- // 2. 获取的是文件的完整名称,包括文件名称+文件拓展名
- String fileNameAndType = file.getOriginalFilename();
-
- // 3. 查询数据库中是否存在当前要上传的音乐(歌曲名+歌手)
- /**
- * 获取标题(标题不包含后缀.mp3)
- * 使用 lastIndexOf 从后向前找第一个 .
- */
- int index = fileNameAndType.lastIndexOf(".");
- String title = fileNameAndType.substring(0,index);
-
- // 使用 list 存放歌曲,获取歌曲名
- List
list = musicMapper.selectBytitle(title); - if(list != null){
- for(Music music : list){
- // 判断当前上传的歌曲+歌手在数据库中是否存在,如果存在则上传失败(歌曲名+歌手 不能重复)
- if(music.getSinger().equals(singer)){
- return new ResponseBodyMessage<>(-200,"上传失败,数据库中存在此歌曲,不能重复上传",false);
- }
- }
- }
-
- // 2. 数据上传到服务器
-
- // 上传文件路径
- String path = SAVE_PATH+fileNameAndType;
-
- // 上传文件
- File dest = new File(path);
- if(!dest.exists()){
- //如果路径不存在就创建目录
- dest.mkdir();
- }
- try {
- // 将接收到的文件传输到给定目标路径
- file.transferTo(dest);
- } catch (IOException e) {
- e.printStackTrace();
- return new ResponseBodyMessage<>(-200,"上传失败,服务器出现问题",false);
- }
-
- // 3. 判断上传的文件是否为mp3文件(判断是否存在 TAG 字符)
- File file1 = new File(path);
- byte[] bytes = null;
- try {
- bytes = Files.readAllBytes(file1.toPath());
- if(bytes == null){
- return new ResponseBodyMessage<>(-200,"上传失败,文件不存在",false);
- }
- String str = new String(bytes);
- if(!str.contains("TAG")){
- file1.delete();
- return new ResponseBodyMessage<>(-200,"上传的文件不是mp3文件",false);
- }
- } catch (IOException e) {
- e.printStackTrace();
- return new ResponseBodyMessage<>(-200,"上传失败,服务器出现问题",false);
- }
-
- // 4. 将数据上传到数据库中(1. 准备数据 2. 调用 insert)
-
-
- /**
- * 获取 userId
- * 登录成功后将用户信息写到 session 中,通过 session 中key值(Constant.USERINFO) 就可以获取到对应的 value 值(用户信息)
- */
- User user = (User)session.getAttribute(Constant.USER_SESSION_KEY);
- // 获取用户Id
- int userId = user.getUserId();
-
- /**
- * url 的作用: 播放音乐->发送 http 请求
- */
- String url = "/music/get?path="+title; // 将 url 存入数据库时不用加后缀 .mp3,在取数据的时候加一个后缀就可以了
-
- /**
- * 获取上传的时间
- * 将获取的时间格式化为:年-月-日 的形式
- */
- SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
- String time = simpleDateFormat.format(new Date());
-
- // 插入数据
- try {
- int ret = musicMapper.insert(title,singer,time,url,userId);
- if(ret == 1){
- // 数据插入成功
- // 这里应该跳转到音乐列表页面
- response.sendRedirect("/list.html");
- return new ResponseBodyMessage<>(200,"数据库上传成功",true);
- }else{
- // 数据插入失败
- return new ResponseBodyMessage<>(-200,"数据库上传失败",false);
- }
- }catch (BindingException | IOException e){
- // 数据库上传失败,将上传到文件夹中的数据删除
- dest.delete();
- e.printStackTrace();
- return new ResponseBodyMessage<>(-200,"数据库上传失败",false);
- }
- }
-
- /**
- * 播放音乐
- * 请求路径:/music/get?get=xxx.mp3
- * @param path
- * @return
- */
- @RequestMapping("/get")
- public ResponseEntity<byte[]> playMusic(@RequestParam String path) {
- File file = new File(SAVE_PATH+path);
- byte[] bytes = null;
- try {
- bytes = Files.readAllBytes(file.toPath()); // 将文件路径中的文件以字节的形式读取,放到 bytes 数组中
- if(bytes == null){
- // 如果没有读取的文件,则返回状态码 400
- return ResponseEntity.badRequest().build();
- }
- // 成功读取到文件
- return ResponseEntity.ok(bytes);
- } catch (IOException e) {
- e.printStackTrace();
- }
- // 如果没有读取的文件,则返回状态码 400
- return ResponseEntity.badRequest().build();
- }
-
- /**
- * 删除单个音乐
- * 请求路径:/music/delete?musicId=x
- * @param musicId
- * @return
- */
- @RequestMapping("/delete")
- public ResponseBodyMessage
deleteMusicByMusicId(@RequestParam String musicId){ - /**
- * 1. 检查待删除的音乐是否存在
- * 2. 如果存在要删除的音乐
- * 1. 删除数据库中的数据
- * 2. 删除服务器上的数据
- */
- // 检查待删除的音乐是否存在
- Music music = musicMapper.findMusicById(Integer.parseInt(musicId));
- if(music == null){
- System.out.println("在控制台打印日志:没有要删除的音乐id");
- return new ResponseBodyMessage<>(-200,"要删除的音乐不存在",false);
- }else{
- // 调用 musicMapper 接口中的 deleteMusicById 方法删除数据库中的数据
- int ret = musicMapper.deleteMusicById(Integer.parseInt(musicId));
-
- if(ret == 1){
- // 成功删除数据库中的数据
- /*int index = music.getUrl().lastIndexOf("=");
- String fileName = music.getUrl().substring(index+1);*/
-
- String fileName = music.getTitle();
- // 根据存放音乐的路径删除服务器中的数据
- File file = new File(SAVE_PATH+fileName+".mp3");
- System.out.println("在控制台打印日志:当前音乐的路径:"+file.getPath());
- //对删除服务器中的数据进行判断
- if(file.delete()){
- // 删除成功
- return new ResponseBodyMessage<>(200,"音乐删除成功",true);
- }else{
- return new ResponseBodyMessage<>(-200,"服务器中的音乐删除失败",false);
- }
- }else{
- return new ResponseBodyMessage<>(-200,"数据库中的音乐删除失败",false);
- }
- }
- }
-
- /**
- * 批量删除选中的音乐
- * 请求路径:/music/deleteSel
- * @param musicId
- * @return
- */
- @RequestMapping("/deleteSel")
- public ResponseBodyMessage
deleteSelMusic(@RequestParam("musicId[]") List musicId) { - System.out.println("在控制台打印日志:所有音乐的 Id:"+musicId);
- int sum = 0; // 统计删除的音乐
- for (int i = 0; i < musicId.size(); i++) {
- Music music = musicMapper.findMusicById(musicId.get(i));
- if (music == null) {
- System.out.println("没有要删除的音乐id");
- return new ResponseBodyMessage<>(-200, "要删除的音乐不存在", false);
- }
- int ret = musicMapper.deleteMusicById(musicId.get(i));
- if (ret == 1) {
- // 成功删除数据库中的数据
- String fileName = music.getTitle();
- // 根据存放音乐的路径删除服务器中的数据
- File file = new File(SAVE_PATH + fileName + ".mp3");
- System.out.println("当前音乐的路径:" + file.getPath());
- //对删除服务器中的数据进行判断
- if (file.delete()) {
- // 成功删除一条数据,sum 就加上 ret(数据库中成功删除)
- sum += ret;
- } else {
- return new ResponseBodyMessage<>(-200, "服务器中的音乐删除失败", false);
- }
- }else{
- return new ResponseBodyMessage<>(-200,"数据库中的音乐删除失败",false);
- }
- }
- if(sum == musicId.size()){
- // 选中的数据全部删除成功
- System.out.println("在控制台打印日志:选择中的歌曲删除成功");
- return new ResponseBodyMessage<>(200,"音乐删除成功",true);
- }else{
- System.out.println("在控制台打印日志:选择中的歌曲删除失败");
- return new ResponseBodyMessage<>(-200,"音乐删除失败",false);
- }
- }
-
- /**
- * 查询音乐
- * @param musicName
- * @return
- */
- @RequestMapping("/findmusic")
- public ResponseBodyMessage
> findMusic(@RequestParam(required = false) String musicName){
- List
musicList = null; - if(musicName != null){
- // 模糊查询,根据歌曲名查询指定的歌曲
- musicList = musicMapper.findMusicByName(musicName);
- }else{
- // 查询所有的音乐
- musicList = musicMapper.findMusic();
- }
- // 查询成功,返回查询到的音乐信息
- return new ResponseBodyMessage<>(200,"查询成功",musicList);
- }
- }

- <div class="upload-container">
- <form method="post" enctype="multipart/form-data" action="/music/upload">
- <div class="upload-dialog">
- <strong>上传本地音乐strong>
- <em>XiaoXiangYeYu's music upload_musicem>
- <div class="row">
- <span>音乐span>
- <input type="file" id="file" name="filename" placeholder="上传歌曲" class="upload_txtbx"/>
- div>
- <div class="row">
- <span>歌手span>
- <input type="text" id="singer" name="singer" placeholder="请输入歌手名" class="upload_txtbx"/>
- div>
- <div class="row">
- <input type="submit" id="submit" value="上传歌曲" class="submit_btn"/>
- div>
- div>
- form>
- div>

- 请求:
- {
- get, // 使用 get 请求
- /music/get?path=xxx.mp3 // 请求路径(数据库中存储的 url)
- }
-
- 响应:
- {
- 音乐数据本身的字节信息 // 服务器将数据以字节的形式返回给客户端,客户端获取到信息后就可以进行解析,然后播放音乐
- }
- /**
- * 播放音乐
- * 请求路径:/music/get?paht=xxx.mp3
- * @param path
- * @return
- */
- @RequestMapping("/get")
- public ResponseEntity<byte[]> playMusic(@RequestParam String path) {
- File file = new File(SAVE_PATH+path);
- byte[] bytes = null;
- try {
- bytes = Files.readAllBytes(file.toPath()); // 将文件路径中的文件以字节的形式读取,放到 bytes 数组中
- if(bytes == null){
- // 如果没有读取的文件,则返回状态码 400
- return ResponseEntity.badRequest().build();
- }
- // 成功读取到文件
- return ResponseEntity.ok(bytes);
- } catch (IOException e) {
- e.printStackTrace();
- }
- // 如果没有读取的文件,则返回状态码 400
- return ResponseEntity.badRequest().build();
- }

- var audios = document.getElementsByTagName("audio");
- // 暂停函数
- function pauseAll() {
- var self = this;
- [].forEach.call(audios, function (i) {
- // 将audios中其他的audio全部暂停
- i !== self && i.pause();
- })
- }
- // 给play事件绑定暂停函数
- [].forEach.call(audios, function (i) {
- i.addEventListener("play", pauseAll.bind(i));
- });
- 请求:
- {
- post, // 使用 post 请求
- /music/delete, // 请求路径
- musicId // 要删除歌曲的 id
- }
-
- 响应:
- {
- "status": 200,
- "message": "删除成功!",
- "data": true
- }
-
- 响应设计字段解释:
- {
- 状态码为 200 表示成功,-200 表示失败
- 状态描述信息,描述此次请求成功或者失败的原因
- 返回的数据,请求成功后,需要给前端的数据信息,true 表示删除成功,false 表示删除失败
- }
- /**
- * 通过音乐 Id 查询当前音乐是否存在
- * @param musicId
- * @return
- */
- Music findMusicById(int musicId);
-
- /**
- * 通过当前音乐 Id 删除音乐
- * @param musicId
- * @return
- */
- int deleteMusicById(int musicId);
- <select id="findMusicById" resultType="com.example.onlinemusic.model.Music">
- select * from music where musicid = #{musicid};
- select>
-
- <delete id="deleteMusicById" parameterType="java.lang.Integer">
- delete from music where musicid = #{musicid};
- delete>
- /**
- * 删除单个音乐
- * 请求路径:/music/delete?musicId=x
- * @param musicId
- * @return
- */
- @RequestMapping("/delete")
- public ResponseBodyMessage
deleteByMusicId(@RequestParam String musicId){ - /**
- * 1. 检查待删除的音乐是否存在
- * 2. 如果存在要删除的音乐
- * 1. 删除数据库中的数据
- * 2. 删除服务器上的数据
- */
- // 检查待删除的音乐是否存在
- Music music = musicMapper.findMusicById(Integer.parseInt(musicId));
- if(music == null){
- System.out.println("在控制台打印日志:没有要删除的音乐id");
- return new ResponseBodyMessage<>(-200,"要删除的音乐不存在",false);
- }else{
- // 调用 musicMapper 接口中的 deleteMusicById 方法删除数据库中的数据
- int ret = musicMapper.deleteMusicById(Integer.parseInt(musicId));
-
- if(ret == 1){
- // 成功删除数据库中的数据
- String fileName = music.getTitle();
-
- // 根据存放音乐的路径删除服务器中的数据
- File file = new File(SAVE_PATH+fileName+".mp3");
- System.out.println("在控制台打印日志:当前音乐的路径:"+file.getPath());
-
- //对删除服务器中的数据进行判断
- if(file.delete()){
- // 删除成功
- return new ResponseBodyMessage<>(200,"音乐删除成功",true);
- }else{
- return new ResponseBodyMessage<>(-200,"服务器中的音乐删除失败",false);
- }
- }else{
- return new ResponseBodyMessage<>(-200,"数据库中的音乐删除失败",false);
- }
- }
- }

- function deleteInfo(obj){
- console.log(obj);
- $.ajax({
- url:"/music/delete",
- type:"POST",
- data:{
- "musicId":obj
- },
- dataType:"json",
-
- success:function(data){
- console.log(data);
- if(data.data == true){
- alert("删除成功,重新加载当前页面");
- window.location.href = "list.html";
- }else{
- alert("删除失败");
- }
- }
-
- });
- }
- 请求:
- {
- post, // 使用 post 请求
- /music/deleteSel, // 请求路径
- data:{
- "id":musicId // 要删除的歌曲 id 的数组
- }
- }
-
- 响应:
- {
- "status": 200,
- "message": "批量删除成功!",
- "data": true
- }
-
- 响应设计字段解释:
- {
- 状态码为 200 表示成功,-200 表示失败
- 状态描述信息,描述此次请求成功或者失败的原因
- 返回的数据,请求成功后,需要给前端的数据信息,true 表示删除成功,false 表示删除失败
- }
- /**
- * 批量删除选中的音乐
- * 请求路径:/music/deleteSel
- * @param musicId
- * @return
- */
- @RequestMapping("/deleteSel")
- public ResponseBodyMessage
deleteSelMusic(@RequestParam("musicId[]") List musicId) { - System.out.println("在控制台打印日志:所有音乐的 Id:"+musicId);
- int sum = 0; // 统计删除的音乐
- for (int i = 0; i < musicId.size(); i++) {
- Music music = musicMapper.findMusicById(musicId.get(i));
- if (music == null) {
- System.out.println("没有要删除的音乐id");
- return new ResponseBodyMessage<>(-200, "要删除的音乐不存在", false);
- }
- int ret = musicMapper.deleteMusicById(musicId.get(i));
- if (ret == 1) {
- // 成功删除数据库中的数据
- String fileName = music.getTitle();
- // 根据存放音乐的路径删除服务器中的数据
- File file = new File(SAVE_PATH + fileName + ".mp3");
- System.out.println("当前音乐的路径:" + file.getPath());
- //对删除服务器中的数据进行判断
- if (file.delete()) {
- // 成功删除一条数据,sum 就加上 ret(数据库中成功删除)
- sum += ret;
- } else {
- return new ResponseBodyMessage<>(-200, "服务器中的音乐删除失败", false);
- }
- }else{
- return new ResponseBodyMessage<>(-200,"数据库中的音乐删除失败",false);
- }
- }
- if(sum == musicId.size()){
- // 选中的数据全部删除成功
- System.out.println("在控制台打印日志:整体删除成功");
- return new ResponseBodyMessage<>(200,"音乐删除成功",true);
- }else{
- System.out.println("在控制台打印日志:整体删除失败");
- return new ResponseBodyMessage<>(-200,"音乐删除失败",false);
- }
- }
- }

- $(function(){
- $("#submit1").click(function(){
- var name = $("#exampleInputName2").val();
- load(name);
- // window.location.href = "findMusic?musicName="+name;
- });
-
- $.when(load).done(function(){
- $("#delete").click(function(){
- var id = new Array(); // 音乐Id
- var i = 0; // 数组下标
- // 遍历checkbox
- $("input:checkbox").each(function(){
- // 如果被选中,this代表发生事件的dom元素,
- if($(this).is(":checked")){
- id[i] = $(this).attr("id");
- i++;
- }
- });
- console.log(id);
-
- $.ajax({
- url:"/music/deleteSel",
- data:{
- "musicId":id
- },
- dataType:"json",
- type:"POST",
-
- success:function(obj){
- if(obj.data == true){
- alert("删除成功");
- window.location.href = "list.html";
- }else{
- alert("删除失败");
- }
- }
- });
- });
- });
-
- });
此处查询需要满足两个功能:
- 支持模糊查询
- 支持传入参数为空,当参数为空时默认查询到所有的音乐
- 请求:
- {
- get, // 使用 get 请求
- /music/findmusic, // 请求路径
- data:{musicName:musicName}, // 根据歌曲名进行查询
- }
-
- 响应:【不给musicName传参】// 如果不传参时默认查询到所有的音乐
- {
- "status": 200,
- "message": "查询到了歌曲的信息",
- "data": [
- {
- "id": 19,
- "title": "张靓颖 - 我的梦",
- "singer": "张靓颖",
- "url": "/music/get?path=张靓颖 - 我的梦",
- "time": "2022-08-20",
- "userid": 1
- },
- {
- "id": 20,
- "title": "纯音乐 - Victory",
- "singer": "张三",
- "url": "/music/get?path=纯音乐 - Victory",
- "time": "2022-03-20",
- "userid": 1
- }]
- }
-
- 响应:【给musicName传参】// 如果传入参数返回指定查询的歌曲
- {
- "status": 200,
- "message": "查询到了歌曲的信息",
- "data": [
- {
- "id": 19,
- "title": "张靓颖 - 我的梦",
- "singer": "张靓颖",
- "url": "/music/get?path=张靓颖 - 我的梦",
- "time": "2022-08-20",
- "userid": 1
- }]
- }
-
- 响应设计字段解释:
- {
- 状态码为 200 表示成功
- 状态描述信息,描述此次请求成功
- 返回的数据,请求成功后给前端的数据信息,返回查询到的音乐信息(歌曲id、歌曲名、歌手、存放歌曲的路径、上传时间、上传用户的id)
- }
- /**
- * 查询所有的音乐
- * @return
- */
- List
findMusic(); -
- /**
- * 模糊查询,根据歌曲名查询指定的歌曲
- * @param musicName
- * @return
- */
- List
findMusicByName(String musicName);
- <select id="findMusic" resultType="com.example.onlinemusic.model.Music">
- select * from music ;
- select>
-
- <select id="findMusicByName" resultType="com.example.onlinemusic.model.Music">
- select * from music where title like concat('%', #{musicName}, '%');
- select>
- /**
- * 查询音乐
- * @param musicName
- * @return
- */
- @RequestMapping("/findmusic")
- public ResponseBodyMessage
> findMusic(@RequestParam(required = false) String musicName){
- List
musicList = null; - if(musicName != null){
- // 模糊查询,根据歌曲名查询指定的歌曲
- musicList = musicMapper.findMusicByName(musicName);
- }else{
- // 查询所有的音乐
- musicList = musicMapper.findMusic();
- }
- // 查询成功,返回查询到的音乐信息
- return new ResponseBodyMessage<>(200,"查询成功",musicList);
- }


-