Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 36 additions & 12 deletions server/src/main/java/cn/keking/utils/DownloadUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.client.HttpClientErrorException;

import java.io.File;
import java.io.FileNotFoundException;
Expand Down Expand Up @@ -46,9 +47,8 @@ public static ReturnResponse<String> downLoad(FileAttribute fileAttribute, Strin
}
ReturnResponse<String> response = new ReturnResponse<>(0, "下载成功!!!", "");
String realPath = getRelFilePath(fileName, fileAttribute);
// 获取文件后缀用于校验
final String fileSuffix = fileAttribute.getSuffix();
// 判断是否非法地址

if (KkFileUtils.isIllegalFileName(realPath)) {
response.setCode(1);
response.setContent(null);
Expand All @@ -61,57 +61,77 @@ public static ReturnResponse<String> downLoad(FileAttribute fileAttribute, Strin
response.setMsg("下载失败:不支持的类型!" + urlStr);
return response;
}
if (fileAttribute.isCompressFile()) { //压缩包文件 直接赋予路径 不予下载
if (fileAttribute.isCompressFile()) {
response.setContent(fileDir + fileName);
response.setMsg(fileName);
return response;
}
// 如果文件是否已经存在、且不强制更新,则直接返回文件路径
if (KkFileUtils.isExist(realPath) && !fileAttribute.forceUpdatedCache()) {
response.setContent(realPath);
response.setMsg(fileName);
return response;
}

try {
URL url = WebUtils.normalizedURL(urlStr);
if (!fileAttribute.getSkipDownLoad()) {
if (isHttpUrl(url)) {
File realFile = new File(realPath);
CloseableHttpClient httpClient = HttpRequestUtils.createConfiguredHttpClient();
String finalUrlStr = urlStr;

final boolean[] hasMimeError = {false};
final String[] mimeErrorMessage = {null};

HttpRequestUtils.executeHttpRequest(url, httpClient, fileAttribute, responseWrapper -> {
// 获取响应头中的Content-Type
String contentType = responseWrapper.getContentType();

// 如果是Office/设计文件,需要校验MIME类型
if (WebUtils.isMimeCheckRequired(fileSuffix)) {
if (!WebUtils.isValidMimeType(contentType, fileSuffix)) {
logger.error("文件类型错误,期望二进制文件但接收到文本类型,url: {}, Content-Type: {}",
finalUrlStr, contentType);
responseWrapper.setHasError(true);
hasMimeError[0] = true;
mimeErrorMessage[0] = "期望二进制文件但接收到文本类型,Content-Type: " + contentType;
return;
}
}

// 保存文件
FileUtils.copyToFile(responseWrapper.getInputStream(), realFile);
});

if (hasMimeError[0]) {
response.setCode(1);
response.setContent(null);
response.setMsg(mimeErrorMessage[0]);
return response;
}

} else if (isFtpUrl(url)) {
String ftpUsername = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_USERNAME);
String ftpPassword = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_PASSWORD);
String ftpControlEncoding = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_CONTROL_ENCODING);
String ftpport = WebUtils.getUrlParameterReg(realPath, URL_PARAM_FTP_PORT);
FtpUtils.download(fileAttribute.getUrl(), ftpport, realPath, ftpUsername, ftpPassword, ftpControlEncoding);
} else if (isFileUrl(url)) { // 添加对file协议的支持
} else if (isFileUrl(url)) {
handleFileProtocol(url, realPath);
} else {
response.setCode(1);
response.setMsg("url不能识别url" + urlStr);
return response;
}
}
response.setContent(realPath);
response.setMsg(fileName);
return response;

} catch (HttpClientErrorException e) {
logger.error("HTTP请求失败,状态码:{},url:{}", e.getStatusCode(), urlStr);
response.setCode(1);
response.setContent(null);
if (e.getStatusCode().is4xxClientError()) {
response.setMsg("文件不存在或无法访问 (HTTP " + e.getStatusCode() + ")");
} else {
response.setMsg("下载失败: " + e.getMessage());
}
return response;
} catch (IOException | GalimatiasParseException e) {
logger.error("文件下载失败,url:{}", urlStr);
response.setCode(1);
Expand All @@ -123,7 +143,11 @@ public static ReturnResponse<String> downLoad(FileAttribute fileAttribute, Strin
}
return response;
} catch (Exception e) {
throw new RuntimeException(e);
logger.error("下载文件时发生未知异常,url:{}", urlStr, e);
response.setCode(1);
response.setContent(null);
response.setMsg("下载失败: " + e.getMessage());
return response;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.client.HttpClientErrorException;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
Expand Down Expand Up @@ -152,34 +154,76 @@ public void getCorsFile(@RequestParam String urlPath,
// 1. 验证接口是否开启
if (!ConfigConstants.getGetCorsFile()) {
logger.info("接口关闭,禁止访问!,url:{}", urlPath);
try {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "接口已关闭");
} catch (IOException ignored) {}
return;
}
//2. 验证访问权限
// 2. 验证访问权限
if (WebUtils.validateKey(key)) {
logger.info("访问不合法:访问密码不正确!,url:{}", urlPath);
try {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "访问密码不正确");
} catch (IOException ignored) {}
return;
}

