🐞 fix(server): 分发文件使用文件中心或者静态文件上传至节点使用实际文件名

This commit is contained in:
小吾立 2024-07-24 14:58:59 +08:00
parent d7cc7d40ba
commit 6cbbe721e8
14 changed files with 49 additions and 25 deletions

View File

@ -9,6 +9,7 @@
3. 【all】优化 解析 HTTP `Accept-Language` 请求头支持多语言最高优先级
4. 【server】修复 页面未刷新情况下打开弹窗次数过多不能提示窗口层级太低(感谢[@lin_yeqi](https://gitee.com/lin_yeqi) [Gitee issues IAEBUZ](https://gitee.com/dromara/Jpom/issues/IAEBUZ)
5. 【server】优化 分发日志现在关联数据信息(感谢[@pumpkinor](https://gitee.com/pumpkinor) [Gitee issues IAF7IV](https://gitee.com/dromara/Jpom/issues/IAF7IV)
6. 【server】优化 分发文件使用文件中心或者静态文件上传至节点使用实际文件名(感谢[@pumpkinor](https://gitee.com/pumpkinor) [Gitee issues IAF7GD](https://gitee.com/dromara/Jpom/issues/IAF7GD)
------

View File

@ -17,6 +17,7 @@ import cn.hutool.core.io.NioUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import lombok.Lombok;
@ -232,4 +233,15 @@ public class FileUtils {
FileUtil.del(src);
}
}
public static String safeFileName(String name1, String extName, String defaultName){
// 需要考虑文件名中存在非法字符 [\s\\/:\*\?\"<>\|]
String name = ReUtil.replaceAll(name1, "[\\s\\\\/:\\*\\?\\\"<>\\|]", "");
if (StrUtil.isEmpty(name)) {
name = defaultName;
} else if (!StrUtil.endWith(name, StrUtil.DOT + extName)) {
name += StrUtil.DOT + extName;
}
return name;
}
}

View File

@ -556,7 +556,7 @@ public class ReleaseManage {
//
Set<Integer> progressRangeList = ConcurrentHashMap.newKeySet((int) Math.floor((float) 100 / buildExtConfig.getLogReduceProgressRatio()));
int finalI = i;
JsonMessage<String> jsonMessage = OutGivingRun.fileUpload(file, startPath,
JsonMessage<String> jsonMessage = OutGivingRun.fileUpload(file, file.getName(), startPath,
projectId, false, last ? afterOpt : AfterOpt.No, nodeModel, false,
this.buildExtraModule.getProjectUploadCloseFirst(), (total, progressSize) -> {
double progressPercentage = Math.floor(((float) progressSize / total) * 100);
@ -604,7 +604,7 @@ public class ReleaseManage {
JsonMessage<String> jsonMessage = BuildUtil.loadDirPackage(this.buildExtraModule.getId(), this.getRealBuildNumberId(), this.resultFile, tarGz, (unZip, zipFile) -> {
String name = zipFile.getName();
Set<Integer> progressRangeList = ConcurrentHashMap.newKeySet((int) Math.floor((float) 100 / buildExtConfig.getLogReduceProgressRatio()));
return OutGivingRun.fileUpload(zipFile,
return OutGivingRun.fileUpload(zipFile, zipFile.getName(),
this.buildExtraModule.getProjectSecondaryDirectory(),
projectId,
unZip,

View File

@ -55,6 +55,7 @@ import org.dromara.jpom.service.node.ProjectInfoCacheService;
import org.dromara.jpom.service.outgiving.DbOutGivingLogService;
import org.dromara.jpom.service.outgiving.OutGivingServer;
import org.dromara.jpom.system.ServerConfig;
import org.dromara.jpom.util.FileUtils;
import org.dromara.jpom.util.StringUtil;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
@ -302,7 +303,7 @@ public class OutGivingProjectController extends BaseServerController {
File file = FileUtil.file(serverConfig.getUserTempPath(), ServerConst.OUTGIVING_FILE, id);
FileUtil.mkdir(file);
File downloadFile = HttpUtil.downloadFileFromUrl(url, file);
this.startTask(outGivingModel, downloadFile, autoUnzip, stripComponents, selectProject, true);
this.startTask(outGivingModel, downloadFile, null, autoUnzip, stripComponents, selectProject, true);
return JsonMessage.success(I18nMessageUtil.get("i18n.download_success_and_distribute.ae94"));
}
@ -392,7 +393,8 @@ public class OutGivingProjectController extends BaseServerController {
outGivingServer.updateById(outGivingModel);
File storageSavePath = serverConfig.fileStorageSavePath();
File file = FileUtil.file(storageSavePath, storageModel.getPath());
this.startTask(outGivingModel, file, autoUnzip, stripComponents, selectProject, false);
String fileName = FileUtils.safeFileName(storageModel.getName(), storageModel.getExtName(), "file-storage." + storageModel.getExtName());
this.startTask(outGivingModel, file, fileName, autoUnzip, stripComponents, selectProject, false);
return JsonMessage.success(I18nMessageUtil.get("i18n.start_distribution_exclamation.9fc2"));
}
@ -426,7 +428,8 @@ public class OutGivingProjectController extends BaseServerController {
outGivingServer.updateById(outGivingModel);
File file = FileUtil.file(storageModel.getAbsolutePath());
this.startTask(outGivingModel, file, autoUnzip, stripComponents, selectProject, false);
String fileName = FileUtils.safeFileName(storageModel.getName(), storageModel.getExtName(), "file-storage." + storageModel.getExtName());
this.startTask(outGivingModel, file, fileName, autoUnzip, stripComponents, selectProject, false);
return JsonMessage.success(I18nMessageUtil.get("i18n.start_distribution_exclamation.9fc2"));
}
@ -439,7 +442,7 @@ public class OutGivingProjectController extends BaseServerController {
* @param stripComponents 剔除目录
* @param selectProject 选择指定项目
*/
private void startTask(OutGivingModel outGivingModel, File file, String autoUnzip,
private void startTask(OutGivingModel outGivingModel, File file, String fileName, String autoUnzip,
String stripComponents,
String selectProject,
boolean deleteFile) {
@ -454,6 +457,7 @@ public class OutGivingProjectController extends BaseServerController {
.id(outGivingModel.getId())
.file(file)
.userModel(getUser())
.fileName(fileName)
.unzip(unzip)
.mode(outGivingModel.getMode())
.modeData(outGivingModel.getModeData())

View File

@ -86,17 +86,6 @@ public abstract class BaseDownloadApiController extends BaseJpomController {
return new long[]{fromPos, downloadSize};
}
protected String convertName(String name1, String extName, String defaultName) {
// 需要考虑文件名中存在非法字符
String name = ReUtil.replaceAll(name1, "[\\s\\\\/:\\*\\?\\\"<>\\|]", "");
if (StrUtil.isEmpty(name)) {
name = defaultName;
} else if (!StrUtil.endWith(name, StrUtil.DOT + extName)) {
name += StrUtil.DOT + extName;
}
return name;
}
public void download(File file, long fileSize, String name, long[] resolveRange, HttpServletResponse response) throws IOException {
Assert.state(FileUtil.isFile(file), I18nMessageUtil.get("i18n.file_does_not_exist_anymore.2fab"));
String contentType = ObjectUtil.defaultIfNull(FileUtil.getMimeType(name), "application/octet-stream");

View File

@ -27,6 +27,7 @@ import org.dromara.jpom.func.files.service.FileStorageService;
import org.dromara.jpom.model.user.UserModel;
import org.dromara.jpom.service.user.TriggerTokenLogServer;
import org.dromara.jpom.system.ServerConfig;
import org.dromara.jpom.util.FileUtils;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.GetMapping;
@ -131,7 +132,7 @@ public class FileStorageApiController extends BaseDownloadApiController {
File storageSavePath = serverConfig.fileStorageSavePath();
File fileStorageFile = FileUtil.file(storageSavePath, storageModel.getPath());
// 需要考虑文件名中存在非法字符
String name = this.convertName(storageModel.getName(), storageModel.getExtName(), fileStorageFile.getName());
String name = FileUtils.safeFileName(storageModel.getName(), storageModel.getExtName(), fileStorageFile.getName());
// 解析断点续传相关信息
long fileSize = FileUtil.size(fileStorageFile);
long[] resolveRange = this.resolveRange(request, fileSize, storageModel.getId(), storageModel.getName(), response);

View File

@ -19,6 +19,7 @@ import org.dromara.jpom.func.files.model.StaticFileStorageModel;
import org.dromara.jpom.func.files.service.StaticFileStorageService;
import org.dromara.jpom.model.user.UserModel;
import org.dromara.jpom.service.user.TriggerTokenLogServer;
import org.dromara.jpom.util.FileUtils;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.GetMapping;
@ -64,7 +65,7 @@ public class StaticFileStorageApiController extends BaseDownloadApiController {
Assert.notNull(userModel, I18nMessageUtil.get("i18n.token_invalid_or_expired.cb96"));
File file = FileUtil.file(storageModel.getAbsolutePath());
// 需要考虑文件名中存在非法字符
String name = this.convertName(storageModel.getName(), storageModel.getExtName(), file.getName());
String name = FileUtils.safeFileName(storageModel.getName(), storageModel.getExtName(), file.getName());
// 解析断点续传相关信息
long fileSize = FileUtil.size(file);
long[] resolveRange = this.resolveRange(request, fileSize, storageModel.getId(), storageModel.getName(), response);

View File

@ -14,6 +14,7 @@ import cn.hutool.core.date.SystemClock;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.keepbx.jpom.model.JsonMessage;
import com.alibaba.fastjson2.JSONObject;
@ -50,6 +51,7 @@ public class OutGivingItemRun implements Callable<OutGivingNodeProject.Status> {
private final String secondaryDirectory;
private final Boolean closeFirst;
private int stripComponents;
private String fileName;
public OutGivingItemRun(OutGivingModel item,
OutGivingNodeProject outGivingNodeProject,
@ -76,6 +78,7 @@ public class OutGivingItemRun implements Callable<OutGivingNodeProject.Status> {
OutGivingNodeProject.Status result;
long time = SystemClock.now();
String fileSize = FileUtil.readableFileSize(file);
this.fileName = StrUtil.emptyToDefault(this.fileName, this.file.getName());
try {
if (this.outGivingNodeProject.getDisabled() != null && this.outGivingNodeProject.getDisabled()) {
// 禁用
@ -84,7 +87,7 @@ public class OutGivingItemRun implements Callable<OutGivingNodeProject.Status> {
}
this.updateStatus(this.outGivingId, OutGivingNodeProject.Status.Ing, I18nMessageUtil.get("i18n.start_distribution.bce5"));
//
JsonMessage<String> jsonMessage = OutGivingRun.fileUpload(file, this.secondaryDirectory,
JsonMessage<String> jsonMessage = OutGivingRun.fileUpload(file, this.fileName, this.secondaryDirectory,
this.outGivingNodeProject.getProjectId(),
unzip,
afterOpt,

View File

@ -80,6 +80,7 @@ public class OutGivingRun {
private UserModel userModel;
private boolean unzip;
private int stripComponents;
private String fileName;
/**
* 分发方式
* upload: "手动上传",
@ -195,6 +196,7 @@ public class OutGivingRun {
final OutGivingNodeProject outGivingNodeProject = outGivingNodeProjects.get(nowIndex);
final OutGivingItemRun outGivingRun = new OutGivingItemRun(item, outGivingNodeProject, file, unzip, sleepTime);
outGivingRun.setStripComponents(stripComponents);
outGivingRun.setFileName(fileName);
OutGivingNodeProject.Status status = outGivingRun.call();
if (status != OutGivingNodeProject.Status.Ok) {
if (afterOpt == AfterOpt.Order_Must_Restart) {
@ -221,6 +223,7 @@ public class OutGivingRun {
for (final OutGivingNodeProject outGivingNodeProject : outGivingNodeProjects) {
final OutGivingItemRun outGivingItemRun = new OutGivingItemRun(item, outGivingNodeProject, file, unzip, null);
outGivingItemRun.setStripComponents(stripComponents);
outGivingItemRun.setFileName(fileName);
syncFinisher.addWorker(() -> {
try {
statusList.add(outGivingItemRun.call());
@ -378,14 +381,14 @@ public class OutGivingRun {
* @param closeFirst 保存项目文件前先关闭项目
* @return json
*/
public static JsonMessage<String> fileUpload(File file, String levelName, String projectId,
public static JsonMessage<String> fileUpload(File file, String fileName, String levelName, String projectId,
boolean unzip,
AfterOpt afterOpt,
NodeModel nodeModel,
boolean clearOld,
Boolean closeFirst,
BiConsumer<Long, Long> streamProgress) {
return fileUpload(file, levelName, projectId, unzip, afterOpt, nodeModel, clearOld, null, closeFirst, streamProgress);
return fileUpload(file, fileName, levelName, projectId, unzip, afterOpt, nodeModel, clearOld, null, closeFirst, streamProgress);
}
/**
@ -402,7 +405,7 @@ public class OutGivingRun {
* @param closeFirst 保存项目文件前先关闭项目
* @return json
*/
public static JsonMessage<String> fileUpload(File file, String levelName, String projectId,
public static JsonMessage<String> fileUpload(File file, String fileName, String levelName, String projectId,
boolean unzip,
AfterOpt afterOpt,
NodeModel nodeModel,
@ -410,7 +413,7 @@ public class OutGivingRun {
Integer sleepTime,
Boolean closeFirst,
BiConsumer<Long, Long> streamProgress) {
return fileUpload(file, levelName, projectId, unzip, afterOpt, nodeModel, clearOld, sleepTime, closeFirst, 0, streamProgress);
return fileUpload(file, fileName, levelName, projectId, unzip, afterOpt, nodeModel, clearOld, sleepTime, closeFirst, 0, streamProgress);
}
/**
@ -427,7 +430,10 @@ public class OutGivingRun {
* @param closeFirst 保存项目文件前先关闭项目
* @return json
*/
public static JsonMessage<String> fileUpload(File file, String levelName, String projectId,
public static JsonMessage<String> fileUpload(File file,
String fileName,
String levelName,
String projectId,
boolean unzip,
AfterOpt afterOpt,
NodeModel nodeModel,
@ -457,6 +463,7 @@ public class OutGivingRun {
data.put("closeFirst", closeFirst);
try {
return NodeForward.requestSharding(nodeModel, NodeUrl.Manage_File_Upload_Sharding, data, file,
fileName,
sliceData -> {
sliceData.putAll(data);
return NodeForward.request(nodeModel, NodeUrl.Manage_File_Sharding_Merge, sliceData);

View File

@ -1768,6 +1768,7 @@
"i18n_9ee9d48699": "Modification is not supported after creation",
"i18n_9f01272a10": " legal risk",
"i18n_9f0de3800b": "Please fill in the warehouse name.",
"i18n_9f4a0d67c6": "It is recommended not to have blank spaces, tables, other blanks, \\,/,:, *,?, _ # # _,<,>, \\, in the file name| Waiting for special characters",
"i18n_9f52492fbc": "For configuration details, please refer to the configuration example.",
"i18n_9f6090c819": "The incoming parameters are: buildId, buildName, type, statusMsg, triggerTime",
"i18n_9f6fa346d8": "Please enter an SSH name",

View File

@ -1768,6 +1768,7 @@
"i18n_9ee9d48699": "创建后不支持修改",
"i18n_9f01272a10": " 法律风险",
"i18n_9f0de3800b": "请填写仓库名称",
"i18n_9f4a0d67c6": "文件名建议不要出现空白、制表、其他空白、\\、/、:、*、、_##_、<、>、\\、| 等特殊字符",
"i18n_9f52492fbc": "配置详情请参考配置示例",
"i18n_9f6090c819": "传入参数有buildId、buildName、type、statusMsg、triggerTime",
"i18n_9f6fa346d8": "请输入 SSH 名称",

View File

@ -1768,6 +1768,7 @@
"i18n_9ee9d48699":"創建後不支持修改",
"i18n_9f01272a10":" 法律風險",
"i18n_9f0de3800b":"請填寫倉庫名稱",
"i18n_9f4a0d67c6":"文件名建議不要出現空白、製表、其他空白、\\、/、:、*、、_##_、<、>、\\、| 等特殊字符",
"i18n_9f52492fbc":"配置詳情請參考配置示例",
"i18n_9f6090c819":"傳入參數有buildId、buildName、type、statusMsg、triggerTime",
"i18n_9f6fa346d8":"請輸入 SSH 名稱",

View File

@ -1768,6 +1768,7 @@
"i18n_9ee9d48699":"建立後不支援修改",
"i18n_9f01272a10":" 法律風險",
"i18n_9f0de3800b":"請填寫倉庫名稱",
"i18n_9f4a0d67c6":"檔名建議不要出現空白、製表、其他空白、\\、/、:、*、、_##_、<、>、\\、| 等特殊字元",
"i18n_9f52492fbc":"配置詳情請參考配置示例",
"i18n_9f6090c819":"傳入引數有buildId、buildName、type、statusMsg、triggerTime",
"i18n_9f6fa346d8":"請輸入 SSH 名稱",

View File

@ -217,6 +217,8 @@
<a-form ref="editForm" :rules="rules" :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
<a-form-item :label="$t('i18n_29139c2a1a')" name="name">
<a-input v-model:value="temp.name" :placeholder="$t('i18n_29139c2a1a')" />
<!-- [\s\\/:\*\?\"<>\|] -->
<template #help>{{ $t('i18n_9f4a0d67c6') }}</template>
</a-form-item>
<a-form-item :label="$t('i18n_824607be6b')" name="keepDay">
<a-input-number