Fix: 修复 Docker 上传目录解析错误
将 multipart 上传目录改为基于 DATA_DIR 的绝对路径,避免 Tomcat 在容器内把相对路径解析到临时目录。同步让上传控制器复用该配置并补充错误日志,确保本地文件在异步上传期间可用。
This commit is contained in:
@@ -9,6 +9,7 @@ import com.sshmanager.service.ConnectionService;
|
|||||||
import com.sshmanager.service.SftpService;
|
import com.sshmanager.service.SftpService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
@@ -43,6 +44,7 @@ public class SftpController {
|
|||||||
private final ConnectionService connectionService;
|
private final ConnectionService connectionService;
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final SftpService sftpService;
|
private final SftpService sftpService;
|
||||||
|
private final String uploadTempLocation;
|
||||||
|
|
||||||
private final Map<String, SftpService.SftpSession> sessions = new ConcurrentHashMap<>();
|
private final Map<String, SftpService.SftpSession> sessions = new ConcurrentHashMap<>();
|
||||||
private final Map<String, Object> sessionLocks = new ConcurrentHashMap<>();
|
private final Map<String, Object> sessionLocks = new ConcurrentHashMap<>();
|
||||||
@@ -53,10 +55,12 @@ public class SftpController {
|
|||||||
|
|
||||||
public SftpController(ConnectionService connectionService,
|
public SftpController(ConnectionService connectionService,
|
||||||
UserRepository userRepository,
|
UserRepository userRepository,
|
||||||
SftpService sftpService) {
|
SftpService sftpService,
|
||||||
|
@Value("${spring.servlet.multipart.location:/app/data/upload-temp}") String uploadTempLocation) {
|
||||||
this.connectionService = connectionService;
|
this.connectionService = connectionService;
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
this.sftpService = sftpService;
|
this.sftpService = sftpService;
|
||||||
|
this.uploadTempLocation = uploadTempLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Long getCurrentUserId(Authentication auth) {
|
private Long getCurrentUserId(Authentication auth) {
|
||||||
@@ -297,9 +301,9 @@ public class SftpController {
|
|||||||
String taskKey = uploadTaskKey(userId, taskId);
|
String taskKey = uploadTaskKey(userId, taskId);
|
||||||
|
|
||||||
// Save file to persistent location before async processing
|
// Save file to persistent location before async processing
|
||||||
java.io.File uploadTempDir = new java.io.File("./data/upload-temp");
|
java.io.File uploadTempDir = new java.io.File(uploadTempLocation);
|
||||||
if (!uploadTempDir.exists()) {
|
if (!uploadTempDir.exists() && !uploadTempDir.mkdirs()) {
|
||||||
uploadTempDir.mkdirs();
|
throw new IOException("Failed to create upload temp directory: " + uploadTempDir.getAbsolutePath());
|
||||||
}
|
}
|
||||||
tempFile = new java.io.File(uploadTempDir, taskId + "_" + file.getOriginalFilename());
|
tempFile = new java.io.File(uploadTempDir, taskId + "_" + file.getOriginalFilename());
|
||||||
file.transferTo(tempFile);
|
file.transferTo(tempFile);
|
||||||
@@ -352,6 +356,8 @@ public class SftpController {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
log.error("Async upload failed, taskId={}, connectionId={}, tempFile={}",
|
||||||
|
taskId, connectionId, savedFile.getAbsolutePath(), e);
|
||||||
status.markError(e.getMessage() != null ? e.getMessage() : "Upload failed");
|
status.markError(e.getMessage() != null ? e.getMessage() : "Upload failed");
|
||||||
// Clean up temp file on error
|
// Clean up temp file on error
|
||||||
if (savedFile.exists()) {
|
if (savedFile.exists()) {
|
||||||
@@ -366,6 +372,7 @@ public class SftpController {
|
|||||||
result.put("message", "Upload started");
|
result.put("message", "Upload started");
|
||||||
return ResponseEntity.ok(result);
|
return ResponseEntity.ok(result);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to prepare upload temp file, connectionId={}", connectionId, e);
|
||||||
// Clean up temp file if initial save failed
|
// Clean up temp file if initial save failed
|
||||||
if (tempFile != null && tempFile.exists()) {
|
if (tempFile != null && tempFile.exists()) {
|
||||||
tempFile.delete();
|
tempFile.delete();
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ spring:
|
|||||||
multipart:
|
multipart:
|
||||||
max-file-size: 2048MB
|
max-file-size: 2048MB
|
||||||
max-request-size: 2048MB
|
max-request-size: 2048MB
|
||||||
location: ./data/upload-temp # 持久化临时目录,避免 Docker 容器重启丢失
|
location: ${DATA_DIR:/app/data}/upload-temp # 使用容器数据目录,避免被解析为 Tomcat 工作目录
|
||||||
file-size-threshold: 0 # 立即写入磁盘,不使用内存缓冲
|
file-size-threshold: 0 # 立即写入磁盘,不使用内存缓冲
|
||||||
datasource:
|
datasource:
|
||||||
url: jdbc:h2:file:./data/sshmanager;DB_CLOSE_DELAY=-1
|
url: jdbc:h2:file:./data/sshmanager;DB_CLOSE_DELAY=-1
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ WORKDIR /app
|
|||||||
|
|
||||||
COPY --from=backend /build/target/*.jar app.jar
|
COPY --from=backend /build/target/*.jar app.jar
|
||||||
|
|
||||||
ENV DATA_DIR=/app/data
|
ENV DATA_DIR=/app/data
|
||||||
RUN mkdir -p ${DATA_DIR}
|
RUN mkdir -p ${DATA_DIR}/upload-temp
|
||||||
|
|
||||||
EXPOSE 48080
|
EXPOSE 48080
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user