【 在线音乐平台(onlinemusic) 】

这篇具有很好参考价值的文章主要介绍了【 在线音乐平台(onlinemusic) 】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、核心功能

  1. 登录,注册,退出(加密操作)
  2. 上传,播放音乐
  3. 删除指定音乐
  4. 批量删除选中的音乐
  5. 查询你想要的音乐(支持模糊查询和全查询)
  6. 添加音乐至喜欢的列表
  7. 移除喜欢的音乐

【 在线音乐平台(onlinemusic) 】


二、效果演示

  1. 登录界面
    【 在线音乐平台(onlinemusic) 】

  2. 注册界面
    【 在线音乐平台(onlinemusic) 】

  3. 主页面
    【 在线音乐平台(onlinemusic) 】

  4. 收藏页面
    【 在线音乐平台(onlinemusic) 】

细节演示,后续说明 !!


三、创建项目

创建一个springboot项目,具体步骤与前面的博客记录一样,这里就不再重复赘述,大家自行参考以下博客:

1.springboot项目的基本创建

2.添加mybatis框架支持

四、数据库设计及配置数据库

4.1 数据库和表设计

思维导图如下:

【 在线音乐平台(onlinemusic) 】

具体创建:

  1. 创建数据库musicserver
-- 数据库
drop database if exists `onlinemusic`;
create database if not exists `musicserver` character set utf8;
-- 使用数据库
use `onlinemusic`;
  1. 创建表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
);
  1. 创建表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
);

title字段为歌曲名称,url字段为歌曲的路径

  1. 创建中间表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
)

4.2 配置连接数据库

打开application.properties配置如下信息:

#配置数据库
#spring.datasource.url=jdbc:mysql://127.0.0.1:3306/onlinemusic?characterEncoding=utf8&useSSL=false
#spring.datasource.username=root
#spring.datasource.password=123456
#spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 上传服务器后,此处需要修改如下:外网IP ,服务器上的数据库没有密码
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

# 音乐上传路径  不推荐使用中文路径  且分隔符不能采用 \\ 因为 Linux中不支持
#music.local.path=F:/music/
# 上传服务器后,此处需要修改为服务器中的地址  注意最后一个 /
music.local.path=/root/savaonlinemusic/

