项目:在线音乐播放服务器——基于SSM框架和mybatis
介绍项目
项目主要是基于SSM框架和mybatis进行实现
主要的功能:
登陆界面,用户注册,音乐的播放列表,删除指定的歌曲,批量删除指定的歌曲,收藏歌曲,查询歌曲,从收藏列表中删除收藏音乐。
功能展示:
登录界面
用户注册
音乐列表
喜欢列表
添加歌曲
首先创建一个Sping Boot项目
再创建musicserver数据库
数据库一共有三个表:收藏歌曲表,歌曲表,用户表
lovemusic表
music表
user表
在.yml文件中配置数据库和xml
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/musicserver?characterEncoding=utf8&serverTimezone=UTCusername: 你自己的数据库用户名password: 你自己的数据库密码driver-class-name: com.mysql.cj.jdbc.Driverservlet:multipart:max-file-size: 15MBmax-request-size: 100MB
#音乐上传后的路径music:local:path: E:/music/mybatis:configuration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImplmapper-locations: classpath:mapper/**.xml
logging:file:name: spring-book.log
创建项目的结构目录
登录模块的设计
创建user类
对应数据库的user表,创建用户的实体类
@Data
public class User {private Integer id;private String username;private String password;
}
创建UserMapper接口
@Mapper
public interface UserMapper {User login(User loginUser);User selectByName(String username);
}
创建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.blame.onlinemusicserver.mapper.UserMapper"><select id="login" resultType="com.blame.onlinemusicserver.model.User" parameterType="com.blame.onlinemusicserver.model.User">select * from user where username=#{username} and password=#{password}</select><select id="selectByName" resultType="com.blame.onlinemusicserver.model.User">select * from user where username=#{username}</select></mapper>
实现登录的请求和响应
创建响应类的工具类
@Data
public class ResponseBodyMessage<T> {private Integer status;private String message;private T data;public ResponseBodyMessage(Integer status, String message, T data) {this.status = status;this.message = message;this.data = data;}
}
创建UserController
@RequestParam:将请求参数绑定到你控制器的⽅法参数上,如果你这个参数不是必须要传的,@RequestParam(required = false) ,默认是true
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/login")public ResponseBodyMessage<User> login(@RequestParam("username") String username,@RequestParam("password") String password,HttpServletRequest request){ResponseBodyMessage<User> result = userService.login(username, password, request);return result;}
}
我们在登录时使用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>
在项目的启动类中添加@SpringBootApplication(exclude =
{org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
//在@SpringBootApplication注解后添加(exclude =
//{org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})的作用:
//我们虽然引用了 security的依赖,但是我们只是使用了它框架中的一个类,不适用其他的,如果不添加这个的话,SpringSecurity⽣效了的,
//此时的接⼝都是被保护的,我们需要通过验证才能正常的访问,导致我们无法登录
@SpringBootApplication(exclude ={org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
public class OnlineMusicServerApplication {public static void main(String[] args) {SpringApplication.run(OnlineMusicServerApplication.class, args);}}
创建AppConfig
定义config包,创建AppConfig,使用@Configuration注解
@Configuration
public class AppConfig implements WebMvcConfigurer {@Beanpublic BCryptPasswordEncoder getbCryptPasswordEncoder(){return new BCryptPasswordEncoder();}
}
创建UserService
request.getSession(),获取这次请求的session对象
.setAttribute()将用户信息存储到session中
因为 USERINFO_SESSION_KEY 这个常量容易拼错,所以我们将其定义在常量信息中
@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate BCryptPasswordEncoder bCryptPasswordEncoder;public ResponseBodyMessage<User> login(String username,String password,HttpServletRequest request) {User loginUser=new User();loginUser.setUsername(username);loginUser.setPassword(password);User user=userMapper.selectByName(username);if(user==null){return new ResponseBodyMessage<>(-1,"用户不存在",loginUser);}if(!bCryptPasswordEncoder.matches(password,user.getPassword())){return new ResponseBodyMessage<>(-1,"密码错误,请重新登录",loginUser);}else {request.getSession().setAttribute(Constant.USERINFO_SESSION_KEY,user);return new ResponseBodyMessage<>(0,"登陆成功",loginUser);}}
}
public class Constant {public static final String USERINFO_SESSION_KEY="USERINFO_SESSION_KEY";
}
验证
新增功能:注册用户功能的实现
修改数据库,在user表中添加email
ALTER TABLE user
ADD COLUMN email VARCHAR(100) NOT NULL;
UserMapper
void insertUser(User user);
<insert id="insertUser">insert into user (username, password, email) VALUES (#{username}, #{password}, #{email})</insert>
UserService
public ResponseBodyMessage<Boolean> register(String username,String password,String email){User existUser=userMapper.selectByName(username);if(existUser!=null){return new ResponseBodyMessage<>(-1,"该用户存在",false);}
// 将注册的密码进行加密String encoderPassword=bCryptPasswordEncoder.encode(password);User user=new User();user.setUsername(username);user.setPassword(encoderPassword);user.setEmail(email);userMapper.insertUser(user);return new ResponseBodyMessage<>(0,"注册成功",true);
}
UserController
使用@Valid来校验参数
使用@Valid记得添加其依赖
<dependency><groupId>jakarta.validation</groupId><artifactId>jakarta.validation-api</artifactId><version>3.0.2</version></dependency><dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId><version>8.0.0.Final</version></dependency>
定义RequestRegister来进行请求数据的封装
@Data
public class RequestRegister {@Size(min = 3, max = 20, message = "用户名长度应在3到20个字符之间")@NotBlank(message = "username不能为空")private String username;@NotBlank(message = "password不能为空")private String password;@Email(message = "邮箱格式不正确")@NotBlank(message = "email不能为空")private String email;
}
@RequestMapping("/register")public ResponseBodyMessage<Boolean> register(@Valid @RequestBody RequestRegister requestRegister){return userService.register(requestRegister.getUsername(), requestRegister.getPassword(), requestRegister.getEmail());}
}
上传音乐模块的实现
请求和响应的接口实现
定义music实体类
对应数据库中的music表的字段
@Data
public class Music {private Integer id;private String title;private String singer;private String time;private String url;private Integer userid;}
定义MusicController
将音乐文件上传到这个路径
Slf4j
@RestController
@RequestMapping("/music")
public class MusicController {@Autowiredprivate MusicService musicService;@RequestMapping("/upload")public ResponseBodyMessage<Boolean> insertMusic(@RequestParam String singer,@RequestParam("filename") MultipartFile file,HttpServletRequest request,HttpServletResponse response){return musicService.insertMusic(singer, file, request, response);}
}
定义MusicService
@Slf4j
@Service
public class MusicService {@Value("${spring.music.local.path}")private String SAVE_PATH;@Autowiredprivate MusicMapper mapper;@Autowiredprivate LoveMusicMapper loveMusicMapper;//添加音乐public ResponseBodyMessage<Boolean> insertMusic(String singer,MultipartFile file,HttpServletRequest request,HttpServletResponse response){//1.检查登录HttpSession session=request.getSession(false);if(session==null || session.getAttribute(Constant.USERINFO_SESSION_KEY)==null){log.error("未登录,请先进行登录");return new ResponseBodyMessage<>(0,"没有登录",false);}//2.检查数数据库中是否有此音乐//得到文件的名字和类型String fileNameAndType=file.getOriginalFilename();System.out.println("fileNameAndType"+fileNameAndType);//以 . 将名字和类型分开,得到titleif(fileNameAndType==null || !fileNameAndType.contains(".")){return new ResponseBodyMessage<>(0,"文件不合法,必须包含拓展名",false);}String title=fileNameAndType.substring(0,fileNameAndType.lastIndexOf("."));log.info("Checking if music exists: title = {}, singer = {}", title, singer);//判断数据库中是否有此歌曲Integer exist= mapper.selectByTitleAndSinger(title,singer);if(exist!=null && exist>0){return new ResponseBodyMessage<>(0,"数据库中已经存在此音乐",false);}//3..上传音乐到服务器String path=SAVE_PATH + fileNameAndType;File dest=new File(path);System.out.println("dest"+dest.getPath());if(!dest.exists()){dest.mkdirs();}//上传文件到目标try {file.transferTo(dest);log.info("服务器上传成功");} catch (IOException e) {throw new RuntimeException(e);}//将歌曲上传到数据库SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd");String time=sf.format(new Date());String url="music/get?path="+title;User user=(User) session.getAttribute(Constant.USERINFO_SESSION_KEY);int userId=user.getId();try {int ret= mapper.insert(title,singer,time,url,userId);if(ret==1){response.sendRedirect("/list.html");return new ResponseBodyMessage<>(1,"数据库上传成功",true);}else {return new ResponseBodyMessage<>(-1,"数据库上传失败",false);}}catch (BindingException e){boolean delete= dest.delete();if(delete){log.info("数据库插入失败,已成功删除服务器上的音乐文件");}else{log.warn("数据库插入失败,未删除服务器上的音乐文件,可能需要手动删除:{}",dest.getAbsolutePath());}return new ResponseBodyMessage<>(-1,"数据库上传失败,在服务器中删除音乐",false);} catch (IOException e) {throw new RuntimeException(e);}}
新增方法:判断上传的文件是否是MP3的形式
我们需要对上传的文件进行MP3文件类型检测,包括文件扩展名和MIME类型的两种检查
文件拓展名是文件名称的一部分,它可以轻易被修改,从而伪装成另一种文件
MIME类型是一种表示文件类型和格式的标准,比拓展名更加可靠
//根据文件的拓展名来判断是否为.MP3文件String extension=fileNameAndType.substring(fileNameAndType.lastIndexOf(".")).toLowerCase();if(!extension.equals(".mp3")){return new ResponseBodyMessage<>(-0,"只支持MP3文件上传",false);}//根据MINE判断try {String mimeType=file.getContentType();log.info("文件的MINI类型:{}",mimeType);if(mimeType==null || !mimeType.equals("audio/mpeg")){return new ResponseBodyMessage<>(0,"文件格式不正确,只支持MP3音频上传",false);}}catch (Exception e){log.error("文件检验异常:{}",e.getMessage());return new ResponseBodyMessage<>(0,"文件类型检验异常",false);}
上传数据库的实现
@Mapper
public interface MusicMapper {Integer insert(String title,String singer,String time,String url,Integer userid);
}
<?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.blame.onlinemusicserver.mapper.MusicMapper"><insert id="insert">insert into music (title,singer,time,url,userid) values (#{title},#{singer},#{time},#{url},#{userid})</insert>
测试
播放⾳乐模块设计
请求和响应的接口实现
在MusicController中新增get方法
是Spring框架中用于构建Http响应的对象
这里返回的是一个字节数组,通常用于传输二进制的数据,图片,音频
我们在这时传入的是音频
@GetMapping("/get")public ResponseEntity<byte[]> get(@RequestParam String path){return musicService.get(path);}
MsuicService
ResponseEntity对象是Spring对请求响应的封装。它继承了HttpEntity对象,包含了Http的响应码 (httpstatus)、响应头(header)、响应体(body)三个部分。
//播放音乐public ResponseEntity<byte[]> get(String path){File file=new File(SAVE_PATH+path);if(!file.exists()){return ResponseEntity.notFound().build();}try {byte[] bytes= Files.readAllBytes(file.toPath());HttpHeaders headers=new HttpHeaders();headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);headers.setContentLength(bytes.length);return ResponseEntity.ok().headers(headers).body(bytes);} catch (IOException e) {e.printStackTrace();return ResponseEntity.internalServerError().build();}}
验证
删除⾳乐模块设计
请求和响应的接口实现
实现MusicMapper
Integer deleteMusicById(Integer musicId);Music selectMusicById(Integer musicId);
<delete id="deleteMusicById">delete from music where id=#{id}</delete><select id="selectMusicById" resultType="com.blame.onlinemusicserver.model.Music">select * from music where id=#{id}</select>
实现MusicController
根据Id来进行删除
@RequestMapping("/delete")public ResponseBodyMessage<Boolean> deleteMusicById(@RequestParam Integer id){return musicService.deleteMusicById(id);}
实现MusicService
如果要将一个歌曲删除,需要判断它是否在收藏歌曲中,如果在,则需要将其从收藏音乐中删除
Boolean deleteLoveMusicByMusicId(Integer musicId);Boolean selectLoveMusicByMusicId(Integer musicId);
判断删除是否成功
//删除音乐public ResponseBodyMessage<Boolean> deleteMusicById(Integer id){Music music=mapper.selectMusicById(id);if(music==null){return new ResponseBodyMessage<>(0,"该音乐不存在,无法删除",false);}//此时integer表示删除的行数,如果行数又0变为1则表示已经进行了删除的步骤Integer integer = mapper.deleteMusicById(id);Boolean isLoved = loveMusicMapper.selectLoveMusicByMusicId(id);if (isLoved != null && isLoved) {// 如果在收藏中,删除收藏记录loveMusicMapper.deleteLoveMusicByMusicId(id);} else {log.info("歌曲不在收藏音乐中");}if(integer==1){String title=music.getTitle();File file=new File(SAVE_PATH+File.separator+ title+".mp3");boolean delete=file.delete();if(delete){log.info("服务器中音乐删除成功");return new ResponseBodyMessage<>(1,"服务器中音乐删除成功",true);}else {log.error("服务器中音乐删除失败");return new ResponseBodyMessage<>(-1,"服务器中音乐删除失败",false);}}return new ResponseBodyMessage<>(-1,"音乐删除失败",false);}
验证
批量删除选中的⾳乐
请求和响应的接口实现
实现MusicController
只需要先刚刚单首歌曲删除的基础上,在外层套一层for循环,依次进行删除,知道for循环结束,删除全部的歌曲
public ResponseBodyMessage<Boolean> deleteMusicByIdMany(List<Integer> id){System.out.println("所有的Id: " + id);int sum=0;for (int i = 0; i < id.size(); i++) {int musicId=id.get(i);Music music=mapper.selectMusicById(musicId);Boolean isLoved = loveMusicMapper.selectLoveMusicByMusicId(musicId);if (isLoved != null && isLoved) {// 如果在收藏中,删除收藏记录loveMusicMapper.deleteLoveMusicByMusicId(musicId);} else {log.info("歌曲不在收藏音乐中");}System.out.println("当前正在处理的 ID: " + id);if(music==null){log.error("该歌曲不存在");continue;}//此时integer表示删除的行数,如果行数又0变为1则表示已经进行了删除的步骤Integer integer = mapper.deleteMusicById(musicId);if(integer==1){String title=music.getTitle();File file=new File(SAVE_PATH+File.separator+ title+".mp3");boolean delete=file.delete();if(delete){sum+=integer;log.info("服务器中音乐删除成功");}else {log.error("服务器中音乐删除失败");}}else {log.error("数据库删除失败");}}if(sum==id.size()){log.info("批量删除成功");return new ResponseBodyMessage<>(1,"服务器中音乐批量删除成功",true);}else {log.info("批量删除失败");return new ResponseBodyMessage<>(-1,"服务器中音乐批量删除失败",false);}}
验证
查询⾳乐模块设计
功能:支持模糊查询,支持输入的参数为空
输入参数为空时,查出所有的歌曲
MusicMapper
一个是根据歌名进行查询,另一个是查询全部的歌曲
List<Music> findMusicByTitle(String name);List<Music> findMusic();
MusicController
@RequestParam不传递参数时候,可以为空
@RequestMapping("/findmusic")public ResponseBodyMessage<List<Music>> findMusic(@RequestParam(required = false) String musicName){return musicService.findMusic(musicName);}
MusicService
public ResponseBodyMessage<List<Music>> findMusic(String musicName){List<Music> musics=null;//musicName!=null是判断传入的歌曲名是否为空//musicName.trim().isEmpty()是去掉前后的空格,判断字符串是否为空,加上这个更加的严谨if(musicName!=null && !musicName.trim().isEmpty()){log.info("已找到该歌曲");musics=mapper.findMusicByTitle(musicName);if(musics.isEmpty()){return new ResponseBodyMessage<>(-1,"没有找到该歌曲",musics);}return new ResponseBodyMessage<>(1,"成功找到该歌曲",musics);}else {log.info("找到全部的歌曲");musics=mapper.findMusic();return new ResponseBodyMessage<>(1,"成功找到全部的歌曲",musics);}}
验证
输入为空,查询全部的歌曲
输入不为空,查询单个歌曲
添加⾳乐⾄喜欢的列表模块设计
请求和响应的接口实现
LoveMusicMapper
@Mapper
public interface LoveMusicMapper {//检查该音乐是否已经被收藏Music selectMusicIsExist(Integer userId,Integer musicId);//将该音乐加入收藏夹Boolean insertLoveMusic(Integer userId,Integer musicId);
}
<select id="selectMusicIsExist" resultType="com.blame.onlinemusicserver.model.Music">select * from lovemusic where user_id=#{userId} and music_id=#{musicId}</select><insert id="insertLoveMusic">insert into lovemusic (user_id,music_id) values (#{userId},#{musicId})</insert>
LoveMusicController
@RestController
@RequestMapping("/lovemusic")
public class LoveMusicController {@Autowiredprivate LoveMusicService loveMusicService;@RequestMapping("addLikeMusic")public ResponseBodyMessage<Boolean> addLikeMusic(@RequestParam String id,HttpServletRequest request){return loveMusicService.addLikeMusic(id,request);}
}
LoveMusicService
@Slf4j
@Service
public class LoveMusicService {@Autowiredprivate LoveMusicMapper loveMusicMapper;//添加收藏public ResponseBodyMessage<Boolean> addLikeMusic(String id,HttpServletRequest request){Integer musicId=Integer.parseInt(id);System.out.println("musicId"+musicId);//1.检查登录HttpSession session=request.getSession(false);if(session==null || session.getAttribute(Constant.USERINFO_SESSION_KEY)==null){log.error("未登录,请先进行登录");return new ResponseBodyMessage<>(0,"没有登录",false);}User user =(User) session.getAttribute(Constant.USERINFO_SESSION_KEY);Integer userId=user.getId();System.out.println("userId"+userId);Music music=loveMusicMapper.selectMusicIsExist(userId,musicId);if(music!=null){return new ResponseBodyMessage<>(-1,"该音乐已经加入了收藏夹",false);}Boolean successAdd=loveMusicMapper.insertLoveMusic(userId,musicId);if(successAdd){return new ResponseBodyMessage<>(1,"音乐加入收藏夹成功",true);}return new ResponseBodyMessage<>(-1,"音乐加入收藏夹失败",false);}
验证
查询喜欢的⾳乐模块设计
请求和响应的接口实现
LoveMusicMapper
//根据用户信息查询收藏的歌曲,支持模糊查询List<Music> findLoveMusicByUserId(Integer userId);//根据用户信息来查询想要查询的收藏的歌曲List<Music> findLoveMusicByUserIdAndMusicId(Integer userId,String musicName);
<select id="findLoveMusicByUserId" resultType="com.blame.onlinemusicserver.model.Music">select m.* from lovemusic lm,music m where lm.music_id=m.id and lm.user_id=#{userId}</select><select id="findLoveMusicByUserIdAndMusicId" resultType="com.blame.onlinemusicserver.model.Music">select m.* from lovemusic lm,music m where lm.music_id=m.id and lm.user_id=#{userId} and title like concat('%',#{musicName},'%')</select>
LoveMusicController
@RequestMapping("/findlovemusic")public ResponseBodyMessage<List<Music>> findLoveMusic(HttpServletRequest request,@RequestParam(required = false)String musicName){return loveMusicService.findLoveMusic(request, musicName);}
LoveMusicService
public ResponseBodyMessage<List<Music>> findLoveMusic(HttpServletRequest request,String musicName){//判断是否登录HttpSession session= request.getSession(false);if(session==null || session.getAttribute(Constant.USERINFO_SESSION_KEY)==null){log.error("未登录,请先进行登录");return new ResponseBodyMessage<>(0,"没有登录",null);}User user=(User) session.getAttribute(Constant.USERINFO_SESSION_KEY);Integer userId=user.getId();System.out.println("userId"+userId);List<Music> musics=null;if(userId==null){return new ResponseBodyMessage<>(-1,"用户不存在",null);}log.info("已经找到该用户");//查询用户的全部收藏音乐if(musicName==null){musics= loveMusicMapper.findLoveMusicByUserId(userId);//判断 musics 不为空且不为 nullif(!CollectionUtils.isEmpty(musics)){return new ResponseBodyMessage<>(0,"已经找到用户的全部收藏音乐",musics);}else {return new ResponseBodyMessage<>(-1,"用户的收藏音乐为空",null);}}else { //根据用户信息来查询想要查询的收藏的歌曲System.out.println("musicName"+musicName);musics=loveMusicMapper.findLoveMusicByUserIdAndMusicId(userId,musicName);//判断 musics 不为空且不为 nullif(!CollectionUtils.isEmpty(musics)){return new ResponseBodyMessage<>(0,"已经找到用户查询的收藏歌曲",musics);}else {return new ResponseBodyMessage<>(-1,"用户没有收藏此音乐",null);}}}
验证
移除喜欢的⾳乐模块设计
请求和响应的接口实现
LoveMusicMapper
Boolean deleteLoveMusic(Integer userId,Integer musicId);
<delete id="deleteLoveMusic">delete from lovemusic where user_id=#{userId} and music_id=#{musicId}</delete>
LoveMusicController
@RequestMapping("deleteLikeMusic")public ResponseBodyMessage<Boolean> deleteLikeMusic(@RequestParam String id,HttpServletRequest request){return loveMusicService.deleteLikeMusic(id,request);}
LoveMusicService
//取消收藏public ResponseBodyMessage<Boolean> deleteLikeMusic(String id,HttpServletRequest request){Integer musicId=Integer.parseInt(id);System.out.println("musicId"+musicId);//检查登录HttpSession session=request.getSession(false);if(session==null || session.getAttribute(Constant.USERINFO_SESSION_KEY)==null){log.error("未登录,请先进行登录");return new ResponseBodyMessage<>(0,"没有登录",false);}User user =(User) session.getAttribute(Constant.USERINFO_SESSION_KEY);Integer userId=user.getId();System.out.println("userId"+userId);Music music=loveMusicMapper.selectMusicIsExist(userId,musicId);if(music==null){return new ResponseBodyMessage<>(-1,"该音乐不在收藏夹内",false);}Boolean successDelete=loveMusicMapper.deleteLoveMusic(userId,musicId);if(successDelete){return new ResponseBodyMessage<>(1,"音乐已经从收藏夹中删除",true);}return new ResponseBodyMessage<>(-1,"音乐从收藏夹中删除失败",false);}
验证
实现客户端的代码
登录界面
<script>$(function () {$("#submit").click(function () {var username = $("#user").val().trim();var password = $("#password").val().trim();if (username === "" || password === "") {$("#message").text("请输入用户名和密码!");$("#messageBox").fadeIn();return;}$.ajax({url: "/user/login",type: "post",dataType: "json",data: {username: username,password: password},success: function (result) {if (result.status === 0) {alert("登录成功!");window.location.href = "list.html";} else {$("#message").text("账号或密码错误,请重试!");$("#messageBox").fadeIn();$("#user").val("");$("#password").val("");}},error: function () {$("#message").text("服务器连接失败,请稍后重试!");$("#messageBox").fadeIn();}});});});</script>
注册界面
<script>$(function () {$("#registerBtn").click(function () {var username = $("#regUser").val().trim();var email = $("#regEmail").val().trim();var password = $("#regPassword").val().trim();if (username === "" || email === "" || password === "") {$("#regMessage").text("请完善注册信息!");$("#regMessageBox").fadeIn();return;}$.ajax({url: "/user/register",type: "post",dataType: "json",contentType: "application/json",data: JSON.stringify({username: username,password: password,email: email}),success: function (result) {if (result.status === 0) {alert("注册成功!");window.location.href = "login.html";} else {$("#regMessage").text(result.message);$("#regMessageBox").fadeIn();}},error: function () {$("#regMessage").text("服务器连接失败,请稍后重试!");$("#regMessageBox").fadeIn();}});});});
</script>
音乐列表界面
<script type="text/javascript">$(function() {load(); // 页面加载时默认加载所有歌曲});function load(musicName) {$.ajax({url: "/music/findmusic",data: { musicName: musicName },type: "get",dataType: "json",success: function(result) {var data = result.data;var s = '';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>';s += '<td>' + data[i].title + '</td>';s += '<td>' + data[i].singer + '</td>';s += '<td><button class="btn" onclick="playerSong(\'' + musicUrl + '\')">播放歌曲</button></td>';s += '<td><button class="btn" onclick="deleteInfo(' + data[i].id + ')">删除</button>';s += '<button class="btn" onclick="loveInfo(' + data[i].id + ')">喜欢</button></td>';s += '</tr>';}$("#info").html(s); // 更新表格}});}function playerSong(musicUrl) {var name = musicUrl.substring(musicUrl.lastIndexOf('/') + 1); // 获取音乐名称SewisePlayer.toPlay(musicUrl, name, 0, true); // 播放歌曲}function deleteInfo(musicId) {$.ajax({url: "/music/delete",data: { id: musicId },type: "post",dataType: "json",success: function(result) {if (result.data) {alert("删除成功");window.location.href = "list.html"; // 页面跳转} else {alert("删除失败");}}});}$(function(){$("#submit1").click( function(){var name = $("#exampleInputName2").val();load(name);});$.when(load).done(function(){$("#delete").click(function(){var id = new Array();var i = 0;//数组的小标//$("input:checkbox").each(function(){//如果被选中,this代表发生事件的dom元素,<input>if( $(this).is(":checked")) {id[i] = $(this).attr("id");i++;}});console.log(id);$.ajax({url:"/music/deleteSel",data:{"id":id},dataType:"json",type:"post",success:function(obj){if(obj.data == true) {alert("删除成功!");window.location.href = "list.html";}else{alert("删除失败!");}}});});});});function loveInfo(musicId) {$.ajax({url: "lovemusic/addLikeMusic",data: { "id": musicId },type: "post",dataType: "json",success: function(result) {if (result.data) {alert("添加收藏成功");window.location.href = "list.html"; // 页面跳转} else {alert("添加收藏失败");}}});}</script>
收藏音乐界面
<script>$(function() {load();$("#submit1").click(function() {var name = $("#exampleInputName2").val();load(name);});});function load(musicName) {$.ajax({url: "/lovemusic/findlovemusic",data: {"musicName": musicName},type: "GET",dataType: "json",success: function(obj) {var data = obj.data;var s = '';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><audio src="' + musicUrl + '" controls preload="none" loop></audio></td>';s += '<td><button class="btn btn-primary" onclick="deleteInfo(' + data[i].id + ')">移除</button></td>';s += '</tr>';}$("#info").html(s);}});}function deleteInfo(id) {$.ajax({url: "/lovemusic/deleteLikeMusic",type: "POST",data: {"id": id},dataType: "json",success: function(val) {if (val.data == true) {alert("删除成功!,重新加载当前页面!");window.location.href = "list.html";} else {alert("删除失败!");}}});}</script>
上传音乐界面
<form method="POST" enctype="multipart/form-data" action="/music/upload"><div class="form-group"><label for="file">🎶 音乐文件</label><input type="file" id="file" name="filename" required /></div><div class="form-group"><label for="singer">👤 歌手名</label><input type="text" id="singer" name="singer" placeholder="请输入歌手名" required /></div><input type="submit" value="📤 上传" /></form>
在这里的播放音乐的工具,我们使用的是一个开源的播放工具
demos/例子说明.md · Jack Zhang/sewise-player - Gitee.com
<div style="width: 180px; height: 140px; position: absolute; bottom: 10px; right: 10px;"><script type="text/javascript" src="player/sewise.player.min.js"></script><script type="text/javascript">SewisePlayer.setup({server: "vod",type: "mp3",videourl: "http://jackzhang1204.github.io/materials/where_did_time_go.mp3",skin: "vodWhite",autostart: "false",});</script></div>
实现播放
function playerSong(musicUrl) {var name = musicUrl.substring(musicUrl.lastIndexOf('/') + 1); // 获取音乐名称SewisePlayer.toPlay(musicUrl, name, 0, true); // 播放歌曲}
配置拦截器
自定义拦截器
@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception {HttpSession session = request.getSession(false);if (session != null && session.getAttribute(Constant.USERINFO_SESSION_KEY) != null) {return true;}// 未登录提示//是告诉浏览器内容是 JSON,并且是 UTF-8 编码;response.setContentType("application/json;charset=utf-8");//手动返回一段json数据,进行错误提示response.getWriter().write("{\"code\":401,\"msg\":\"未登录,请先登录\"}");return false;}
}
使用拦截器
只释放静态资源和登录接口
@Configuration
public class AppConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {LoginInterceptor loginInterceptor = new LoginInterceptor();registry.addInterceptor(loginInterceptor).addPathPatterns("/**") // 拦截所有请求.excludePathPatterns("/js/**","/css/**","/images/**","/fonts/**","/player/**","/login.html","/user/login"); // 放行静态资源和登录接口}
}
项目在此全部结束,可以将项目部署到云服务器上
希望能对大家有所帮助!!!
相关文章:
项目:在线音乐播放服务器——基于SSM框架和mybatis
介绍项目 项目主要是基于SSM框架和mybatis进行实现 主要的功能: 登陆界面,用户注册,音乐的播放列表,删除指定的歌曲,批量删除指定的歌曲,收藏歌曲,查询歌曲,从收藏列表中删除收藏…...
Linux配置vimplus
配置vimplus CentOS的配置方案很简单,但是Ubuntu的解决方案网上也很多但是有效的很少,尤其是22和24的解决方案,在此我整理了一下我遇到的问题解决方法 CentOS7 一键配置VimForCPP 基本上不会有什么特别难解决的报错 sudo yum install vims…...
Ubuntu22.04开机运行程序
新建启动文件 sudo vim /etc/systemd/system/trojan.service 2. 写入配置文件 [Unit] DescriptionTrojan Proxy Service Afternetwork.target[Service] Typesimple ExecStart/home/cui/Downloads/trojan/trojan -c /home/cui/Downloads/trojan/config.json Restarton-failur…...
高效查询:位图、B+树
1. 位图(BitMap)与布隆过滤器(Bloom Filter) 1.1. 问题背景与解决方案 问题背景 场景:网页爬虫判重 搜索引擎的爬虫会不断地解析网页中的链接并继续爬取。一个网页可能在多个页面中出现,容易重复爬取。…...
HashMap的扩容机制
在添加元素或初始化的时候需要调用resize方法进行扩容,第一次添加数据初始化数组长度为16,以后每次每次扩容都是达到了扩容阈值(数组长度 * 0.75) 每次扩容的时候,都是扩容之前容量的2倍; 扩容之后&#…...
从坏道扫描到错误修复:HD Tune实战指南
一、硬盘检测的必要性 随着计算机使用时间的增加,机械硬盘和固态硬盘都会出现不同程度的性能衰减。定期进行硬盘健康检查可以:及时发现潜在故障;预防数据丢失风险;掌握存储设备实际状态。 二、HD Tune功能解析 性能测试&#x…...
Leetcode 3553. Minimum Weighted Subgraph With the Required Paths II
Leetcode 3553. Minimum Weighted Subgraph With the Required Paths II 1. 解题思路2. 代码实现 题目链接:3553. Minimum Weighted Subgraph With the Required Paths II 1. 解题思路 这一题很惭愧,并没有自力搞定,是看了大佬们的解答才有…...
算法加训之最短路 上(dijkstra算法)
目录 P4779 【模板】单源最短路径(标准版)(洛谷) 思路 743. 网络延迟时间(力扣) 思路 1514.概率最大路径(力扣) 思路 1631.最小体力消耗路径 思路 1976. 到达目的地的方案数 …...
01 Nginx安装及基本配置
01 Nginx安装 # 官网:https://nginx.org/en/ # 点击下载图1 Nginx下载官网 # https://nginx.org/en/download.html # 全是各个平台的源码包图2 Nginx下载版本 # 找到最下面的stable and mainline(稳定版和主线版)图3 找到最下面的稳定版 # https://nginx.org/en/li…...
ABP vNext 多租户系统实现登录页自定义 Logo 的最佳实践
🚀 ABP vNext 多租户系统实现登录页自定义 Logo 的最佳实践 🧭 版本信息与运行环境 ABP Framework:v8.1.5.NET SDK:8.0数据库:PostgreSQL(支持 SQLServer、MySQL 等)BLOB 存储:本地…...
Docker 网络
目录 前言 1. Docker 网络模式 2. 默认 bridge 网络详解 (1)特点 (2)操作示例 3. host 网络模式 (1)特点 (2)操作示例 4. overlay…...
btc交易所关键需求区 XBIT反弹与上涨潜力分析
在加密货币市场的浪潮中,狗狗币(DOGE)近期的走势吸引了众多投资者的目光。根据XBIT分析,狗狗币刚刚踏入关键需求区,此前虽从高点大幅下跌了10%,但XBIT去中心化交易所平台分析师认为,短期内它有望…...
深度剖析:YOLOv8融入UNetv2 SDI模块的性能提升之旅
文章目录 一、引言二、SDI多层次特征融合模块概述(一)背景和动机(二)模块设计原理 三、SDI模块实现(一)关键代码结构(二)代码解析 四、将SDI模块融入YOLOv8(一࿰…...
图像定制大一统?字节提出DreamO,支持人物生成、 ID保持、虚拟试穿、风格迁移等多项任务,有效解决多泛化性冲突。
字节提出了一个统一的图像定制框架DreamO,支持人物生成、 ID保持、虚拟试穿、风格迁移等多项任务,不仅在广泛的图像定制场景中取得了高质量的结果,而且在适应多条件场景方面也表现出很强的灵活性。现在已经可以支持消费级 GPU(16G…...
spark数据处理练习题详解【下】
12. (单选题) def main(args: Array[String]): Unit { println(func1("张三",f1)) } def func1(name:String,fp:(________________)): String { fp(name) } def f1(s:String): String { "welcome "s } 选择填空() A.String>S…...
Vue基础(11)_条件渲染
原生css想让显示的元素隐藏,方式有以下几点: display: none; opacity: 0; visibility: hidden; 那么vue中是怎样实现元素显示/隐藏的呢? 条件渲染 v-show 写法:v-show"表达式" 判断:表达式转换为布尔值(tr…...
湖北理元理律师事务所:债务优化服务的四维创新实践
在债务问题普遍影响家庭经济稳定的当下,专业法律服务机构的价值不仅在于提供解决方案,更需构建可持续的服务生态。湖北理元理律师事务所通过“法律心理技术教育”四维服务体系,探索出一条兼顾债务化解与生活质量保障的创新路径。 服务模式创…...
ubuntu工控机固定设备usb串口号
ubuntu工控机固定设备usb串口号 1、多个USB设备的ID相同 ubuntu系统中的串口使用权限并没有对所有的用户进行开放,所以在使用代码对串口进行操作时,需要打开用户对串口的使用权限,否则在代码中会出现“串口无法打开的报错”,只有…...
MongoDB的安装及简单使用
MongoDB 是一个开源的文档型 NoSQL 数据库,由 MongoDB Inc. 开发,专为灵活性和可扩展性设计。 特点: 1.文档模型:数据以 BSON(二进制 JSON)格式存储,支持嵌套结构。 2.动态 S…...
卷积神经网络进阶:转置卷积与棋盘效应详解
【内容摘要】 本文深入解析卷积神经网络中的转置卷积(反卷积)技术,重点阐述标准卷积与转置卷积的计算过程、转置卷积的上采样作用,以及其常见问题——棋盘效应的产生原因与解决方法,为图像分割、超分辨率等任务提供理论…...
Linux进程信号(三)之信号产生2
文章目录 4. 由软件条件产生信号5. 硬件异常产生信号模拟一下除0错误和野指针异常除0错误野指针错误 总结思考一下 4. 由软件条件产生信号 SIGPIPE是一种由软件条件产生的信号,在“管道”中已经介绍过了。 软件条件不就绪,很明显这个软件条件没有直接报错ÿ…...
【AWS入门】Amazon SageMaker简介
【AWS入门】Amazon SageMaker简介 [AWS Essentials] Brief Introduction to Amazon SageMaker By JacksonML 机器学习(Machine Learning,简称ML) 是当代流行的计算机科学分支技术。通常,人们在本地部署搭建环境,以满足机器学习的要求。 AWS…...
MySQL--day2--基本的select语句
(以下内容全部来自上述课程) SQL概述 结构化查询语句 1. SQL分类 DDL:数据定义(definition)语言:create、drop、alter… DML:数据操作(manipulation)语言ÿ…...
程序代码篇---python获取http界面上按钮或者数据输入
文章目录 前言 前言 本文简单接受了python获取http界面上按钮或者数据输入...
网络安全利器:蜜罐技术详解
蜜罐是网络安全领域中一种主动防御和情报收集的重要工具。本文将深入探讨蜜罐技术的原理、类型、应用场景以及部署注意事项。 1. 什么是蜜罐? 蜜罐(Honeypot)是一种安全资源,其价值在于被探测、攻击或未经授权使用。简单来说,蜜罐就是一个诱饵系统,用来吸引黑客的注意力…...
回溯实战篇3
文章目录 前言排列全排列全排列II 棋盘问题N皇后解数独 其他递增子序列重新安排行程 前言 今天继续带大家进行回溯的实战篇3,去学习如何用回溯的方法去解决排列和棋盘以及其他用回溯方法解决的问题,最重要的就是学会回溯三部曲的构建,一文带…...
Spark 基础自定义分区器
(一)什么是分区 【复习提问:RDD的定义是什么?】 在 Spark 里,弹性分布式数据集(RDD)是核心的数据抽象,它是不可变的、可分区的、里面的元素并行计算的集合。 在 Spark 中…...
【提高+/省选−】洛谷P1495 —— 【模板】中国剩余定理(CRT)/ 曹冲养猪
见:P1495 【模板】中国剩余定理(CRT)/ 曹冲养猪 - 洛谷 题目描述 自从曹冲搞定了大象以后,曹操就开始捉摸让儿子干些事业,于是派他到中原养猪场养猪,可是曹冲满不高兴,于是在工作中马马虎虎&a…...
系统架构设计师考前冲刺笔记-第1章-系统工程与信息系统基础
文章目录 第1章 系统工程与信息系统基础大纲13 DSS5678 BSP910 SCM11 OLAP12 OLAP14 BRP15 集成16 企业门户19 边缘计算 第1章 系统工程与信息系统基础 大纲 1 3 DSS DSS 决策支持系统 Decision Support System 5 6 7 8 BSP 9 10 SCM 注意:生产计划 11 OLAP O…...
Vue环境下数据导出Excel的全面指南
文章目录 1. 前言2. 原生JavaScript实现方案2.1 使用Blob对象和URL.createObjectURL2.2 使用Base64编码实现 3. 常用第三方库方案3.1 使用SheetJS (xlsx)3.2 使用ExcelJS3.3 使用vue-json-excel 4. 服务器端导出方案4.1 前端请求服务器生成Excel4.2 使用Web Worker处理大数据导…...
Linux下 使用 SSH 完成 Git 绑定 GitHub
文章目录 1、检查 SSH2、生成 SSH key3、添加 SSH key4、验证绑定是否成功 1、检查 SSH Git Bash 中输入ssh命令,查看本机是否安装 SSH: 2、生成 SSH key (1)输入 ssh-keygen -t rsa 命令,表示我们指定 RSA 算法生…...
Jsoup库和Apache HttpClient库有什么区别?
Jsoup 和 Apache HttpClient 是两个功能不同的库,它们在 Java 开发中被广泛使用,但用途和功能有明显的区别: Jsoup 用途:Jsoup 是一个用于解析 HTML 文档的库。它提供了非常方便的方法来抓取和解析网页内容,提取和操作…...
安全漏洞频发,如何加强防护措施?
当系统安全漏洞频发时,应从代码安全审查、自动化漏洞扫描、权限控制与访问管理、员工安全意识培训等四个关键维度加强防护。其中,代码安全审查是防止漏洞渗透的第一道防线。企业应将代码安全审查纳入CI/CD流程,实施静态代码分析和依赖包检查机…...
Text models —— BERT,RoBERTa, BERTweet,LLama
BERT 什么是BERT? BERT,全称Bidirectional Encoder Representations from Transformers,BERT是基于Transformer的Encoder(编码器)结构得来的,因此核心与Transformer一致,都是注意力机制。这种…...
CodeBuddy初探
回顾Trae 上一篇博客Trae IDE和VSCode Trae插件初探-CSDN博客,我们进行了TraeIDE和Trae插件初探,给了Trae这样一个任务: 生成一个to do list清单web页面,采用vue实现,可以在页面上进行todolist进行增删改查。 Trae的…...
spark数据处理练习题详解【上】
1. (单选题) scala中属于序列的可变的集合,可以添加,删除元素的是() A.Array B.List C.Tuple D.ListBuffer 答案及解析:D 在Scala中,属于序列的可变集合,可以添加和删除元素的是ÿ…...
sparkSQL读入csv文件写入mysql(2)
(二)创建数据库和表 接下来,我们去创建一个新的数据库,数据表,并插入一条数据。 -- 创建数据库 CREATE DATABASE spark; -- 使用数据库 USE spark;-- 创建表 create table person(id int, name char(20), age int);-- …...
产品周围的几面墙
不能把排序,当单选题做。 2025年的杭州咖啡馆,味道最浓的不是咖啡,是聊各种项目和创业的卷味。 在过去几年,聊项目的也不少,那时候带着更加浓烈的自信和松弛感,不过今年略带几分忐忑和试探的口吻。 看到网…...
【锂电池剩余寿命预测】LSTM长短期记忆神经网络锂电池剩余寿命预测(Pytorch完整源码和数据)
目录 效果一览程序获取程序内容代码分享效果一览 程序获取 获取方式一:文章顶部资源处直接下载:...
螺旋矩阵--LeetCode
题目 给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。 示例 1: 输入:matrix [[1,2,3],[4,5,6],[7,8,9]] 输出:[1,2,3,6,9,8,7,4,5]示例 2: 输入:matrix [[…...
湖北理元理律师事务所:债务管理的社会价值探索
债务问题从来不是孤立的经济事件,其背后牵涉家庭稳定、社会信用体系乃至区域经济发展。湖北理元理律师事务所通过五年服务数据发现:科学债务规划可使单个家庭挽回约23%的可支配收入,间接降低离婚率、心理健康问题发生率等社会成本。 债务优化…...
知识图谱(KG)与大语言模型(LLM)
知识图谱(KG)以其结构化的知识表示和推理能力,为大语言模型(LLM)的“幻觉”、知识更新滞后和可解释性不足等问题提供了有力的解决方案。反过来,LLM的强大文本理解和生成能力也为KG的构建、补全、查询和应用…...
LLM大语言模型系列1-token
一,什么是token 1,什么是token: 参考:https://en.wikipedia.org/wiki/Token https://en.wikipedia.org/wiki/Lexical_analysis#Token 我们有很多描述token的解释,建议是汇总在一起进行综合理解: 1️⃣To…...
数据清洗-案例
四)实现代码 在之前的项目的基础之上,重写去写一个包,并创建两个类:WebLogMapper和WebLogDriver类。 (1)编写WebLogMapper类 package com.root.mapreduce.weblog; import java.io.IOException; import…...
项目的部署发布和访问的流程
首先打包项目: npm run build 打包后的文件会生成在dist文件夹中,将dist文件夹需要放到服务器里面,意味着服务有dist静态资源(index.html,css/,js/,img/) 用户在浏览器输入域名&am…...
人工智能、机器学习、深度学习定义与联系
人工智能、机器学习、深度学习定义与联系目录 一、人工智能(Artificial Intelligence, AI)1、定义2、特征:3、关键阶段的概述:1. 萌芽期(1940s–1950s):理论奠基2. 形成期(1950s–19…...
Gartner《如何将生成式人工智能(GenAI)集成到应用架构》学习心得
针对软件架构师、技术专业人士如何更好的把 GenAI 如何融入解决方案,提升用户体验、生产力并带来差异化成果的趋势,Gartner发布了《Integrating GenAI Into Your Application Architecture》研究报告。 报告首先介绍了 GenAI 的发展背景,指出其已成为主流趋势,大型语言模型…...
vscode中Debug c++
在vscode中Debug ros c程序 1 在Debug模式下编译 如果用命令行catkin_make,在输入catkin_make时加上一个参数: catkin_make -DCMAKE_BUILD_TYPEDebug 或者直接修改CMakelist.txt,添加以下代码: SET(CMAKE_BUILD_TYPE "D…...
c++从入门到精通(六)--特殊工具与技术-完结篇
特殊工具与技术-完结篇 控制内存分配 重载new和delete: 如果应用程序希望控制内存分配的过程,则它们需要定义自己的operator new函数和operator delete函数。当自定义了全局的operator new函数和operator delete函数后,我们就担负起了控…...
原型链的详细解释及使用场景
一、原型链的概念 原型链是JavaScript实现继承和属性共享的核心机制。每个对象都有一个内部属性[[Prototype]](可通过proto访问),指向其原型对象。当访问对象的属性时,若对象自身不存在该属性,则会沿着原型链向上查找…...