diff --git a/dinky-admin/src/main/java/org/dinky/service/resource/impl/ResourceServiceImpl.java b/dinky-admin/src/main/java/org/dinky/service/resource/impl/ResourceServiceImpl.java index 5f9b5db87f..1b77e4b2e5 100644 --- a/dinky-admin/src/main/java/org/dinky/service/resource/impl/ResourceServiceImpl.java +++ b/dinky-admin/src/main/java/org/dinky/service/resource/impl/ResourceServiceImpl.java @@ -266,10 +266,26 @@ public void upload( new LambdaQueryWrapper().eq(Resources::getPid, pid).eq(Resources::getFileName, fileName)); String fullName; if (currentUploadResource != null) { + // Same-name overwrite: update remote storage and invalidate local tmp/rs cache + long oldSize = Opt.ofNullable(currentUploadResource.getSize()).orElse(0L); if (desc != null) { currentUploadResource.setDescription(desc); } fullName = currentUploadResource.getFullName(); + currentUploadResource.setSize(size); + updateById(currentUploadResource); + uploadAction.accept(fullName); + URLUtils.clearRsLocalCache(fullName); + + long delta = size - oldSize; + if (delta != 0) { + List resourceByPidToParent = getResourceByPidToParent(new ArrayList<>(), pid); + resourceByPidToParent.forEach(x -> { + long parentSize = Opt.ofNullable(x.getSize()).orElse(0L); + x.setSize(Math.max(0L, parentSize + delta)); + }); + updateBatchById(resourceByPidToParent); + } } else { Resources resources = new Resources(); resources.setPid(pid); @@ -283,12 +299,12 @@ public void upload( resources.setSize(size); resources.setDescription(desc); saveOrUpdate(resources); - } - uploadAction.accept(fullName); + uploadAction.accept(fullName); - List resourceByPidToParent = getResourceByPidToParent(new ArrayList<>(), pid); - resourceByPidToParent.forEach(x -> x.setSize(x.getSize() + size)); - updateBatchById(resourceByPidToParent); + List resourceByPidToParent = getResourceByPidToParent(new ArrayList<>(), pid); + resourceByPidToParent.forEach(x -> x.setSize(x.getSize() + size)); + updateBatchById(resourceByPidToParent); + } } @Transactional(rollbackFor = Exception.class) @@ -338,6 +354,8 @@ public boolean remove(Integer id) { systemConfiguration.getResourcesModel().getValue().name(), byId.getFullName()); } + // Always clear local rs cache so deleted/overwritten resources are not reused + URLUtils.clearRsLocalCache(byId.getFullName()); if (isExistsChildren(id)) { if (byId.getIsDirectory()) { List resourceByPidToChildren = diff --git a/dinky-common/src/main/java/org/dinky/utils/URLUtils.java b/dinky-common/src/main/java/org/dinky/utils/URLUtils.java index fa92e81844..3f76bea1c9 100644 --- a/dinky-common/src/main/java/org/dinky/utils/URLUtils.java +++ b/dinky-common/src/main/java/org/dinky/utils/URLUtils.java @@ -51,10 +51,25 @@ public class URLUtils { public static File toFile(String urlPath) { try { URL url = new URL(urlPath); - URLConnection urlConnection = url.openConnection(); if ("rs".equals(url.getProtocol())) { - String path = StrUtil.join(File.separator, TMP_PATH, "rs", url.getPath()); - return FileUtil.writeFromStream(urlConnection.getInputStream(), path); + URLConnection urlConnection = url.openConnection(); + // Avoid JVM URL / JarURLConnection caches returning stale jar content after overwrite + urlConnection.setUseCaches(false); + File target = getRsLocalFile(url.getPath()); + // Delete existing file first so ClassLoader / JarFile does not keep the old inode + if (FileUtil.exist(target)) { + FileUtil.del(target); + } + FileUtil.mkParentDirs(target); + File tmpFile = FileUtil.file(target.getAbsolutePath() + ".downloading"); + try { + FileUtil.writeFromStream(urlConnection.getInputStream(), tmpFile); + return FileUtil.move(tmpFile, target, true); + } finally { + if (FileUtil.exist(tmpFile)) { + FileUtil.del(tmpFile); + } + } } else if ("file".equals(url.getProtocol())) { return new File(url.getPath()); } @@ -65,6 +80,29 @@ public static File toFile(String urlPath) { } } + /** + * Clear the local cache file (or directory) under tmp/rs for a resource fullName. + * + *

Used after resource overwrite / delete so subsequent rs:// loads do not reuse stale files. + * + * @param fullName resource full name stored in DB, e.g. {@code app.jar} or {@code dir/app.jar} + */ + public static void clearRsLocalCache(String fullName) { + if (StrUtil.isBlank(fullName)) { + return; + } + String relative = StrUtil.removePrefix(fullName, StrUtil.SLASH); + File localFile = FileUtil.file(TMP_PATH, "rs", relative); + if (FileUtil.exist(localFile)) { + FileUtil.del(localFile); + } + } + + private static File getRsLocalFile(String urlPath) { + String relative = StrUtil.removePrefix(urlPath, StrUtil.SLASH); + return FileUtil.file(TMP_PATH, "rs", relative); + } + /** * 获得URL,常用于使用绝对路径时的情况 *