#配置xml
mybatis.mapper-locations=classpath:mybatis/**Mapper.xml

# 配置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

#配置springboot上传文件的大小,默认每个文件的配置最大为15Mb,单次请求的文件的总数不能大于100Mb
spring.servlet.multipart.max-file-size = 15MB
spring.servlet.multipart.max-request-size=100MB



五、创建配置类

创建 tools 工具包,放置全局可以使用的配置类

  1. 设置统一返回格式
package com.example.onlinemusic.tools;

import lombok.Data;

@Data
public class ResponseBodyMessage<T> {
    private int status;//状态码
    private String message;//状态描述信息[出错的原因? 没出错的原因?]
    private T data;//返回的数据

    public ResponseBodyMessage(int status, String message, T data) {
        this.status = status;
        this.message = message;
        this.data = data;
    }
}

注意: 将返回类 ResponseBodyMessage 设置为泛型,方便返回数据类型的设置

  1. Constant类,储存复杂的不变常量

如设置了session对象,此时的key值是一个字符串,将来在其他地方获取对应的session需要通过这个字符串获取,但是存在一定的写错的情况。所以,此时建议把他定义为一个常量

public class Constant {
    public static final String USERINFO_SESSION_KEY = "USERINFO_SESSION_KEY";
}

六、具体功能实现

6.1 注册模块

  1. Controller 层实现
    @Autowired
    private UserService userService;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    //注册
    @RequestMapping("/reg")
    public ResponseBodyMessage<Boolean> reg(@RequestParam String username, @RequestParam String password) {
        User user1 = userService.selectByName(username);
        if (user1 != null) {
            return new ResponseBodyMessage<>(-1, "当前用户已存在!", false);
        } else {
            User user2 = new User();
            user2.setUsername(username);
            String password1 = bCryptPasswordEncoder.encode(password);
            user2.setPassword(password1);
            userService.addUser(user2);
            return new ResponseBodyMessage<>(1, "注册成功!", true);
        }
    }

注意: 用户存在就不能再次注册

  1. Mapper 层实现
public int addUser(User user);
  1. xml 文件实现
    <!-- 新增用户 -->
    <insert id="addUser">
        insert into user(username,password) values (#{username},#{password});
    </insert>
  1. 请求和响应设计

请求
POST /user/reg
{username: “”,password: “”}
响应
{
status: 1/-1,
message: “”,
data: “”
}

6.2 登录模块

  1. 创建User类
    在package com.example.musicserver.model包中创建User类
package com.example.onlinemusic.model;

import lombok.Data;

@Data
public class User {
    private int id;
    private String username;
    private String password;
}

  1. 创建对应的Mapper和Service

2.1新建mapper包,新建UserMapper

package com.example.onlinemusic.mapper;

import com.example.onlinemusic.model.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {
    public User login(User loginUser);
}

2.2创建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">
    <select id="login" resultType="com.example.onlinemusic.model.User">
        select * from user where username=#{username} and password=#{password}
    </select>
</mapper>

2.3创建Service类

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public User login(User userlogin){
        return userMapper.login(userlogin);
    }
 }
  1. 实现登录

3.1登录的请求和响应设计

请求
POST /user/login
{username: “”,password: “”}
响应
{
status: 0/-1,
message: “”,
data: “”
}

响应体设计字段解释:
{
status:状态码,为1代表成功,负数代表失败
message:状态描述信息,描述此次请求成功或者失败的原因
data:返回的数据,请求成功后,需要给前端的数据信息
}

3.2创建UserController类

    @Autowired
    private UserService userService;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;
    
    @RequestMapping("/login1")
    //将请求参数绑定到你控制器的方法参数上 。如果这个参数是非必传的可以写为:@RequestParam(required = false) ,默认是true
    public ResponseBodyMessage<Boolean> login1(@RequestParam String username, @RequestParam String password, HttpServletRequest request) {


        User user = userService.selectByName(username);

        if (user != null) {
            // bCryptPasswordEncoder 采用注入的方式
            boolean flg = bCryptPasswordEncoder.matches(password, user.getPassword());
            if (!flg) {
                return new ResponseBodyMessage<>(-1, "密码错误 !", false);
            } else {
                System.out.println("登录成功 !");
                // 登录成功,将用户信息写入Session中
                request.getSession().setAttribute(Constant.USERINFO_SESSION_KEY, user);
                return new ResponseBodyMessage<>(1, "登录成功 !", true);
            }

        } else {
            System.out.println("登录失败 !");
            return new ResponseBodyMessage<>(-1, "登录失败 !", false);
        }
    }

拓展:登录注册加密(MD5,BCrypt)

  1. MD5 加密
    MD5是一个安全的散列算法,输入两个不同的明文不会得到相同的输出值,根据输出值,不能得到原始的明文,即其过程不可逆; 但是虽然不可逆,但是不是说就是安全的。因为自从出现彩虹表后,这样的密码也"不安全"。

彩虹表:彩虹表就是一个庞大的、针对各种可能的字母组合预先计算好的哈希值的集合,不一定是针对MD5算法的,各种算法的都有,有了它可以快速的破解各类密码。越是复杂的密码,需要的彩虹表就越大,现在主流的彩虹表都是100G以上。

不安全的原因:

  1. 暴力攻击速度很快
  2. 字典表很大
  3. 碰撞

更安全的做法是加盐或者长密码等做法,盐是在每个密码中加入一些单词来变成一个新的密码,存入数据库当中,让整个加密的字符串变的更长,破解时间变慢。密码学的应用安全,是建立在破解所要付出的成本远超出能得到的利益上的 。

但是,因为这里没有用随机盐值,固定盐值使得每次加密的密码都是固定的,也存在暴力破解风险。更安全的是,当密码长度很大,盐值也是随机的情况下,密码的强度也加大了。破解成本也增加了,也就是下面介绍的 BCrypt

(重点参考)MD5详解:https://md5.cc/news1.aspx

  1. BCrypt 加密
    Bcrypt就是一款加密工具,可以比较方便地实现数据的加密工作。你也可以简单理解为它内部自己实现了随机加盐处理 。我们使用MD5加密,每次加密后的密文其实都是一样的,这样就方便了MD5通过大数据的方式进行破解。Bcrypt生成的密文是60位的。而MD5的是32位的。Bcrypt破解难度更大。

在我们此项目中也是使用了 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})

解释: 因为在SpringBoot中,默认的Spring Security生效了的,此时的接口都是被保护的,我们需要通过验证才能正常的访问。此时通过上述配置,即可禁用默认的登录验证该项目本身没有那么复杂,并没有用到 Spring Security 框架,只是用到了其中的 BCryptPasswordEncoder 类

最后就是登录注册中分别调用的 encode 和 matches 方法:

encode方法:对用户密码进行加密
matches方法:参数一,待检验的未加密的密码 。参数二:从数据库中查询出的加密后密码


总结:

  1. 密码学的应用安全,是建立在破解所要付出的成本远超出能得到的利益上的 。
  2. 使用BCrypt相比于MD5加密更好的一点在于,破解的难度上加大
  3. BCrypt的破解成本增加了,导致系统的运行成本也会大大的增加 。
  4. 回到本质的问题,你的数据库中的数据价值如何?如果你是银行类型的,那么使用BCrypt是不错的,一般情况使用MD5加盐,已经够用了。

BCrypt加密: 一种加盐的单向Hash,不可逆的加密算法,同一种明文(plaintext),每次加密后的密文都不一样,而且不可反向破解生成明文,破解难度很大。
MD5加密: 是不加盐的单向Hash,不可逆的加密算法,同一个密码经过hash的时候生成的是同一个hash值,在大多数的情况下,有些经过md5加密的方法将会被破解。

Bcrypt生成的密文是60位的。而MD5的是32位的。

目前,MD5和BCrypt比较流行。相对来说,BCrypt比MD5更安全,但加密更慢。 虽然BCrpyt也是输入的字符串+盐,但是与MD5+盐的主要区别是:每次加的盐不同,导致每次生成的结果也不相同。无法比对!


6.3 退出模块

所谓退出即将Session中的用户信息删除掉,这样只有再次登录才能进行后续页面访问

  1. Controller 层实现
 @RequestMapping("/logout")
    public ResponseBodyMessage<Boolean> logout(HttpServletRequest request, HttpServletResponse response) {
        //退出登录则清除session中的用户信息
        HttpSession session = request.getSession(false);
        if (session == null) {
            System.out.println("您还未登录!");
            return new ResponseBodyMessage<>(-1, "您还未登录!", false);
        } else {

            session.removeAttribute(Constant.USERINFO_SESSION_KEY);

            if (session.getAttribute(Constant.USERINFO_SESSION_KEY) == null) {
                System.out.println("清除session中的用户信息成功!");
//                try {
//                    response.sendRedirect("login.html");//此处要注意,只需要将后端数据传给前端,让前端来判断是否需要重定向即可
//                } catch (IOException e) {               //若后端也进行重定向,就可能出错 !!
//                    e.printStackTrace();
//                }
                return new ResponseBodyMessage<>(1, "退出成功!", true);
            }
            return new ResponseBodyMessage<>(-1, "退出失败!", false);
        }
    }
}
  1. 前端实现
    <script>
        function exit() {
            // 2.发送 ajax 请求到后端
            jQuery.ajax({
                url: "/user/logout",
                type: "POST",
                dataTye: "json",
                success: function (data) {
                    if (data.status == 1) {
                        alert("退出登录成功!");
                        window.location.href = "login.html";
                    } else {
                        alert("退出失败,请重试!");
                    }
                }
            });
        }

        $(function (){
            $("#logout").click(function (){
                exit();
            });
        });
    </script>

6.4 上传音乐模块

  1. 请求响应设计

请求:
{
post,
/music/upload
{singer,MultipartFile file},
}
响应:
{
“status”: 1,
“message”: “上传成功!”,
“data”: true
}

  1. 新建Music类:
package com.example.onlinemusic.model;

import lombok.Data;

@Data
public class Music {
    private int id;
    private String title;
    private String singer;
    private String url;
    private String time;
    private int userid;
}

  1. 创建MusicController类
    上传本地成功后,还要实现数据库上传,需要先进行数据库的查询,然后插入数据库
@RestController
@RequestMapping("/music")
public class MusicController {

    @Value("${music.local.path}")
    private String Save_path;

    @Autowired
    private UserService userService;

    @RequestMapping("/upload")
    // MultipartFile是Spring框架中处理文件上传的主要类
    public ResponseBodyMessage<Boolean> insertMusic(@RequestParam String singer, @RequestParam("filename") MultipartFile file,
                                                    HttpServletRequest request, HttpServletResponse response) {
        // 没有获取到 Session 不会创建
        HttpSession httpSession = request.getSession(false);
        // 1.验证是否登录,登录成功后才允许上传
        if (httpSession == null || httpSession.getAttribute(Constant.USERINFO_SESSION_KEY) == null) {
            System.out.println("没有登录!");
            return new ResponseBodyMessage<>(-1, "没有登陆,请登录后上传 !", false);
        } else {
            // 获取音乐文件名称及类型 如 xxx.mp3
            String filenameAndType = file.getOriginalFilename();
            System.out.println("filenameAndType--->>>>>>>>>>>>>>>>>" + filenameAndType);

            // 设置文件保存路径 Save_path 采取从配置文件中读取的方式
            String path = Save_path + filenameAndType;

            //2.上传服务器  上传音乐到指定的文件目录
            // 创建文件,该文件路径为 path
            File dest = new File(path);
            if (!dest.exists()) {
                dest.mkdirs();
            }
            try {
                // 将上传的文件保存在 dest 目录下
                file.transferTo(dest);
                //return new ResponseBodyMessage<>(1, "服务器上传音乐成功 !", true);
            } catch (IOException e) {
                e.printStackTrace();
                return new ResponseBodyMessage<>(-1, "服务器上传音乐失败 !", false);
            }

            //3.上传数据库
            //3.1 先准备好歌曲的字段数据
            int LastPointIndex = filenameAndType.lastIndexOf(".");//返回上传文件名称最后一个点的下标
            //数据库当中存储的歌曲名称不包含.mp3. 所以需要进行截取
            String title = filenameAndType.substring(0, LastPointIndex);// substring 截取的是左闭右开区间,我们只需要从0下标到最后一个点的下标

            //SimpleDateFormat来格式化当前的系统时间
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            String time = simpleDateFormat.format(new Date());

            //这里的 url 会被用到播放音乐的模块
            String url = "/music/get?path=" + title;

            User user = (User) httpSession.getAttribute(Constant.USERINFO_SESSION_KEY);
            int userid = user.getId();

            // TODO:这里可以有一步:验证上传的文件在数据库中是否已经存在【判断title 和 singer 是否有和数据库中的数据一致的】
            // 此处遇到的问题:1.端口被进程占用,如何处理! 先在cmd中查看被那个进程占用,再用命令杀掉该进程
            //              2.mybatis xml文件中,查询语句的返回类型只能是对应的实体,不能是受影响的行数
            Music music = userService.selectinsertMusic(title, singer);

            if (music != null) {
                return new ResponseBodyMessage<>(-1, "该音乐已存在,上传数据库失败!", false);
            } else {
                //3.2 将数据插入数据库
                int ret = userService.insertMusic(title, singer, time, url, userid);

                if (ret == 1) {
                    //Ctrl+alt+T 快速生产 try catch 包裹
                    try {
                        // 当上传数据库成功后,会跳转到音乐列表界面
                        response.sendRedirect("/list.html");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    return new ResponseBodyMessage<>(1, "数据库上传音乐成功 !", true);
                } else {
                    // 如果上传数据库失败,就会把服务器中的文件删除掉 !
                    dest.delete();
                    return new ResponseBodyMessage<>(-1, "数据库上传音乐失败,已删除服务器上传的音乐!", false);
                }
            }
        }
    }
  1. 使用@Value(“${music.local.path}”),获取到配置文件当中的值。不建议中文路径。
  2. MultipartFile类,在org.springframework.web.multipart包当中,是Spring框架中处理文件上传的主要类
    主要方法介绍:
    【 在线音乐平台(onlinemusic) 】

MultipartFile详解:https://www.jianshu.com/p/e3d798c906cd


  1. 定义接口MusicMapper
@Mapper
public interface MusicMapper {
    //上传音乐
    public int insertMusic(String title, String singer, String time, String url, int userid);

    //上传音乐时,先查询数据库中是否已经存在该音乐
    public Music selectinsertMusic(String title, String singer);
 }
  1. xml 文件实现
    <insert id="insertMusic">
        insert into music(title, singer, time, url, userid)
        values (#{title}, #{singer}, #{time}, #{url}, #{userid})
    </insert>

    <select id="selectinsertMusic" resultType="com.example.onlinemusic.model.Music">
        select *
        from music
        where title = #{title}
          and singer = #{singer}
    </select>
知识拓展1:如何判断上传的文件是mp3

每个文件都存在其构成的方式【不能通过后缀名判断】

mp3文件格式:

【 在线音乐平台(onlinemusic) 】

由上图结构可知,每个Frame都由帧头和数据部分组成。具体参考以下链接:

1、https://blog.csdn.net/ffjffjffjffjffj/article/details/99691239
2、https://www.cnblogs.com/ranson7zop/p/7655474.html
3、https://blog.csdn.net/sunshine1314/article/details/2514322


知识拓展2:时间格式化的类:SimpleDateFormat
package com.example.onlinemusic.tools;

import java.text.SimpleDateFormat;
import java.util.Date;

public class SimpleDateFormatTest {
    public static void main(String[] args) {
        //获取当前年月日
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
        String time=sf.format(new Date());
        System.out.println(time);

        //获取当前年月日 和 时分秒
        SimpleDateFormat sf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time2=sf2.format(new Date());
        System.out.println(time2);
    }
}


6.5 播放音乐模块

  1. Controller 层实现
    //播放音乐:发送一个请求来获取音乐资源字节文件
    @RequestMapping("/get")
    // ResponseEntity对象是Spring对请求响应的封装。它继承了HttpEntity对象,包含了Http的响应码httpstatus)、响应头(header)、响应体(body)三个部分
    public ResponseEntity<byte[]> getMusic(String path) {
        File file = new File(Save_path + path);// 请求时注意要加上.mp3
        byte[] body = null;
        try {
            // Files.readAllBytes(String path) : 读取文件中的所有字节,读入内存 ,参数path是文件的路径
            body = Files.readAllBytes(file.toPath());
            if (body == null) {
                // 如果 body 此时还为null,说明没有读取到文件,即前端发送了一个错误的请求,没有找到对应的音乐文件
                return ResponseEntity.badRequest().build();// 错误请求,返回 400 状态码
            }
            return ResponseEntity.ok(body);//返回 200 状态码 以及对应的文件字节
        } catch (IOException e) {
            e.printStackTrace();
        }
        return ResponseEntity.badRequest().build();
    }

代码解释:

  1. Files.readAllBytes(String path) : 读取文件中的所有字节,读入内存 ,参数path是文件的路径
  2. ResponseEntity:ResponseEntity对象是Spring对请求响应的封装。它继承了HttpEntity对象,包含了Http的响应码(httpstatus)、响应头(header)、响应体(body)三个部分。ResponseEntity类继承自HttpEntity类,被用于Controller层方法 。ResponseEntity.ok 方法有2个方法,分别是有参数和没有参数

源码如下:

//这个方法若被调用的话,返回OK状态
public static ResponseEntity.BodyBuilder ok(){
return status(HttpStatus.OK);
} /
/这个方法若被调用的话,返回body内容和OK状态
public static <T> ResponseEntity<T> ok(T body) {
ResponseEntity.BodyBuilder builder = ok();
//ResponseEntity可以通过这个builder返回任意类型的body内容
return builder.body(body);
  1. 与API中的描述一致,无参ok方法返回OK状态,有参ok方法返回body内容和OK状态
  2. body类型 是 泛型T,也就是我们不确定body是什么类型,可以向ok方法传递任意类型的值
  3. 有参ok方法其实有调用无参ok方法

参考链接:

1、使用ResponseEntity处理API返回
2、https://www.jianshu.com/p/1238bfb29ee1
3、https://blog.csdn.net/qq_43317193/article/details/100109136

难点:添加前端开源播放器控件

1.码云地址: https://gitee.com/jackzhang1204/sewise-player
2.GitHub地址:https://github.com/jackzhang1204/sewise-player
3.音频播放示例:https://gitee.com/jackzhang1204/sewise-player/blob/master/demos/audio.html

将该开源项目,下载到本地,取出player文件夹,放入static文件夹下

前端嵌入播放器:

    <script type="text/javascript">
        SewisePlayer.setup({
            server: "vod",
            type: "mp3",
            //这里是默认的一个网址
            videourl: "http://jackzhang1204.github.io/materials/where_did_time_go.mp3",
            skin: "vodWhite",
            //这里需要设置false
            autostart: "false",
        });
    </script>

实现播放:

    function playerSong(obj) {
            console.log(obj)
            var name = obj.substring(obj.lastIndexOf('=') + 1);
            //obj:播放地址 name:歌曲或者视频名称 0:播放的时间  false:点击播放按钮时不开启自动播放
            SewisePlayer.toPlay(obj, name, 0, true);
        }

6.6 删除音乐模块

当删除主页面的音乐后,收藏列表对应的音乐也要被删除掉 !!

6.6.1 删除单个音乐

  1. 请求和响应设计

请求:
{
post,
/music/delete,
id
}
响应:
{
“status”: 1,
“message”: “删除成功!”,
“data”: true
}

  1. Service 层
    public Music selectMusicById(int id){
        return musicMapper.selectMusicById(id);
    }

    public int deleteMusicById(int id){
        return musicMapper.deleteMusicById(id);
    }
  1. Mapper 层
//根据id删除音乐,先查询判断数据库中是否存在该id的音乐
    public Music selectMusicById(@RequestParam Integer id);

    //如果查询到数据库中存在该id对应的音乐,即可以删除
    public int deleteMusicById(@RequestParam Integer id);
  1. xml 文件实现
    <select id="selectMusicById" resultType="com.example.onlinemusic.model.Music">
        select *
        from music
        where id = #{id}
    </select>

    <delete id="deleteMusicById" parameterType="java.lang.Integer">
        delete
        from music
        where id = #{id}
    </delete>
  1. Controller 层实现
//根据单个id 删除单个音乐
    //后期优化:根据id删除可能会出现数据库删除成功,服务器删除成功!但是此时数据库另一个id对应的音乐也是删除的这个音乐(音乐名字相同,歌手不同),
    //这时该idList在数据库中的音乐就没有删除,但是在服务器上已经删除了该音乐
    @RequestMapping("/delete")
    public ResponseBodyMessage<Boolean> deleteMusicByid(String id) {
        int musicId = Integer.parseInt(id);// PS:如果参数直接是int类型,就会报错,是String类型,将其转换为int 就不会报错 原因不知
        //1.先查询该数据库中该idList是否存在音乐
        Music music = userService.selectMusicById(musicId);
        if (music == null) {
            return new ResponseBodyMessage<>(-1, "数据库中没有你想删除的音乐", false);
        } else {
            //2.删除数据库中的音乐
            int ret = userService.deleteMusicById(musicId);
            if (ret == 1) {
                //3.数据库中删除成功,还要删除服务器中的音乐
                String title = music.getTitle();
                File file = new File(Save_path + title + ".mp3");
                System.out.println("此时文件的路径:" + file.getPath());
                Boolean flg = file.delete();
                if (flg) {
                    //数据库和服务器中的音乐数据被删除后,收藏表中对应的音乐也应被删除
                    userService.deleteLoveMusicById(musicId);
                    return new ResponseBodyMessage<>(1, "数据库删除成功且服务器删除成功且删除收藏表对应的音乐 !", true);
                } else {
                    return new ResponseBodyMessage<>(-1, "数据库删除成功但是服务器删除失败 !", false);
                }
            } else {
                return new ResponseBodyMessage<>(-1, "数据库删除失败 !", false);
            }
        }
    }

6.6.2 批量删除选中音乐

  1. 请求和响应设计

请求:
{
post,
/music/deletesel,
data:{“id”:id}
}
响应:
{
“status”: 1,
“message”: “批量删除成功”,
“data”: true
}

  1. Controller 层实现
//根据id批量删除  与单个删除逻辑一样,只是需要循环遍历集合来一个一个删除
    @RequestMapping("/deletebatch")
    public ResponseBodyMessage<Boolean> deleteBatchMusic(@RequestParam(value = "id[]") List<Integer> idList) {

        int sum = 0;
        for (int i = 0; i < idList.size(); i++) {
            Music music = userService.selectMusicById(idList.get(i));
            if (music == null) {
                return new ResponseBodyMessage<>(-1, "数据库中没有你想删除的音乐", false);
            } else {
                //2.删除数据库中的音乐
                int ret = userService.deleteMusicById(idList.get(i));
                if (ret == 1) {
                    //3.数据库中删除成功,还要删除服务器中的音乐
                    String title = music.getTitle();
                    File file = new File(Save_path + title + ".mp3");
                    System.out.println("此时文件的路径:" + file.getPath());
                    Boolean flg = file.delete();
                    if (flg) {
                        //数据库和服务器中的音乐数据被删除后,收藏表中对应的音乐也应被删除
                        userService.deleteLoveMusicById(idList.get(i));
                        sum += ret;
                        // 注意:下面语句放开注释,整个功能逻辑就会出错 !!  花了半小时才找出来0.0
                        //return new ResponseBodyMessage<>(1, "数据库删除成功且服务器删除成功 !", true);
                    } else {
                        return new ResponseBodyMessage<>(-1, "数据库删除成功但是服务器删除失败 !", false);
                    }
                } else {
                    return new ResponseBodyMessage<>(-1, "数据库删除失败 !", false);
                }
            }
        }
        if (sum == idList.size()) {
            return new ResponseBodyMessage<>(1, "批量删除音乐成功 !", true);
        } else {
            return new ResponseBodyMessage<>(-1, "批量删除音乐失败 !", false);
        }
    }

6.7 查询音乐模块

当输入参数时进行模糊查询,参数为空时进行全部查询

  1. 请求和响应设计

请求:
{
get,
/music/findmusic,
data:{musicName:musicName},
}
响应:【不给musicName传参】
{
“status”: 1/-1,
“message”: “”,
“data”: “”
}
响应:【给musicName传参】
{
“status”: 1/-1,
“message”: “”,
“data”: “”
}

  1. Service 层实现
    public List<Music> findMusicByMusicName(String name){
        return musicMapper.findMusicByMusicName(name);
    }

    public List<Music> findMusic(){
        return musicMapper.findMusic();
    }
  1. Mapper 层实现
 // 根据歌曲名字,查询音乐
    List<Music> findMusicByMusicName(String name);

    // 当参数为空时,查询所有音乐
    List<Music> findMusic();
  1. xml 文件实现
    <select id="findMusicByMusicName" resultType="com.example.onlinemusic.model.Music">
        select * from music where title like concat('%',#{musicName},'%')
    </select>

    <select id="findMusic" resultType="com.example.onlinemusic.model.Music">
        select * from music
    </select>

注意: 进行模糊查询时,使用like语句的写法

  1. Controller 层实现
    // 查询音乐
    @RequestMapping("/findmusic")
    //(required=false)可以不传入参数,默认为 true,不传入参数就会报错 !!                    //该参数要和前端参数对应
    public ResponseBodyMessage<List<Music>> findMusic(@RequestParam(required = false) String musicName) {
        List<Music> musicList = null;
        if (musicName != null) {
            //不为空就进行模糊查询
            musicList = userService.findMusicByMusicName(musicName);
        } else {
            //参数为空就查询全部的音乐
            musicList = userService.findMusic();
        }
        return new ResponseBodyMessage<>(1, "查询到了歌曲信息", musicList);
    }

6.8 添加音乐至喜欢的列表模块

  1. 请求和响应设计

请求:
{
post,
/lovemusic/insertlikemusic
data: id//音乐id
}
响应:
{
“status”: 1,
“message”: “点赞音乐成功”,
“data”: true
}

  1. LoveMusicMapper接口
    1、需要查询此次收藏音乐是否之前收藏过,收藏过则不能添加
    2、没有收藏过,插入数据库中一条记录
@Mapper
public interface LoveMusicMapper {

    //1.收藏音乐之前先查看该音乐是否已经收藏过了
    public Music findLoveMusicByMusicIdAndUserId(@RequestParam  Integer userId, @RequestParam Integer musicId);

    //2.没有收藏该音乐的话就可以插入该音乐到收藏表中
    public Boolean insertLoveMusic(@RequestParam Integer userId, @RequestParam Integer musicId);
 }
  1. xml 文件实现
    <select id="findLoveMusicByMusicIdAndUserId" resultType="com.example.onlinemusic.model.Music">
        select *
        from lovemusic
        where user_id = #{userId}
          and music_id = #{musicId}
    </select>

    <!-- 下面的返回类型不能是 Boolean -->
    <insert id="insertLoveMusic">
        insert into lovemusic(user_id, music_id)
        values (#{userId}, #{musicId})
    </insert>
  1. Controller 层实现
@RestController
@RequestMapping("/lovemusic")
public class LoveMusicController {

    @Resource
    private UserService userService;

    @RequestMapping("/insertlikemusic")
    public ResponseBodyMessage<Boolean> LoveMusic(@RequestParam String id, HttpServletRequest request) {
        int musicId = Integer.parseInt(id);

        HttpSession httpSession = request.getSession(false);
        if (httpSession == null || httpSession.getAttribute(Constant.USERINFO_SESSION_KEY) == null) {
            return new ResponseBodyMessage<>(-1, "您还没有登录 !", false);
        } else {
            User user = (User) httpSession.getAttribute(Constant.USERINFO_SESSION_KEY);
            int userId = user.getId();

            //TODO:还可以扩展,该音乐是否已经上传,只有上传成功的音乐才能收藏

            //先查询该音乐是否已经被收藏
            Music music = userService.findLoveMusicByMusicIdAndUserId(userId, musicId);
            if (music != null) {
                System.out.println("该音乐已经被收藏 !");
                return new ResponseBodyMessage<>(-1, "该音乐已经被收藏,重复收藏失败 !", false);
            } else {
                //没有收藏过就可以进行收藏操作
                Boolean flg = userService.insertLoveMusic(userId, musicId);
                if (flg) {
                    return new ResponseBodyMessage<>(1, "收藏音乐成功 !", true);
                } else {
                    return new ResponseBodyMessage<>(-1, "收藏音乐失败 !", false);
                }
            }
        }
    }

6.9 查询收藏的音乐模块

功能实现和之前在主页面的查询一样 !! 注意细节修改

  1. 请求和响应设计

请求:
{
get,
/lovemusic/findlovemusic,
data:{musicName:musicName},
}
响应:【不给musicName传参】
{
“status”: 1/-1,
“message”: “”,
“data”: “”
}
响应:【给musicName传参】
{
“status”: 1/-1,
“message”: “”,
“data”: “”
}

  1. Mapper 层实现
    //查询用户收藏的音乐:如果没有传入具体的歌曲名,显示当前用户收藏的所有音乐
    public List<Music> findLoveMusicByUserId(int userId);

    //根据某个用户的ID和歌曲名称查询某个用户收藏的音乐,支持模糊查询
    public List<Music> findLoveMusicBykeyAndUID(String musicName, int userId);
  1. xml 文件实现(重要)
    <select id="findLoveMusicByUserId" resultType="com.example.onlinemusic.model.Music">
        select m.*
        from lovemusic lm,
             music m
        where m.id = lm.music_id
          and lm.user_id = #{userId}
    </select>

    <select id="findLoveMusicBykeyAndUID" resultType="com.example.onlinemusic.model.Music">
        select m.*
        from lovemusic lm,
             music m
        where m.id = lm.music_id
          and lm.user_id = #{userId}
          and title like concat('%', #{musicName}, '%')
    </select>
  1. Controller 层
    这里就不重复赘述了,大家参考前面 !!

6.10 删除收藏的音乐模块

  1. 请求和响应设计

请求:
{
post,
/lovemusic/deletelovemusic,
data:{id:id}
}
响应:
{
“status”: 1,
“message”: “取消收藏成功!”,
“data”: true
}

  1. Service 层
    将上述 Service 层代码补齐
    public Music findLoveMusicByMusicIdAndUserId(Integer userId,Integer musicId){
        return loveMusicMapper.findLoveMusicByMusicIdAndUserId(userId,musicId);
    }

    public Boolean insertLoveMusic(Integer userId,Integer musicId){
        return loveMusicMapper.insertLoveMusic(userId,musicId);
    }

    public List<Music> findLoveMusicByUserId(int userId){
        return loveMusicMapper.findLoveMusicByUserId(userId);
    }

    public List<Music> findLoveMusicBykeyAndUID(String musicName, int userId){
        return loveMusicMapper.findLoveMusicBykeyAndUID(musicName,userId);
    }

    public int deleteLoveMusic(int userId,int musicId){
        return loveMusicMapper.deleteLoveMusic(userId,musicId);
    }

    public int deleteLoveMusicById(int musicId){
        return loveMusicMapper.deleteLoveMusicById(musicId);
    }
  1. Mapper 层
    //移除自己收藏的音乐,但是不是删除音乐本身,只是从数据库中删除了记录而已
    public int deleteLoveMusic(int userId,int musicId);

    //删除音乐完善:当删除库中的音乐的时候,同步删除lovemusic中的数据
    public int deleteLoveMusicById(int musicId);
  1. xml 文件实现
    <delete id="deleteLoveMusic" parameterType="java.lang.Integer">
        delete from lovemusic where user_id = #{userId} and music_id = #{musicId}
    </delete>

    <delete id="deleteLoveMusicById" parameterType="java.lang.Integer">
        delete from lovemusic where music_id=#{musicId}
    </delete>

七、配置拦截器

  1. 新建 LoginInterceptor ,实现HandlerInterceptor 接口
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 {
        // 没有获取到 Session 不会创建
        HttpSession httpSession = request.getSession(false);
        // 1.验证是否登录,登录成功后才允许上传
        if (httpSession == null || httpSession.getAttribute(Constant.USERINFO_SESSION_KEY) == null) {
            System.out.println("没有登录!");
            response.sendRedirect("/login.html");
            return false;
        }
        return true;
    }
}

  1. 将拦截器注册到系统配置文件,并设置拦截规则
package com.example.onlinemusic.config;

import org.springframework.beans.factory.annotation.Autowired;
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();
    }

    @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("/reg.html")
                .excludePathPatterns("/login.html")
                //排除登录注册接口
                .excludePathPatterns("/user/login1")
                .excludePathPatterns("/user/reg");//注意里面的格式 / 不要掉

    }
}


八、测试用例(了解即可)

【 在线音乐平台(onlinemusic) 】

九、项目部署

前提:拥有一台服务器,在服务器上安装必须的文件如,JDK,Mysql 等

  1. 将数据库在服务器上重新进行建表等操作,相应的数据也需要重新获取
    【 在线音乐平台(onlinemusic) 】

  2. 修改项目中的路径,数据库密码等,匹配服务器即可
    【 在线音乐平台(onlinemusic) 】

  3. 将打包好的jar包,通过xftp 上传到服务器上相应的文件目录下
    【 在线音乐平台(onlinemusic) 】

  4. 使用java -jar xxxx.jar 启动项目【前台运行的方式】,再通过自己的外网IP访问

  5. 后台运行springboot项目:nohup java -jar xxx.jar >> log.log &

nohup:后台运行项目的指令
使用 >> log.log 将运行的日志记录到 log.log中,PS: log.log前缀名可以随意改变
& 表示 一直运行

  1. 进行相应的功能检查,并修改
  2. springboot项目更新

1.先通过 ps -ef | grep java 查询当前的springboot项目的进程,然后 kill 【进程ID】kill掉
命令说明:
ps : Linux 当中查看进程的命令
-e 代表显示所有的进程
-f 代表全格式【显示全部的信息】
grep : 全局正则表达式
2.将原服务器上的jar包删除掉,重新上传新的jar包
3.重新进行后台的启动文章来源地址https://www.toymoban.com/news/detail-438771.html


十、项目拓展方向

  1. 检测文件是否为mp3文件
  2. 实现对音乐的评论,对评论的点赞和回复功能
  3. 新增MV功能
  4. 新增创建歌单的功能
  5. 自己实现一个流媒体服务器,使用流媒体协议对歌曲进行播放

到了这里,关于【 在线音乐平台(onlinemusic) 】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 网易音乐网站系统|前后端分离springboot+vue实现在线音乐网站

    作者主页:编程千纸鹤 作者简介:Java、前端、Python开发多年,做过高程,项目经理,架构师 主要内容:Java项目开发、毕业设计开发、面试技术整理、最新技术分享 收藏点赞不迷路  关注作者有好处 文末获得源码 项目编号:BS-PT-104 网易音乐网站系统是一个在线的音乐网站

    2024年02月05日
    浏览(40)
  • 【基于Qt的在线音乐播放器】

    本在线音乐播放器的功能在于创建一个音乐播放器页面,可以实现搜索功能通过HTTP协议获取网络中数据并解析出来,播放搜索到的歌曲并展示相关信息。效果如图: Musicinterface 视图类; 主要功能:初始化窗口;设置按钮功能和窗口内容的显示; HttpHandle 网络连接类; 主要功

    2024年02月03日
    浏览(39)
  • flask搭建在线音乐网系统

    1.使用虚拟环境Virtualenv来创建项目  2.  Flask框架介绍 Flask框架是一个用 Python编写的轻量级Web应用程序框架 ,依赖于Werkzeug和Jinja2两个外部库。Werkzeug是一个WSGI工具包,用于接收和处理HTTP请求,匹配视图函数,支持Cookie和会话管理,交互式调试等功能。Jinja2是一个模板引擎,

    2024年02月09日
    浏览(36)
  • 留用户、补内容,在线音乐暗战不停

    在线音乐在人们的日常生活中扮演着愈发重要的角色,尤其是在面临巨大压力时,人们往往更倾向于通过倾听一段音乐来缓解内心的紧张与焦虑。而随着在线音乐用户数量的增长以及付费意愿的增强,在线音乐行业也实现了稳步发展。 经过多年的发展,在线音乐行业已基本形

    2023年04月16日
    浏览(28)
  • 基于python的在线音乐系统设计与实现【附源码】

    完整的看企鹅:2-3-8-6-7-0-4-0-3-0 Django音乐系统 本音乐系统借助了当前互联网的发展趋势,近几年,随着网络的快速发展,网络已经融入人们的生活中。互联网给人们的生活带来了许多便利,基本上可以达到足不出户就能完成许多事情。互联网的使用基本实现全覆盖,上至老人

    2024年02月03日
    浏览(36)
  • ssm042在线云音乐系统的设计与实现+jsp

    随着移动互联网时代的发展,网络的使用越来越普及,用户在获取和存储信息方面也会有激动人心的时刻。音乐也将慢慢融入人们的生活中。影响和改变我们的生活。随着当今各种流行音乐的流行,人们在日常生活中经常会用到的就是在线云音乐系统。 本文首先分析了基于

    2024年04月15日
    浏览(40)
  • 计算机毕业设计----SSH实现简单在线听音乐收藏管理系统

    项目介绍 项目分为管理员与普通用户两种角色, 管理员角色包含以下功能: 管理员登录,用户管理,歌曲管理等功能。 用户角色包含以下功能: 按分类查看,添加歌单,用户登录等功能。 环境需要 1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以

    2024年02月01日
    浏览(46)
  • 基于网易云音乐API的微信小程序——zwhdlb的音乐平台

    最近在学习小程序的开发的过程中,临时想写一个音乐小程序,看到了网易云 提供了后台api程序,这方便我们直接进行音乐小程序的开发不用再从后端开始开发,网易云音乐平时也经常在用,因此想记录一下学习过程 开发工具:微信开发者工具 界面UI组件库用到的是ColorUI

    2023年04月27日
    浏览(46)
  • 【开源】基于Vue+SpringBoot的音乐平台

    项目编号: S 055 ,文末获取源码。 color{red}{项目编号:S055,文末获取源码。} 项目编号: S 055 ,文末获取源码。 基于微信小程序+JAVA+Vue+SpringBoot+MySQL的音乐平台,包含了音乐档案模块、音乐收藏模块、音乐订单模块,支持PC后台和微信小程序用户端使用,还包含系统自带的

    2024年02月04日
    浏览(40)
  • 基于java,springboot的音乐分享平台

    音乐网站与分享平台的主要使用者分为管理员和用户,实现功能包括管理员:首页、个人中心、用户管理、音乐资讯管理、音乐翻唱管理、在线听歌管理、留言板管理、系统管理,用户:首页、个人中心、音乐翻唱管理、我的收藏管理,前台首页;首页、音乐资讯、音乐翻唱

    2024年02月10日
    浏览(69)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包