wenj you hua

This commit is contained in:
menxipeng
2025-08-28 21:43:41 +08:00
parent 40234f3694
commit baa9bace5b
3 changed files with 158 additions and 56 deletions

View File

@@ -41,66 +41,56 @@ public class FileController extends BaseController {
// 检查用户登录状态 // 检查用户登录状态
// 获取文件字节 // 获取文件字节
if (objectName.equals("musicFile")) { // if (objectName.equals("musicFile")) {
LoginUser userInfo = SecurityUtils.getLoginUser(); // LoginUser userInfo = SecurityUtils.getLoginUser();
if (userInfo == null){ // if (userInfo == null){
response.setStatus(HttpServletResponse.SC_BAD_REQUEST); // response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.setContentType("application/json;charset=UTF-8"); // response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":401,\"msg\":\"用户未登录\"}"); // response.getWriter().write("{\"code\":401,\"msg\":\"用户未登录\"}");
return; // return;
} // }
SysUser sysUser = userInfo.getUser(); // SysUser sysUser = userInfo.getUser();
if (sysUser == null) { // if (sysUser == null) {
if (StrUtil.isBlank(musicId)) { // if (StrUtil.isBlank(musicId)) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST); // response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.setContentType("application/json;charset=UTF-8"); // response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":400,\"msg\":\"音乐ID不能为空\"}"); // response.getWriter().write("{\"code\":400,\"msg\":\"音乐ID不能为空\"}");
return; // return;
} // }
ShopUser shopUser = userInfo.getShopUser(); // ShopUser shopUser = userInfo.getShopUser();
if (shopUser == null) { // if (shopUser == null) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json;charset=UTF-8"); // response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":401,\"msg\":\"用户未登录\"}"); // response.getWriter().write("{\"code\":401,\"msg\":\"用户未登录\"}");
return; // return;
} // }
//shopUser = shopUserMapper.selectShopUserByUserId(userId); // //shopUser = shopUserMapper.selectShopUserByUserId(userId);
MusicInfo musicInfo = musicInfoMapper.selectByMusicId(musicId); // MusicInfo musicInfo = musicInfoMapper.selectByMusicId(musicId);
//
if (musicInfo == null) { // if (musicInfo == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND); // response.setStatus(HttpServletResponse.SC_NOT_FOUND);
response.setContentType("application/json;charset=UTF-8"); // response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":404,\"msg\":\"音乐信息不存在\"}"); // response.getWriter().write("{\"code\":404,\"msg\":\"音乐信息不存在\"}");
return; // return;
} // }
//
if (musicInfo.getVip() != null && musicInfo.getVip() == 1) { // if (musicInfo.getVip() != null && musicInfo.getVip() == 1) {
// 判断用户vip // // 判断用户vip
if (!MusicUtil.getShopIsVip(shopUser)) { // if (!MusicUtil.getShopIsVip(shopUser)) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN); // response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.setContentType("application/json;charset=UTF-8"); // response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":403,\"msg\":\"该音乐为VIP专享请升级VIP后下载\"}"); // response.getWriter().write("{\"code\":403,\"msg\":\"该音乐为VIP专享请升级VIP后下载\"}");
return; // return;
} // }
} // }
} // }
} // }
byte[] data = AliConfig.ossDown(ossPath);
if (data == null || data.length == 0) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":404,\"msg\":\"文件不存在或下载失败\"}");
return;
}
// 设置响应头 // 设置响应头
String fileExtension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase(); String fileExtension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
if (fileExtension.equals("mp3")) { if (fileExtension.equals("mp3")) {
// 对于MP3文件设置为音频流支持直接播放 // 对于MP3文件设置为音频流支持直接播放
response.setContentType("audio/mpeg"); response.setContentType("audio/mpeg");
// 设置内容长度
response.setContentLength(data.length);
// 允许范围请求,支持断点续传 // 允许范围请求,支持断点续传
response.setHeader("Accept-Ranges", "bytes"); response.setHeader("Accept-Ranges", "bytes");
// 不设置Content-Disposition这样浏览器会直接播放而不是下载 // 不设置Content-Disposition这样浏览器会直接播放而不是下载
@@ -109,9 +99,23 @@ public class FileController extends BaseController {
response.setContentType("application/octet-stream"); response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=" + java.net.URLEncoder.encode(fileName, "UTF-8")); response.setHeader("Content-Disposition", "attachment; filename=" + java.net.URLEncoder.encode(fileName, "UTF-8"));
} }
// 设置缓冲区大小和连接保持
response.setBufferSize(8192); // 8KB缓冲区
response.setHeader("Connection", "Keep-Alive");
try { try {
response.getOutputStream().write(data); // 使用流式下载而不是一次性加载全部内容到内存
response.getOutputStream().flush(); boolean success = AliConfig.ossDownloadStream(ossPath, response.getOutputStream());
if (!success) {
if (!response.isCommitted()) {
response.reset();
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":404,\"msg\":\"文件不存在或下载失败\"}");
}
return;
}
} catch (IOException e) { } catch (IOException e) {
// 检查是否为客户端断开连接的错误Broken pipe // 检查是否为客户端断开连接的错误Broken pipe
if (e.getMessage() != null && if (e.getMessage() != null &&

View File

@@ -160,4 +160,100 @@ public class AliConfig {
return null; return null;
} }
/**
* 流式下载OSS文件直接写入输出流避免一次性加载整个文件到内存
*
* @param objectName OSS对象名称
* @param outputStream 输出流
* @return 是否下载成功
*/
public static boolean ossDownloadStream(String objectName, java.io.OutputStream outputStream) {
// 配置参数
String endpoint = "https://oss-cn-beijing.aliyuncs.com";
String accessKeyId = AliKeyConfig.ACCESS_KEY_ID;
String accessKeySecret = AliKeyConfig.ACCESS_KEY_SECRET;
String bucketName = "wenzhuangmusic";
OSS ossClient = null;
InputStream inputStream = null;
try {
ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
// 判断文件是否存在
boolean exists = ossClient.doesObjectExist(bucketName, objectName);
if (!exists) {
log.error("OSS文件不存在: {}", objectName);
return false;
}
// 获取文件对象
OSSObject ossObject = ossClient.getObject(bucketName, objectName);
inputStream = ossObject.getObjectContent();
// 使用缓冲区进行流式传输
byte[] buffer = new byte[8192]; // 8KB缓冲区
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
try {
outputStream.write(buffer, 0, bytesRead);
// 定期刷新输出流,避免缓冲区溢出
if (bytesRead == buffer.length) {
outputStream.flush();
}
} catch (java.io.IOException e) {
// 检查是否为客户端断开连接的错误
if (e.getMessage() != null &&
(e.getMessage().contains("Broken pipe") ||
e.getMessage().contains("Connection reset by peer") ||
e.getMessage().contains("连接被对方重置") ||
e.getMessage().contains("你的主机中的软件中止了一个已建立的连接") ||
e.getMessage().contains("Software caused connection abort") ||
e.getMessage().contains("SocketTimeoutException"))) {
// 客户端已断开连接,记录日志但不作为错误处理
log.info("客户端断开连接,文件传输中断: {}", e.getMessage());
return true; // 返回true因为这不是服务器端的错误
} else {
// 其他IO错误重新抛出
throw e;
}
}
}
outputStream.flush();
return true;
} catch (java.io.IOException e) {
// 检查是否为客户端断开连接的错误
if (e.getMessage() != null &&
(e.getMessage().contains("Broken pipe") ||
e.getMessage().contains("Connection reset by peer") ||
e.getMessage().contains("连接被对方重置") ||
e.getMessage().contains("你的主机中的软件中止了一个已建立的连接") ||
e.getMessage().contains("Software caused connection abort") ||
e.getMessage().contains("SocketTimeoutException"))) {
// 客户端已断开连接,记录日志但不作为错误处理
log.info("客户端断开连接,文件传输中断: {}", e.getMessage());
return true; // 返回true因为这不是服务器端的错误
} else {
// 其他IO错误
log.error("下载OSS文件失败: {}", e.getMessage());
return false;
}
} catch (Exception e) {
log.error("下载OSS文件失败: {}", e.getMessage());
return false;
} finally {
// 关闭资源
try {
if (inputStream != null) {
inputStream.close();
}
} catch (java.io.IOException e) {
log.error("关闭输入流失败: {}", e.getMessage());
}
if (ossClient != null) {
ossClient.shutdown();
}
}
}
} }

View File

@@ -101,6 +101,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<select id="selectShareInfoListPage" resultType="com.ruoyi.common.core.domain.entity.ShareInfo"> <select id="selectShareInfoListPage" resultType="com.ruoyi.common.core.domain.entity.ShareInfo">
SELECT SELECT
si.id as id,
si.share_num as shareNum,
su.username as username, su.username as username,
su.nickname as nickname, su.nickname as nickname,
su.head_img as headImg, su.head_img as headImg,