URL url;
try {
urlPath = WebUtils.decodeUrl(urlPath, encryption);
url = WebUtils.normalizedURL(urlPath);
} catch (Exception ex) {
logger.error(String.format(BASE64_DECODE_ERROR_MSG, urlPath),ex);
logger.error(String.format(BASE64_DECODE_ERROR_MSG, urlPath), ex);
try {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "URL 解析失败");
} catch (IOException ignored) {}
return;
}

assert urlPath != null;
if (!isHttpUrl(url) && !isFtpUrl(url)) {
logger.info("读取跨域文件异常,可能存在非法访问,urlPath:{}", urlPath);
try {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "不支持的协议");
} catch (IOException ignored) {}
return;
}

FileAttribute fileAttribute = fileHandlerService.getFileAttribute(urlPath, req);
InputStream inputStream = null;
logger.info("读取跨域文件url:{}", urlPath);
if (!isFtpUrl(url)) {
CloseableHttpClient httpClient = HttpRequestUtils.createConfiguredHttpClient();

HttpRequestUtils.executeHttpRequest(url, httpClient, fileAttribute, responseWrapper -> IOUtils.copy(responseWrapper.getInputStream(), response.getOutputStream()));
if (!isFtpUrl(url)) {
// HTTP/HTTPS 处理
try (CloseableHttpClient httpClient = HttpRequestUtils.createConfiguredHttpClient()) {
HttpRequestUtils.executeHttpRequest(url, httpClient, fileAttribute, responseWrapper -> {
// 如果响应状态码不是 2xx,可以提前抛出异常(executeHttpRequest 内部可能已经处理)
// 这里直接复制流
IOUtils.copy(responseWrapper.getInputStream(), response.getOutputStream());
});
} catch (HttpClientErrorException e) {
// 捕获 HTTP 4xx 错误(如 404)
logger.error("HTTP 请求失败,状态码:{},url:{}", e.getStatusCode(), urlPath);
try {
if (e.getStatusCode().is4xxClientError()) {
response.sendError(e.getStatusCode().value(), "文件不存在或无法访问");
} else {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "下载文件时发生错误");
}
} catch (IOException ignored) {
}
} catch (Exception e) {
// 捕获其他异常(如连接超时、IO 异常等)
logger.error("读取跨域文件异常,url:{}", urlPath, e);
try {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "读取文件失败: " + e.getMessage());
} catch (IOException ignored) {
}
}
// 注意:httpClient 可能需要关闭,但 executeHttpRequest 内部应该已管理其生命周期
// 如果 executeHttpRequest 未关闭 client,可在此关闭
} else {
// FTP 处理
InputStream inputStream = null;
try {
String filename = urlPath.substring(urlPath.lastIndexOf('/') + 1);
String contentType = WebUtils.getContentTypeByFilename(filename);
Expand All @@ -190,10 +234,23 @@ public void getCorsFile(@RequestParam String urlPath,
String ftpPassword = WebUtils.getUrlParameterReg(urlPath, URL_PARAM_FTP_PASSWORD);
String ftpControlEncoding = WebUtils.getUrlParameterReg(urlPath, URL_PARAM_FTP_CONTROL_ENCODING);
String support = WebUtils.getUrlParameterReg(urlPath, URL_PARAM_FTP_PORT);
inputStream= FtpUtils.preview(urlPath,support, urlPath, ftpUsername, ftpPassword, ftpControlEncoding);
inputStream = FtpUtils.preview(urlPath, support, urlPath, ftpUsername, ftpPassword, ftpControlEncoding);
IOUtils.copy(inputStream, response.getOutputStream());
} catch (IOException e) {
logger.error("读取跨域文件异常,url:{}", urlPath);
logger.error("读取跨域文件异常,url:{}", urlPath, e);
try {
// 根据异常信息判断是否为文件不存在
if (e.getMessage() != null && (e.getMessage().contains("550") || e.getMessage().contains("File not found"))) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "FTP 文件不存在");
} else {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "FTP 读取失败");
}
} catch (IOException ignored) {}
} catch (Exception e) {
logger.error("FTP 预览发生未知异常,url:{}", urlPath, e);
try {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "FTP 服务异常");
} catch (IOException ignored) {}
} finally {
IOUtils.closeQuietly(inputStream);
}
Expand Down
Loading